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