libvisiontransfer  10.0.0
imageprotocol.cpp
1 /*******************************************************************************
2  * Copyright (c) 2022 Nerian Vision GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
15 #include <cstring>
16 #include <iostream>
17 #include <limits>
18 #include <vector>
19 #include <memory>
20 #include <algorithm>
21 #include "visiontransfer/imageprotocol.h"
22 #include "visiontransfer/alignedallocator.h"
23 #include "visiontransfer/datablockprotocol.h"
24 #include "visiontransfer/exceptions.h"
25 #include "visiontransfer/bitconversions.h"
26 #include "visiontransfer/internalinformation.h"
27 
28 // Network headers
29 #ifdef _WIN32
30  #ifndef NOMINMAX
31  #define NOMINMAX
32  #endif
33  #include <winsock2.h>
34 #else
35  #include <arpa/inet.h>
36 #endif
37 
38 #define LOG_WARN(expr)
39 //#define LOG_WARN(expr) std::cerr << "DataBlockProtocol: " << expr << std::endl
40 
41 using namespace std;
42 using namespace visiontransfer;
43 using namespace visiontransfer::internal;
44 
45 namespace visiontransfer {
46 
47 /*************** Pimpl class containing all private members ***********/
48 
49 class ImageProtocol::Pimpl {
50 public:
51 
52  static const int IMAGE_HEADER_OFFSET = sizeof(DataBlockProtocol::HeaderPreamble) + 10;
53 
54  Pimpl(bool server, ProtocolType protType, int maxUdpPacketSize);
55 
56  // Redeclaration of public members
57  void setTransferImageSet(const ImageSet& imageSet);
58  void setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
59  int firstTileWidth = 0, int middleTilesWidth = 0, int lastTileWidth = 0);
60  void setRawValidBytes(const std::vector<int>& validBytesVec);
61  const unsigned char* getTransferMessage(int& length);
62  bool transferComplete();
63  void resetTransfer();
64  bool getReceivedImageSet(ImageSet& imageSet);
65  bool getPartiallyReceivedImageSet(ImageSet& imageSet,
66  int& validRows, bool& complete);
67  bool imagesReceived() const;
68 
69  unsigned char* getNextReceiveBuffer(int& maxLength);
70 
71  void processReceivedMessage(int length);
72  int getProspectiveMessageSize();
73  int getNumDroppedFrames() const;
74  void resetReception();
75  bool isConnected() const;
76  const unsigned char* getNextControlMessage(int& length);
77  bool newClientConnected();
78 
79  std::string statusReport();
80 
81 private:
82  unsigned short MAGIC_SEQUECE = 0x3D15;
83 
84  // Header data transferred in the first packet
85 #pragma pack(push,1)
86  struct HeaderDataLegacy {
87  unsigned short magic;
88 
89  unsigned char protocolVersion;
90  unsigned char isRawImagePair_OBSOLETE;
91 
92  unsigned short width;
93  unsigned short height;
94 
95  unsigned short firstTileWidth;
96  unsigned short lastTileWidth;
97 
98  unsigned char format0;
99  unsigned char format1;
100  unsigned short minDisparity;
101  unsigned short maxDisparity;
102  unsigned char subpixelFactor;
103 
104  unsigned int seqNum;
105  int timeSec;
106  int timeMicrosec;
107 
108  float q[16];
109 
110  unsigned short middleTilesWidth;
111  };
112  // Header data v2: extensible and forwards-compatible
113  struct HeaderDataV2: public HeaderDataLegacy {
114  unsigned short totalHeaderSize;
115  unsigned short flags;
116  unsigned char numberOfImages;
117  unsigned char format2;
118  enum FlagBits {
119  NEW_STYLE_TRANSFER = 1,
120  HEADER_V3 = 2,
121  HEADER_V4 = 4,
122  HEADER_V5 = 8,
123  // future protocol extensions should mark a new bit here
124  };
125  };
126  // Header data v3, adds arbitrary image channel assignments
127  struct HeaderDataV3: public HeaderDataV2 {
128  // HEADER_V3 bit implies that this extension is present,
129  // declaring arbitrary channel roles for each of numberOfImages active channels.
130  // If not present, is it an old sender that always sends two images
131  // (channel 0: left, channel 1: right or disparity (if active))
132  unsigned char imageTypes[8];
133  };
134  // Header data v4, adds exposure time and sync pulse
135  struct HeaderDataV4: public HeaderDataV3 {
136  int exposureTime; // exposure time in microseconds
137  int lastSyncPulseSec;
138  int lastSyncPulseMicrosec;
139  };
140  // Header data v5, adds format for 4th image
141  struct HeaderData: public HeaderDataV4{
142  unsigned char format3;
143  };
144 #pragma pack(pop)
145 
146  // Underlying protocol for data transfers
147  DataBlockProtocol dataProt;
148  ProtocolType protType;
149 
150  // Transfer related variables
151  std::vector<unsigned char> headerBuffer;
152 
153  // Reception related variables
154  std::vector<unsigned char, AlignedAllocator<unsigned char> >decodeBuffer[ImageSet::MAX_SUPPORTED_IMAGES];
155  bool receiveHeaderParsed;
156  HeaderData receiveHeader;
157  int lastReceivedPayloadBytes[ImageSet::MAX_SUPPORTED_IMAGES];
158  bool receptionDone;
159 
160  // Copies the transmission header to the given buffer
161  void copyHeaderToBuffer(const ImageSet& imageSet, int firstTileWidth,
162  int middleTilesWidth, int lastTileWidth, unsigned char* buffer);
163 
164  // Decodes header information from the received data
165  void tryDecodeHeader(const unsigned char* receivedData, int receivedBytes);
166 
167  // Decodes a received image from a non-interleaved buffer
168  unsigned char* decodeNoninterleaved(int imageNumber, int numImages, int receivedBytes,
169  unsigned char* data, int& validRows, int& rowStride);
170 
171  // Decodes a received image from an interleaved buffer
172  unsigned char* decodeInterleaved(int imageNumber, int numImages, int receivedBytes,
173  unsigned char* data, int& validRows, int& rowStride);
174 
175  int getNumTiles(int width, int firstTileWidth, int middleTilesWidth, int lastTileWidth);
176 
177  int getFrameSize(int width, int height, int firstTileWidth, int middleTilesWidth,
178  int lastTileWidth, int totalBits);
179 
180  int getFormatBits(ImageSet::ImageFormat format, bool afterDecode);
181 
182  void decodeTiledImage(int imageNumber, int lastReceivedPayloadBytes, int receivedPayloadBytes,
183  const unsigned char* data, int firstTileStride, int middleTilesStride, int lastTileStride,
184  int& validRows, ImageSet::ImageFormat format, bool dataIsInterleaved);
185 
186  void decodeRowsFromTile(int startRow, int stopRow, unsigned const char* src,
187  unsigned char* dst, int srcStride, int dstStride, int tileWidth);
188 
189  void allocateDecodeBuffer(int imageNumber);
190 };
191 
192 
193 /******************** Stubs for all public members ********************/
194 
195 ImageProtocol::ImageProtocol(bool server, ProtocolType protType, int maxUdpPacketSize)
196  : pimpl(new Pimpl(server, protType, maxUdpPacketSize)) {
197  // All initializations are done by the Pimpl class
198 }
199 
200 ImageProtocol::~ImageProtocol() {
201  delete pimpl;
202 }
203 
205  pimpl->setTransferImageSet(imageSet);
206 }
207 
208 void ImageProtocol::setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& imageData,
209  int firstTileWidth, int middleTilesWidth, int lastTileWidth) {
210  pimpl->setRawTransferData(metaData, imageData, firstTileWidth, middleTilesWidth, lastTileWidth);
211 }
212 
213 void ImageProtocol::setRawValidBytes(const std::vector<int>& validBytesVec) {
214  pimpl->setRawValidBytes(validBytesVec);
215 }
216 
217 const unsigned char* ImageProtocol::getTransferMessage(int& length) {
218  return pimpl->getTransferMessage(length);
219 }
220 
222  return pimpl->transferComplete();
223 }
224 
226  pimpl->resetTransfer();
227 }
228 
230  return pimpl->getReceivedImageSet(imageSet);
231 }
232 
234  ImageSet& imageSet, int& validRows, bool& complete) {
235  return pimpl->getPartiallyReceivedImageSet(imageSet, validRows, complete);
236 }
237 
239  return pimpl->imagesReceived();
240 }
241 
242 unsigned char* ImageProtocol::getNextReceiveBuffer(int& maxLength) {
243  return pimpl->getNextReceiveBuffer(maxLength);
244 }
245 
247  pimpl->processReceivedMessage(length);
248 }
249 
251  return pimpl->getNumDroppedFrames();
252 }
253 
255  pimpl->resetReception();
256 }
257 
259  return pimpl->isConnected();
260 }
261 
262 const unsigned char* ImageProtocol::getNextControlMessage(int& length) {
263  return pimpl->getNextControlMessage(length);
264 }
265 
267  return pimpl->newClientConnected();
268 }
269 
270 /******************** Implementation in pimpl class *******************/
271 
272 ImageProtocol::Pimpl::Pimpl(bool server, ProtocolType protType, int maxUdpPacketSize)
273  :dataProt(server, (DataBlockProtocol::ProtocolType)protType,
274  maxUdpPacketSize), protType(protType),
275  receiveHeaderParsed(false), lastReceivedPayloadBytes{0},
276  receptionDone(false) {
277  headerBuffer.resize(sizeof(HeaderData) + 128);
278  memset(&headerBuffer[0], 0, sizeof(headerBuffer.size()));
279  memset(&receiveHeader, 0, sizeof(receiveHeader));
280 }
281 
282 void ImageProtocol::Pimpl::setTransferImageSet(const ImageSet& imageSet) {
283  for (int i=0; i<imageSet.getNumberOfImages(); ++i) {
284  if(imageSet.getPixelData(i) == nullptr) {
285  throw ProtocolException("Image data is null pointer!");
286  }
287  }
288 
289  // Set header as first piece of data
290  copyHeaderToBuffer(imageSet, 0, 0, 0, &headerBuffer[IMAGE_HEADER_OFFSET]);
291  dataProt.resetTransfer();
292  int numTransferBlocks = imageSet.getNumberOfImages();
293  dataProt.setTransferHeader(&headerBuffer[IMAGE_HEADER_OFFSET], sizeof(HeaderData), numTransferBlocks);
294  for (int i=0; i<imageSet.getNumberOfImages(); ++i) {
295  int bits = getFormatBits(imageSet.getPixelFormat(i), false);
296  int rawDataLength = getFrameSize(imageSet.getWidth(), imageSet.getHeight(), 0, 0, 0, bits);
297  dataProt.setTransferBytes(i, rawDataLength);
298  }
299 
300  // Perform 12 bit packed encoding if necessary
301  int bits[ImageSet::MAX_SUPPORTED_IMAGES] = {0};
302  int rowSize[ImageSet::MAX_SUPPORTED_IMAGES] = {0};
303  const unsigned char* pixelData[ImageSet::MAX_SUPPORTED_IMAGES] = {nullptr};
304  std::vector<unsigned char> encodingBuffer[ImageSet::MAX_SUPPORTED_IMAGES];
305 
306  for(int i = 0; i<imageSet.getNumberOfImages(); i++) {
307  bits[i] = getFormatBits(imageSet.getPixelFormat(i), false);
308  rowSize[i] = imageSet.getWidth()*bits[i]/8;
309 
310  if(imageSet.getPixelFormat(i) != ImageSet::FORMAT_12_BIT_MONO) {
311  pixelData[i] = imageSet.getPixelData(i);
312  } else {
313  encodingBuffer[i].resize(rowSize[i] * imageSet.getHeight());
314  BitConversions::encode12BitPacked(0, imageSet.getHeight(), imageSet.getPixelData(i),
315  &encodingBuffer[i][0], imageSet.getRowStride(i), rowSize[i], imageSet.getWidth());
316  pixelData[i] = &encodingBuffer[i][0];
317  }
318  }
319 
320  for (int i=0; i<imageSet.getNumberOfImages(); ++i) {
321  dataProt.setTransferData(i, const_cast<unsigned char*>(pixelData[i])); // these are always reserved memory or untile buffers
322  }
323 }
324 
325 void ImageProtocol::Pimpl::setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
326  int firstTileWidth, int middleTilesWidth, int lastTileWidth) {
327  if(static_cast<int>(rawData.size()) != metaData.getNumberOfImages()) {
328  throw ProtocolException("Mismatch between metadata and number of image buffers!");
329  }
330 
331  // Set header as first piece of data
332  copyHeaderToBuffer(metaData, firstTileWidth, middleTilesWidth, lastTileWidth, &headerBuffer[IMAGE_HEADER_OFFSET]);
333  dataProt.resetTransfer();
334  int numTransferBlocks = metaData.getNumberOfImages();
335  dataProt.setTransferHeader(&headerBuffer[IMAGE_HEADER_OFFSET], sizeof(HeaderData), numTransferBlocks);
336  // Now set the size per channel (replaces old final size argument to setTransferHeader()
337  for (int i=0; i<metaData.getNumberOfImages(); ++i) {
338  int rawDataLength = getFrameSize(metaData.getWidth(), metaData.getHeight(),
339  firstTileWidth, middleTilesWidth, lastTileWidth, metaData.getBitsPerPixel(i));
340  dataProt.setTransferBytes(i, rawDataLength);
341  }
342 
343  for (int i=0; i<metaData.getNumberOfImages(); ++i) {
344  dataProt.setTransferData(i, rawData[i]);
345  }
346 }
347 
348 void ImageProtocol::Pimpl::setRawValidBytes(const std::vector<int>& validBytesVec) {
349  for (int i=0; i<static_cast<int>(validBytesVec.size()); ++i) {
350  dataProt.setTransferValidBytes(i, validBytesVec[i]);
351  }
352 }
353 
354 const unsigned char* ImageProtocol::Pimpl::getTransferMessage(int& length) {
355  const unsigned char* msg = dataProt.getTransferMessage(length);
356 
357  if(msg == nullptr) {
358  msg = dataProt.getTransferMessage(length);
359  }
360 
361  return msg;
362 }
363 
364 bool ImageProtocol::Pimpl::transferComplete() {
365  return dataProt.transferComplete();
366 }
367 
368 int ImageProtocol::Pimpl::getNumTiles(int width, int firstTileWidth, int middleTilesWidth, int lastTileWidth) {
369  if(lastTileWidth == 0) {
370  return 1;
371  } else if(middleTilesWidth == 0) {
372  return 2;
373  } else {
374  int tileWidth = firstTileWidth + lastTileWidth - middleTilesWidth;
375  return (width - 2*tileWidth + firstTileWidth + lastTileWidth) / (firstTileWidth + lastTileWidth - tileWidth);
376  }
377 }
378 
379 int ImageProtocol::Pimpl::getFrameSize(int width, int height, int firstTileWidth,
380  int middleTilesWidth, int lastTileWidth, int totalBits) {
381  return (width * height * totalBits) /8;
382 }
383 
384 int ImageProtocol::Pimpl::getFormatBits(ImageSet::ImageFormat format, bool afterDecode) {
385  if(afterDecode) {
386  return ImageSet::getBytesPerPixel(format)*8;
387  } else {
388  switch(format) {
389  case ImageSet::FORMAT_8_BIT_MONO: return 8;
390  case ImageSet::FORMAT_12_BIT_MONO: return 12;
391  case ImageSet::FORMAT_8_BIT_RGB: return 24;
392  default: throw ProtocolException("Illegal pixel format!");
393  }
394  }
395 }
396 
397 void ImageProtocol::Pimpl::copyHeaderToBuffer(const ImageSet& imageSet,
398  int firstTileWidth, int middleTilesWidth, int lastTileWidth, unsigned char* buffer) {
399  int timeSec = 0, timeMicrosec = 0;
400  HeaderData* transferHeader = reinterpret_cast<HeaderData*>(buffer);
401 
402  memset(transferHeader, 0, sizeof(*transferHeader));
403  transferHeader->magic = htons(MAGIC_SEQUECE);
404  transferHeader->protocolVersion = InternalInformation::CURRENT_PROTOCOL_VERSION;
405  transferHeader->isRawImagePair_OBSOLETE = 0;
406  transferHeader->width = htons(imageSet.getWidth());
407  transferHeader->height = htons(imageSet.getHeight());
408  transferHeader->firstTileWidth = htons(firstTileWidth);
409  transferHeader->lastTileWidth = htons(lastTileWidth);
410  transferHeader->middleTilesWidth = htons(middleTilesWidth);
411  transferHeader->format0 = static_cast<unsigned char>(imageSet.getPixelFormat(0));
412  transferHeader->format1 = (imageSet.getNumberOfImages() <= 1) ? 0 : static_cast<unsigned char>(imageSet.getPixelFormat(1));
413  transferHeader->seqNum = static_cast<unsigned int>(htonl(imageSet.getSequenceNumber()));
414  transferHeader->format2 = (imageSet.getNumberOfImages() <= 2) ? 0 : static_cast<unsigned char>(imageSet.getPixelFormat(2));
415  transferHeader->format3 = (imageSet.getNumberOfImages() <= 3) ? 0 : static_cast<unsigned char>(imageSet.getPixelFormat(3));
416  transferHeader->numberOfImages = static_cast<unsigned char>(imageSet.getNumberOfImages());
417  transferHeader->exposureTime = htonl(imageSet.getExposureTime());
418 
419  imageSet.getLastSyncPulse(timeSec, timeMicrosec);
420  transferHeader->lastSyncPulseSec = htonl(timeSec);
421  transferHeader->lastSyncPulseMicrosec = htonl(timeMicrosec);
422 
423  transferHeader->totalHeaderSize = htons(sizeof(HeaderData));
424  transferHeader->flags = htons(HeaderData::FlagBits::NEW_STYLE_TRANSFER | HeaderData::FlagBits::HEADER_V3
425  | HeaderData::FlagBits::HEADER_V4 | HeaderData::FlagBits::HEADER_V5);
426 
427  int minDisp = 0, maxDisp = 0;
428  imageSet.getDisparityRange(minDisp, maxDisp);
429  transferHeader->minDisparity = minDisp;
430  transferHeader->maxDisparity = maxDisp;
431 
432  transferHeader->subpixelFactor = imageSet.getSubpixelFactor();
433 
434  imageSet.getTimestamp(timeSec, timeMicrosec);
435  transferHeader->timeSec = static_cast<int>(htonl(static_cast<unsigned int>(timeSec)));
436  transferHeader->timeMicrosec = static_cast<int>(htonl(static_cast<unsigned int>(timeMicrosec)));
437 
438  int numImageChannels = 0;
439  for (int i=0; i<(int) sizeof(transferHeader->imageTypes); ++i) {
440  transferHeader->imageTypes[i] = static_cast<unsigned char>(ImageSet::ImageType::IMAGE_UNDEFINED);
441  }
442  int idx = imageSet.getIndexOf(ImageSet::ImageType::IMAGE_LEFT);
443  if (idx>=0) {
444  transferHeader->imageTypes[idx] = static_cast<unsigned char>(ImageSet::ImageType::IMAGE_LEFT);
445  numImageChannels++;
446  }
447  idx = imageSet.getIndexOf(ImageSet::ImageType::IMAGE_RIGHT);
448  if (idx>=0) {
449  transferHeader->imageTypes[idx] = static_cast<unsigned char>(ImageSet::ImageType::IMAGE_RIGHT);
450  numImageChannels++;
451  }
452  idx = imageSet.getIndexOf(ImageSet::ImageType::IMAGE_DISPARITY);
453  if (idx>=0) {
454  transferHeader->imageTypes[idx] = static_cast<unsigned char>(ImageSet::ImageType::IMAGE_DISPARITY);
455  numImageChannels++;
456  }
457  idx = imageSet.getIndexOf(ImageSet::ImageType::IMAGE_COLOR);
458  if (idx>=0) {
459  transferHeader->imageTypes[idx] = static_cast<unsigned char>(ImageSet::ImageType::IMAGE_COLOR);
460  numImageChannels++;
461  }
462  if (numImageChannels != imageSet.getNumberOfImages()) {
463  throw std::runtime_error("Mismatch between reported number of images and enabled channel selection!");
464  }
465 
466 
467  if(imageSet.getQMatrix() != nullptr) {
468  memcpy(transferHeader->q, imageSet.getQMatrix(), sizeof(float)*16);
469  }
470 }
471 
472 void ImageProtocol::Pimpl::resetTransfer() {
473  dataProt.resetTransfer();
474 }
475 
476 unsigned char* ImageProtocol::Pimpl::getNextReceiveBuffer(int& maxLength) {
477  maxLength = dataProt.getMaxReceptionSize();
478  return dataProt.getNextReceiveBuffer(maxLength);
479 }
480 
481 void ImageProtocol::Pimpl::processReceivedMessage(int length) {
482  receptionDone = false;
483 
484  // Add the received message
485  dataProt.processReceivedMessage(length, receptionDone);
486  if(!dataProt.wasHeaderReceived() && receiveHeaderParsed) {
487  // Something went wrong. We need to reset!
488  LOG_WARN("Resetting image protocol!");
489  resetReception();
490  return;
491  }
492 
493  int receivedBytes = 0;
494  dataProt.getReceivedData(receivedBytes);
495 
496  // Immediately try to decode the header
497  if(!receiveHeaderParsed) {
498  int headerLen = 0;
499  unsigned char* headerData = dataProt.getReceivedHeader(headerLen);
500  if(headerData != nullptr) {
501  tryDecodeHeader(headerData, headerLen);
502  }
503  }
504 }
505 
506 void ImageProtocol::Pimpl::tryDecodeHeader(const
507 unsigned char* receivedData, int receivedBytes) {
508  // Extra data fields that have been added to the header. Must be
509  // removed when the protocol version number is updated
510  constexpr int optionalDataSize = sizeof(receiveHeader.middleTilesWidth);
511  constexpr int mandatoryDataSize = static_cast<int>(sizeof(HeaderDataLegacy)) - optionalDataSize;
512  constexpr int fullyExtensibleHeaderSize = static_cast<int>(sizeof(HeaderDataV2));
513  bool isCompleteHeader = false;
514 
515  if(receivedBytes >= mandatoryDataSize) {
516  if (receivedBytes < fullyExtensibleHeaderSize) {
517  *(static_cast<HeaderDataLegacy*>(&receiveHeader)) = *reinterpret_cast<const HeaderDataLegacy*>(receivedData);
518  } else {
519  memcpy(&receiveHeader, receivedData, std::min((size_t)receivedBytes, sizeof(HeaderData)));
520  receiveHeader = *reinterpret_cast<const HeaderData*>(receivedData);
521  isCompleteHeader = true;
522  }
523  if(receiveHeader.magic != htons(MAGIC_SEQUECE)) {
524  // Let's not call this an error. Perhaps it's just not a header
525  // packet
526  return;
527  }
528 
529  if(receiveHeader.protocolVersion != InternalInformation::CURRENT_PROTOCOL_VERSION) {
530  throw ProtocolException("Protocol version mismatch!");
531  }
532 
533  // Convert byte order
534  receiveHeader.width = ntohs(receiveHeader.width);
535  receiveHeader.height = ntohs(receiveHeader.height);
536  receiveHeader.firstTileWidth = ntohs(receiveHeader.firstTileWidth);
537  receiveHeader.lastTileWidth = ntohs(receiveHeader.lastTileWidth);
538 
539  receiveHeader.timeSec = static_cast<int>(
540  ntohl(static_cast<unsigned int>(receiveHeader.timeSec)));
541  receiveHeader.timeMicrosec = static_cast<int>(
542  ntohl(static_cast<unsigned int>(receiveHeader.timeMicrosec)));
543  receiveHeader.seqNum = ntohl(receiveHeader.seqNum);
544 
545  // Optional data items
546  if(receivedBytes >= mandatoryDataSize + optionalDataSize) {
547  receiveHeader.middleTilesWidth = ntohs(receiveHeader.middleTilesWidth);
548  } else {
549  receiveHeader.middleTilesWidth = 0;
550  }
551  if (isCompleteHeader) {
552  // This is a header of v2 or above, which self-reports its extension level in the flags field
553  receiveHeader.totalHeaderSize = ntohs(receiveHeader.totalHeaderSize);
554  receiveHeader.flags = ntohs(receiveHeader.flags);
555  receiveHeader.exposureTime = ntohl(receiveHeader.exposureTime);
556  receiveHeader.lastSyncPulseSec = htonl(receiveHeader.lastSyncPulseSec);
557  receiveHeader.lastSyncPulseMicrosec = htonl(receiveHeader.lastSyncPulseMicrosec);
558  } else {
559  // Infer missing fields for legacy compatibility transfers
560  receiveHeader.totalHeaderSize = (receivedBytes <= mandatoryDataSize) ? mandatoryDataSize : static_cast<int>(sizeof(HeaderDataLegacy));
561  receiveHeader.flags = 0;
562  receiveHeader.numberOfImages = 2;
563  receiveHeader.format2 = 0;
564  receiveHeader.format3 = 0;
565  receiveHeader.exposureTime = 0;
566  receiveHeader.lastSyncPulseSec = 0;
567  receiveHeader.lastSyncPulseMicrosec = 0;
568  }
569 
570  receiveHeaderParsed = true;
571  }
572 }
573 
574 bool ImageProtocol::Pimpl::imagesReceived() const {
575  return receptionDone && receiveHeaderParsed;
576 }
577 
578 bool ImageProtocol::Pimpl::getReceivedImageSet(ImageSet& imageSet) {
579  bool complete = false;
580  int validRows;
581  bool ok = getPartiallyReceivedImageSet(imageSet, validRows, complete);
582 
583  return (ok && complete);
584 }
585 
586 bool ImageProtocol::Pimpl::getPartiallyReceivedImageSet(ImageSet& imageSet, int& validRows, bool& complete) {
587  imageSet.setWidth(0);
588  imageSet.setHeight(0);
589 
590  complete = false;
591 
592  if(!receiveHeaderParsed) {
593  // We haven't even received the image header yet
594  return false;
595  } else {
596  // We received at least some pixel data
597  imageSet.setNumberOfImages(receiveHeader.numberOfImages);
598  bool flaggedDisparityPair = (receiveHeader.isRawImagePair_OBSOLETE == 0); // only meaningful in headers <=V2
599  bool isInterleaved = (receiveHeader.flags & HeaderData::FlagBits::NEW_STYLE_TRANSFER) == 0;
600  bool arbitraryChannels = (receiveHeader.flags & HeaderData::FlagBits::HEADER_V3) > 0;
601  bool hasExposureTime = (receiveHeader.flags & HeaderData::FlagBits::HEADER_V4) > 0;
602 
603  // Forward compatibility check: mask out all known flag bits and see what remains
604  unsigned short unaccountedFlags = receiveHeader.flags & ~(HeaderData::FlagBits::NEW_STYLE_TRANSFER
605  | HeaderData::FlagBits::HEADER_V3 | HeaderData::FlagBits::HEADER_V4 | HeaderData::FlagBits::HEADER_V5);
606  if (unaccountedFlags != 0) {
607  // Newer protocol (unknown flag present) - we will try to continue
608  // since connection has not been refused earlier
609  static bool warnedOnceForward = false;
610  if (!warnedOnceForward) {
611  LOG_WARN("Warning: forward-compatible mode; will attempt to process image stream with unknown extra flags. Consider upgrading the client software.");
612  warnedOnceForward = true;
613  }
614  }
615 
616  imageSet.setWidth(receiveHeader.width);
617  imageSet.setHeight(receiveHeader.height);
618 
619  imageSet.setPixelFormat(0, static_cast<ImageSet::ImageFormat>(receiveHeader.format0));
620  if (imageSet.getNumberOfImages() > 1) imageSet.setPixelFormat(1, static_cast<ImageSet::ImageFormat>(receiveHeader.format1));
621  if (imageSet.getNumberOfImages() > 2) imageSet.setPixelFormat(2, static_cast<ImageSet::ImageFormat>(receiveHeader.format2));
622  if (imageSet.getNumberOfImages() > 3) imageSet.setPixelFormat(3, static_cast<ImageSet::ImageFormat>(receiveHeader.format3));
623 
624  int rowStrideArr[ImageSet::MAX_SUPPORTED_IMAGES] = {0};
625  int validRowsArr[ImageSet::MAX_SUPPORTED_IMAGES] = {0};
626  unsigned char* pixelArr[ImageSet::MAX_SUPPORTED_IMAGES] = {nullptr};
627 
628  if (isInterleaved) {
629  // OLD transfer (forced to interleaved 2 images mode)
630  static bool warnedOnceBackward = false;
631  if (!warnedOnceBackward) {
632  LOG_WARN("Info: backward-compatible mode; the device is sending with a legacy protocol. Consider upgrading its firmware.");
633  warnedOnceBackward = true;
634  }
635  unsigned char* data = dataProt.getBlockReceiveBuffer(0);
636  int validBytes = dataProt.getBlockValidSize(0);
637  for (int i=0; i < 2; ++i) {
638  pixelArr[i] = decodeInterleaved(i, imageSet.getNumberOfImages(), validBytes, data, validRowsArr[i], rowStrideArr[i]);
639  }
640  // Legacy sender with mode-dependent channel selection
641  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_LEFT, 0);
642  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_RIGHT, flaggedDisparityPair ? -1 : 1);
643  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_DISPARITY, flaggedDisparityPair ? 1 : -1);
644  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_COLOR, -1);
645  } else {
646  // NEW transfer
647  try {
648  for (int i=0; i<receiveHeader.numberOfImages; ++i) {
649  unsigned char* data = dataProt.getBlockReceiveBuffer(i);
650  int validBytes = dataProt.getBlockValidSize(i);
651  pixelArr[i] = decodeNoninterleaved(i, imageSet.getNumberOfImages(), validBytes, data, validRowsArr[i], rowStrideArr[i]);
652  }
653  } catch(const ProtocolException& ex) {
654  LOG_WARN("Protocol exception: " + ex.what());
655  resetReception();
656  return false;
657  }
658  if (arbitraryChannels) {
659  // Completely customizable channel selection
660  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_LEFT, -1);
661  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_RIGHT, -1);
662  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_DISPARITY, -1);
663  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_COLOR, -1);
664  for (int i=0; i<imageSet.getNumberOfImages(); ++i) {
665  int typ = receiveHeader.imageTypes[i];
666  ImageSet::ImageType imgtype = static_cast<ImageSet::ImageType>(typ);
667  imageSet.setIndexOf(imgtype, i);
668  }
669  } else {
670  static bool warnedOnceV2 = false;
671  if (!warnedOnceV2) {
672  LOG_WARN("Info: received a transfer with header v2");
673  warnedOnceV2 = true;
674  }
675  // Older v2 header; accessing imageTypes is not valid
676  // Two-image sender with mode-dependent channel selection
677  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_LEFT, 0);
678  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_RIGHT, flaggedDisparityPair ? -1 : 1);
679  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_DISPARITY, flaggedDisparityPair ? 1 : -1);
680  imageSet.setIndexOf(ImageSet::ImageType::IMAGE_COLOR, -1);
681  }
682  if(hasExposureTime) {
683  imageSet.setExposureTime(receiveHeader.exposureTime);
684  imageSet.setLastSyncPulse(receiveHeader.lastSyncPulseSec, receiveHeader.lastSyncPulseMicrosec);
685  }
686  }
687 
688  for (int i=0; i<receiveHeader.numberOfImages; ++i) {
689  imageSet.setRowStride(i, rowStrideArr[i]);
690  imageSet.setPixelData(i, pixelArr[i]);
691  }
692  imageSet.setQMatrix(receiveHeader.q);
693 
694  imageSet.setSequenceNumber(receiveHeader.seqNum);
695  imageSet.setTimestamp(receiveHeader.timeSec, receiveHeader.timeMicrosec);
696  imageSet.setDisparityRange(receiveHeader.minDisparity, receiveHeader.maxDisparity);
697  imageSet.setSubpixelFactor(receiveHeader.subpixelFactor);
698 
699  validRows = validRowsArr[0];
700  for (int i=0; i<receiveHeader.numberOfImages; ++i) {
701  if (validRowsArr[i] < validRows) {
702  validRows = validRowsArr[i];
703  }
704  }
705 
706  if(validRows == receiveHeader.height || receptionDone) {
707  complete = true;
708  resetReception();
709  }
710 
711  return true;
712  }
713 }
714 
715 unsigned char* ImageProtocol::Pimpl::decodeNoninterleaved(int imageNumber, int numImages, int receivedBytes,
716  unsigned char* data, int& validRows, int& rowStride) {
717  ImageSet::ImageFormat format;
718  int bits = 8;
719  switch (imageNumber) {
720  case 0:
721  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format0);
722  break;
723  case 1:
724  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format1);
725  break;
726  case 2:
727  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format2);
728  break;
729  case 3:
730  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format3);
731  break;
732  default:
733  throw ProtocolException("Not implemented: decodeNoninterleaved with image index > 2");
734  }
735  bits = getFormatBits(static_cast<ImageSet::ImageFormat>(format), false);
736 
737  int totalBits = bits;
738  unsigned char* ret = nullptr;
739 
740  if(receiveHeader.lastTileWidth == 0) {
741  int bufferOffset0 = 0;
742  int bufferRowStride = receiveHeader.width*(totalBits) / 8;
743 
744  if(format == ImageSet::FORMAT_8_BIT_MONO || format == ImageSet::FORMAT_8_BIT_RGB) {
745  // No decoding is necessary. We can just pass through the
746  // data pointer
747  ret = &data[bufferOffset0];
748  rowStride = bufferRowStride;
749  validRows = std::min(receivedBytes / bufferRowStride, (int)receiveHeader.height);
750  } else {
751  // Perform 12-bit => 16 bit decoding
752  allocateDecodeBuffer(imageNumber);
753  validRows = std::min(receivedBytes / bufferRowStride, (int)receiveHeader.height);
754  rowStride = 2*receiveHeader.width;
755  int lastRow = std::min(lastReceivedPayloadBytes[imageNumber] / bufferRowStride, validRows);
756 
757  BitConversions::decode12BitPacked(lastRow, validRows, &data[bufferOffset0],
758  &decodeBuffer[imageNumber][0], bufferRowStride, rowStride, receiveHeader.width);
759 
760  ret = &decodeBuffer[imageNumber][0];
761  }
762  } else {
763  // Decode the tiled transfer
764  decodeTiledImage(imageNumber,
765  lastReceivedPayloadBytes[imageNumber], receivedBytes, data,
766  receiveHeader.firstTileWidth * (totalBits) / 8,
767  receiveHeader.middleTilesWidth * (totalBits) / 8,
768  receiveHeader.lastTileWidth * (totalBits) / 8,
769  validRows, format, false);
770  ret = &decodeBuffer[imageNumber][0];
771  rowStride = receiveHeader.width*getFormatBits(
772  static_cast<ImageSet::ImageFormat>(format), true)/8;
773  }
774 
775  lastReceivedPayloadBytes[imageNumber] = receivedBytes;
776  return ret;
777 }
778 
779 
780 unsigned char* ImageProtocol::Pimpl::decodeInterleaved(int imageNumber, int numImages, int receivedBytes,
781  unsigned char* data, int& validRows, int& rowStride) {
782  ImageSet::ImageFormat format = static_cast<ImageSet::ImageFormat>(
783  imageNumber == 0 ? receiveHeader.format0 : receiveHeader.format1);
784  int bits0 = getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format0), false);
785  int bits1 = getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format1), false);
786  int bits2 = getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format2), false);
787  int bits3 = getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format3), false);
788 
789  int totalBits = (numImages<3)?(bits0 + bits1):(bits0 + bits1 + bits2 + bits3);
790 
791  unsigned char* ret = nullptr;
792 
793  if(receiveHeader.lastTileWidth == 0) {
794  int bufferOffset;
795  switch (imageNumber) {
796  case 0: { bufferOffset = 0; break; }
797  case 1: { bufferOffset = receiveHeader.width * bits0/8; break; }
798  case 2: { bufferOffset = receiveHeader.width * (bits0 + bits1)/8; break; }
799  default:
800  throw ProtocolException("Not implemented: image index > 2");
801  }
802  int bufferRowStride = receiveHeader.width*(totalBits) / 8;
803 
804  if(format == ImageSet::FORMAT_8_BIT_MONO || format == ImageSet::FORMAT_8_BIT_RGB) {
805  // No decoding is necessary. We can just pass through the
806  // data pointer
807  ret = &data[bufferOffset];
808  rowStride = bufferRowStride;
809  validRows = receivedBytes / bufferRowStride;
810  } else {
811  // Perform 12-bit => 16 bit decoding
812  allocateDecodeBuffer(imageNumber);
813  validRows = std::min(receivedBytes / bufferRowStride, (int)receiveHeader.height);
814  rowStride = 2*receiveHeader.width;
815  int lastRow = lastReceivedPayloadBytes[imageNumber] / bufferRowStride;
816 
817  BitConversions::decode12BitPacked(lastRow, validRows, &data[bufferOffset],
818  &decodeBuffer[imageNumber][0], bufferRowStride, rowStride, receiveHeader.width);
819 
820  ret = &decodeBuffer[imageNumber][0];
821  }
822  } else {
823  // Decode the tiled transfer
824  decodeTiledImage(imageNumber,
825  lastReceivedPayloadBytes[imageNumber], receivedBytes, data,
826  receiveHeader.firstTileWidth * (totalBits) / 8,
827  receiveHeader.middleTilesWidth * (totalBits) / 8,
828  receiveHeader.lastTileWidth * (totalBits) / 8,
829  validRows, format, true);
830  ret = &decodeBuffer[imageNumber][0];
831  rowStride = receiveHeader.width*getFormatBits(
832  static_cast<ImageSet::ImageFormat>(format), true)/8;
833  }
834 
835  lastReceivedPayloadBytes[imageNumber] = receivedBytes;
836  return ret;
837 }
838 
839 void ImageProtocol::Pimpl::allocateDecodeBuffer(int imageNumber) {
840  ImageSet::ImageFormat format;
841  switch (imageNumber) {
842  case 0:
843  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format0);
844  break;
845  case 1:
846  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format1);
847  break;
848  case 2:
849  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format2);
850  break;
851  case 3:
852  format = static_cast<ImageSet::ImageFormat>(receiveHeader.format3);
853  break;
854  default:
855  throw ProtocolException("Not implemented: allocateDecodeBuffer with image index > 2");
856  }
857  int bitsPerPixel = getFormatBits(format, true);
858  int bufferSize = receiveHeader.width * receiveHeader.height * bitsPerPixel / 8;
859 
860  if(decodeBuffer[imageNumber].size() != static_cast<unsigned int>(bufferSize)) {
861  decodeBuffer[imageNumber].resize(bufferSize);
862  }
863 }
864 
865 void ImageProtocol::Pimpl::decodeTiledImage(int imageNumber, int lastReceivedPayloadBytes, int receivedPayloadBytes,
866  const unsigned char* data, int firstTileStride, int middleTilesStride, int lastTileStride, int& validRows,
867  ImageSet::ImageFormat format, bool dataIsInterleaved) {
868  // Allocate a decoding buffer
869  allocateDecodeBuffer(imageNumber);
870 
871  // Get beginning and end of first tile
872  int numTiles = getNumTiles(receiveHeader.width, receiveHeader.firstTileWidth,
873  receiveHeader.middleTilesWidth, receiveHeader.lastTileWidth);
874  int payloadOffset = 0;
875  int decodeXOffset = 0;
876  int prevTileStrides = 0;
877  for(int i = 0; i < numTiles; i++) {
878  // Get relevant parameters
879  int tileWidth = 0;
880  int tileStride = 0;
881 
882  if(i == 0) {
883  tileStride = firstTileStride;
884  tileWidth = receiveHeader.firstTileWidth;
885  } else if(i == numTiles-1) {
886  tileStride = lastTileStride;
887  tileWidth = receiveHeader.lastTileWidth;
888  } else {
889  tileStride = middleTilesStride;
890  tileWidth = receiveHeader.middleTilesWidth;
891  }
892 
893  int tileStart = std::max(0, (lastReceivedPayloadBytes - payloadOffset) / tileStride);
894  int tileStop = std::min(std::max(0, (receivedPayloadBytes - payloadOffset) / tileStride), (int)receiveHeader.height);
895  int tileOffset;
896  if (dataIsInterleaved) {
897  switch (imageNumber) {
898  case 0: { tileOffset = 0; break; }
899  case 1: { tileOffset = tileWidth * (
900  getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format0), false)
901  )/8; break; }
902  case 2: { tileOffset = tileWidth * (
903  getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format0), false)
904  + getFormatBits(static_cast<ImageSet::ImageFormat>(receiveHeader.format1), false)
905  )/8; break; }
906  default:
907  throw ProtocolException("Not implemented: image index > 2");
908  }
909  } else {
910  tileOffset = 0;
911  }
912  if(i > 0) {
913  tileOffset += receiveHeader.height * prevTileStrides;
914  }
915 
916  // Decode
917  int bytesPixel;
918  if(format == ImageSet::FORMAT_12_BIT_MONO) {
919  bytesPixel = 2;
920  BitConversions::decode12BitPacked(tileStart, tileStop, &data[tileOffset],
921  &decodeBuffer[imageNumber][decodeXOffset], tileStride, 2*receiveHeader.width, tileWidth);
922  } else {
923  bytesPixel = (format == ImageSet::FORMAT_8_BIT_RGB ? 3 : 1);
924  decodeRowsFromTile(tileStart, tileStop, &data[tileOffset],
925  &decodeBuffer[imageNumber][decodeXOffset], tileStride,
926  receiveHeader.width*bytesPixel, tileWidth*bytesPixel);
927  }
928 
929  payloadOffset += receiveHeader.height * tileStride;
930  decodeXOffset += tileWidth * bytesPixel;
931  prevTileStrides += tileStride;
932  if(i == numTiles-1) {
933  validRows = tileStop;
934  }
935  }
936 }
937 
938 void ImageProtocol::Pimpl::decodeRowsFromTile(int startRow, int stopRow, unsigned const char* src,
939  unsigned char* dst, int srcStride, int dstStride, int tileWidth) {
940  for(int y = startRow; y < stopRow; y++) {
941  memcpy(&dst[y*dstStride], &src[y*srcStride], tileWidth);
942  }
943 }
944 
945 void ImageProtocol::Pimpl::resetReception() {
946  receiveHeaderParsed = false;
947  for (int i=0; i<ImageSet::MAX_SUPPORTED_IMAGES; ++i) {
948  lastReceivedPayloadBytes[i] = 0;
949  }
950  dataProt.resetReception(false);
951  receptionDone = false;
952 }
953 
954 bool ImageProtocol::Pimpl::isConnected() const {
955  return dataProt.isConnected();
956 }
957 
958 const unsigned char* ImageProtocol::Pimpl::getNextControlMessage(int& length) {
959  return dataProt.getNextControlMessage(length);
960 }
961 
962 bool ImageProtocol::Pimpl::newClientConnected() {
963  return dataProt.newClientConnected();
964 }
965 
966 int ImageProtocol::Pimpl::getNumDroppedFrames() const {
967  return dataProt.getDroppedReceptions();
968 }
969 
970 std::string ImageProtocol::statusReport() {
971  return pimpl->statusReport();
972 }
973 std::string ImageProtocol::Pimpl::statusReport() {
974  return dataProt.statusReport();
975 }
976 
977 
978 
979 } // namespace
980 
void resetTransfer()
Aborts the transmission of the current transfer and performs a reset of the internal state...
unsigned char * getNextReceiveBuffer(int &maxLength)
Returns the buffer for receiving the next network message.
void setNumberOfImages(int number)
Sets the number of valid images in this set.
Definition: imageset.h:414
int getRowStride(int imageNumber) const
Returns the row stride for the pixel data of one image.
Definition: imageset.h:221
void setTimestamp(int seconds, int microsec)
Sets the time at which this image set has been captured.
Definition: imageset.h:166
void setPixelData(int imageNumber, unsigned char *pixelData)
Sets the pixel data for the given image.
Definition: imageset.h:137
void getTimestamp(int &seconds, int &microsec) const
Returns the time at which this image set has been captured.
Definition: imageset.h:312
void processReceivedMessage(int length)
Handles a received network message.
const float * getQMatrix() const
Returns a pointer to the disparity-to-depth mapping matrix q.
Definition: imageset.h:296
void setWidth(int w)
Sets a new width for both images.
Definition: imageset.h:93
int getBitsPerPixel(int imageNumber) const
Returns the number of bits that are required to store one image pixel.
Definition: imageset.h:386
int getIndexOf(ImageType what, bool throwIfNotFound=false) const
Returns the index of a specific image type.
Definition: imageset.cpp:214
int getNumberOfImages() const
Returns the number of images in this set.
Definition: imageset.h:407
void setExposureTime(int timeMicrosec)
Sets the exposure time that was used for capturing the image set.
Definition: imageset.h:475
int getExposureTime() const
Gets the exposure time in microseconds that was used for capturing the image set. ...
Definition: imageset.h:485
unsigned int getSequenceNumber() const
Returns the sequence number for this image set.
Definition: imageset.h:303
void setSequenceNumber(unsigned int num)
Sets the sequence number for this image set.
Definition: imageset.h:155
int getHeight() const
Returns the height of each image.
Definition: imageset.h:210
void setHeight(int h)
Sets a new width for both images.
Definition: imageset.h:98
int getBytesPerPixel(int imageNumber) const
Returns the number of bytes that are required to store one image pixel.
Definition: imageset.h:375
bool isConnected() const
Returns true if a remote connection is established.
int getSubpixelFactor() const
Gets the subpixel factor for this image set.
Definition: imageset.h:333
const unsigned char * getNextControlMessage(int &length)
If a control message is pending to be transmitted then the message data will be returned by this meth...
void setPixelFormat(int imageNumber, ImageFormat format)
Sets the pixel format for the given image.
Definition: imageset.h:119
bool transferComplete()
Returns true if the current transfer has been completed.
bool getPartiallyReceivedImageSet(ImageSet &imageSet, int &validRows, bool &complete)
Returns a partially received image.
ProtocolType
Supported network protocols.
Definition: imageprotocol.h:43
void setRawTransferData(const ImageSet &metaData, const std::vector< unsigned char *> &imageData, int firstTileWidth=0, int middleTilesWidth=0, int lastTileWidth=0)
Sets the already pre-formatted image data for the next transfer.
void setRowStride(int imageNumber, int stride)
Sets a new row stride for the pixel data of one image.
Definition: imageset.h:107
void setLastSyncPulse(int seconds, int microsec)
Sets the timestamp of the last received sync pulse.
Definition: imageset.h:496
void setTransferImageSet(const ImageSet &imageSet)
Sets a new image that will be transfer.
bool imagesReceived() const
Returns true if the images of the current transfer have been received.
void getDisparityRange(int &minimum, int &maximum) const
Gets the value range for the disparity map contained in this image set. If the image set does not con...
Definition: imageset.h:325
ImageType
Supported image types.
Definition: imageset.h:67
ImageFormat
Image formats that can be transferred.
Definition: imageset.h:44
bool getReceivedImageSet(ImageSet &imageSet)
Returns a received image when complete.
unsigned char * getPixelData(int imageNumber) const
Returns the pixel data for the given image.
Definition: imageset.h:275
A set of one to three images, but usually two (the left camera image and the disparity map)...
Definition: imageset.h:38
ImageFormat getPixelFormat(int imageNumber) const
Returns the pixel format for the given image.
Definition: imageset.h:248
const unsigned char * getTransferMessage(int &length)
Gets the next network message for the current transfer.
void getLastSyncPulse(int &seconds, int &microsec) const
Gets the timestamp of the last received sync pulse.
Definition: imageset.h:508
A protocol for transmitting large blocks of data over a network.
bool newClientConnected()
Returns true if the last message has established a new connection from a client.
int getNumDroppedFrames() const
Returns the number of frames that have been dropped since connecting to the current remote host...
void setIndexOf(ImageType what, int idx)
Assign an image index to a specified ImageType, -1 to disable.
Definition: imageset.cpp:240
void setRawValidBytes(const std::vector< int > &validBytes)
Updates the number of valid bytes in a partial raw transfer.
void setDisparityRange(int minimum, int maximum)
Sets the value range for the disparity map contained in this image set.
Definition: imageset.h:178
Exception class that is used for all protocol exceptions.
Definition: exceptions.h:25
int getWidth() const
Returns the width of each image.
Definition: imageset.h:205
void resetReception()
Aborts the reception of the current image transfer and resets the internal state. ...
void setSubpixelFactor(int subpixFact)
Sets the subpixel factor for this image set.
Definition: imageset.h:186
void setQMatrix(const float *q)
Sets the pointer to the disparity-to-depth mapping matrix q.
Definition: imageset.h:148
Nerian Vision Technologies