CustusX  15.4.0-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxImageStreamerOpenCV.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 #include "cxImageStreamerOpenCV.h"
33 #include "cxConfig.h"
34 
35 #include <QCoreApplication>
36 #include <QTimer>
37 #include <QTime>
38 #include <QHostAddress>
39 #include "igtlOSUtil.h"
40 #include "igtlImageMessage.h"
41 #include "igtlServerSocket.h"
42 #include "vtkImageData.h"
43 #include "vtkSmartPointer.h"
44 #include "vtkMetaImageReader.h"
45 #include "vtkImageImport.h"
46 #include "vtkLookupTable.h"
47 #include "vtkImageMapToColors.h"
48 #include "vtkMetaImageWriter.h"
49 
50 #include "cxTypeConversions.h"
52 #include "cxStringHelpers.h"
53 #include "cxDoubleProperty.h"
54 #include "cxBoolProperty.h"
55 
56 #ifdef CX_USE_OpenCV
57 #include <opencv2/highgui/highgui.hpp>
58 #include <opencv2/imgproc/imgproc.hpp>
59 #endif
60 
61 namespace cx
62 {
63 
64 std::vector<PropertyPtr> ImageStreamerOpenCVArguments::getSettings(QDomElement root)
65 {
66  std::vector<PropertyPtr> retval;
67  retval.push_back(this->getVideoPortOption(root));
68  retval.push_back(this->getPrintPropertiesOption(root));
69  return retval;
70 }
71 
73 {
74  DoublePropertyPtr retval;
75  retval = DoubleProperty::initialize("videoport", "Video Port", "Select video source as an integer from 0 and up.", 0, DoubleRange(0, 10, 1), 0, root);
76  retval->setGuiRepresentation(DoublePropertyBase::grSPINBOX);
77  retval->setGroup("OpenCV");
78  return retval;
79 }
80 
82 {
83  BoolPropertyPtr retval;
84  bool defaultValue = false;
85  retval = BoolProperty::initialize("properties", "Print Properties",
86  "When starting OpenCV, print properties to console",
87  defaultValue, root);
88  retval->setAdvanced(true);
89  retval->setGroup("OpenCV");
90  return retval;
91 }
92 
94 {
95  StringMap retval;
96  retval["--type"] = "OpenCV";
97  retval["--videoport"] = this->getVideoPortOption(root)->getValueAsVariant().toString();
98  if (this->getPrintPropertiesOption(root)->getValue())
99  retval["--properties"] = "1";
100  return retval;
101 }
102 
104 {
105  QStringList retval;
106 #ifdef CX_USE_OpenCV
107  retval << "--videoport: video id, default=0";
108  retval << "--in_width: width of incoming image, default=camera";
109  retval << "--in_height: height of incoming image, default=camera";
110  retval << "--out_width: width of outgoing image, default=camera";
111  retval << "--out_height: width of outgoing image, default=camera";
112  retval << "--properties: dump image properties";
113 #endif
114  return retval;
115 }
116 
117 } // namespace cx
118 
119 //------------------------------------------------------------
120 //------------------------------------------------------------
121 //------------------------------------------------------------
122 
123 namespace
124 {
125 
126 //------------------------------------------------------------
127 // Function to generate random matrix.
128 void GetRandomTestMatrix(igtl::Matrix4x4& matrix)
129 {
130  //float position[3];
131  //float orientation[4];
132 
133  matrix[0][0] = 1.0;
134  matrix[1][0] = 0.0;
135  matrix[2][0] = 0.0;
136  matrix[3][0] = 0.0;
137  matrix[0][1] = 0.0;
138  matrix[1][1] = -1.0;
139  matrix[2][1] = 0.0;
140  matrix[3][1] = 0.0;
141  matrix[0][2] = 0.0;
142  matrix[1][2] = 0.0;
143  matrix[2][2] = 1.0;
144  matrix[3][2] = 0.0;
145  matrix[0][3] = 0.0;
146  matrix[1][3] = 0.0;
147  matrix[2][3] = 0.0;
148  matrix[3][3] = 1.0;
149 }
150 
151 }
152 
153 namespace cx
154 {
156 {
157  mGrabbing = false;
158  mAvailableImage = false;
159  setSendInterval(40);
160 
161 #ifdef CX_USE_OpenCV
162  mVideoCapture.reset(new cv::VideoCapture());
163 #endif
164  mSendTimer = new QTimer(this);
165  connect(mSendTimer, SIGNAL(timeout()), this, SLOT(send())); // this signal will be executed in the thread of THIS, i.e. the main thread.
166 }
167 
169 {
170  this->deinitialize_local();
171 }
172 
174 {
175  return "OpenCV";
176 }
177 
179 {
181 }
182 
183 
185 {
187 }
188 
189 void ImageStreamerOpenCV::deinitialize_local()
190 {
191 #ifdef CX_USE_OpenCV
192  while (mGrabbing) // grab() method seems to call processmessages itself...
193  qApp->processEvents();
194  mVideoCapture->release();
195  mVideoCapture.reset(new cv::VideoCapture());
196 #endif
197 }
198 
199 void ImageStreamerOpenCV::initialize_local()
200 {
201 
202  if (!mArguments.count("videoport"))
203  mArguments["videoport"] = "0";
204  if (!mArguments.count("out_width"))
205  mArguments["out_width"] = "";
206  if (!mArguments.count("out_height"))
207  mArguments["out_height"] = "";
208  if (!mArguments.count("in_width"))
209  mArguments["in_width"] = "";
210  if (!mArguments.count("in_height"))
211  mArguments["in_height"] = "";
212 
213  QString videoSource = mArguments["videoport"];
214  int videoport = convertStringWithDefault(mArguments["videoport"], 0);
215 
216  bool sourceIsInt = false;
217  videoSource.toInt(&sourceIsInt);
218 
219 #ifdef CX_USE_OpenCV
220  if (sourceIsInt){
221  // open device (camera)
222  mVideoCapture->open(videoport);
223  }
224  else{
225  // open file
226  mVideoCapture->open(videoSource.toStdString().c_str());
227  }
228 
229  if (!mVideoCapture->isOpened())
230  {
231  cerr << "ImageStreamerOpenCV: Failed to open a video device or video file!\n" << endl;
232  return;
233  }
234  else
235  {
236  //determine default values
237  int default_width = mVideoCapture->get(CV_CAP_PROP_FRAME_WIDTH);
238  int default_height = mVideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT);
239 
240  //set input size
241  int in_width = convertStringWithDefault(mArguments["in_width"], default_width);
242  int in_height = convertStringWithDefault(mArguments["in_height"], default_height);
243  mVideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, in_width);
244  mVideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, in_height);
245 
246  //set output size (resize)
247  int out_width = convertStringWithDefault(mArguments["out_width"], in_width);
248  int out_height = convertStringWithDefault(mArguments["out_height"], in_height);
249  mRescaleSize.setWidth(out_width);
250  mRescaleSize.setHeight(out_height);
251 
252  if (mArguments.count("properties"))
253  this->dumpProperties();
254 
255  std::cout << "ImageStreamerOpenCV: Started streaming from openCV device "
256  << videoSource.toStdString()
257  << ", size=(" << in_width << "," << in_height << ")";
258  if (( in_width!=mRescaleSize.width() )|| (in_height!=mRescaleSize.height()))
259  std::cout << ". Scaled to (" << mRescaleSize.width() << "," << mRescaleSize.height() << ")";
260 
261  std::cout << std::endl;
262  }
263 #endif
264 }
265 
267 {
268  this->initialize_local();
269 
270  if (!mSendTimer)
271  {
272  std::cout << "ImageStreamerOpenCV: Failed to start streaming: Not initialized." << std::endl;
273  return false;
274  }
275 
276  mSender = sender;
277  mSendTimer->start(getSendInterval());
278  this->continousGrabEvent(); // instead of grabtimer
279 
280  return true;
281 }
282 
284 {
285  if (!mSendTimer)
286  return;
287  mSendTimer->stop();
288  mSender.reset();
289 
290  this->deinitialize_local();
291 }
292 
293 void ImageStreamerOpenCV::dumpProperties()
294 {
295 #ifdef CX_USE_OpenCV
296  this->dumpProperty(CV_CAP_PROP_POS_MSEC, "CV_CAP_PROP_POS_MSEC");
297  this->dumpProperty(CV_CAP_PROP_POS_FRAMES, "CV_CAP_PROP_POS_FRAMES");
298  this->dumpProperty(CV_CAP_PROP_POS_AVI_RATIO, "CV_CAP_PROP_POS_AVI_RATIO");
299  this->dumpProperty(CV_CAP_PROP_FRAME_WIDTH, "CV_CAP_PROP_FRAME_WIDTH");
300  this->dumpProperty(CV_CAP_PROP_FRAME_HEIGHT, "CV_CAP_PROP_FRAME_HEIGHT");
301  this->dumpProperty(CV_CAP_PROP_FPS, "CV_CAP_PROP_FPS");
302  this->dumpProperty(CV_CAP_PROP_FOURCC, "CV_CAP_PROP_FOURCC");
303  this->dumpProperty(CV_CAP_PROP_FRAME_COUNT, "CV_CAP_PROP_FRAME_COUNT");
304  this->dumpProperty(CV_CAP_PROP_FORMAT, "CV_CAP_PROP_FORMAT");
305  this->dumpProperty(CV_CAP_PROP_MODE, "CV_CAP_PROP_MODE");
306  this->dumpProperty(CV_CAP_PROP_BRIGHTNESS, "CV_CAP_PROP_BRIGHTNESS");
307  this->dumpProperty(CV_CAP_PROP_CONTRAST, "CV_CAP_PROP_CONTRAST");
308  this->dumpProperty(CV_CAP_PROP_SATURATION, "CV_CAP_PROP_SATURATION");
309  this->dumpProperty(CV_CAP_PROP_HUE, "CV_CAP_PROP_HUE");
310  this->dumpProperty(CV_CAP_PROP_GAIN, "CV_CAP_PROP_GAIN");
311  this->dumpProperty(CV_CAP_PROP_EXPOSURE, "CV_CAP_PROP_EXPOSURE");
312  this->dumpProperty(CV_CAP_PROP_CONVERT_RGB, "CV_CAP_PROP_CONVERT_RGB");
313  // this->dumpProperty(CV_CAP_PROP_WHITE_BALANCE, "CV_CAP_PROP_WHITE_BALANCE");
314  this->dumpProperty(CV_CAP_PROP_RECTIFICATION, "CV_CAP_PROP_RECTIFICATION");
315 #endif
316 }
317 
318 void ImageStreamerOpenCV::dumpProperty(int val, QString name)
319 {
320 #ifdef CX_USE_OpenCV
321  double value = mVideoCapture->get(val);
322  if (value != -1)
323  std::cout << "Property " << name.toStdString() << " : " << mVideoCapture->get(val) << std::endl;
324 #endif
325 }
326 
332 void ImageStreamerOpenCV::continousGrabEvent()
333 {
334  if (!mSendTimer->isActive())
335  return;
336  this->grab();
337  QMetaObject::invokeMethod(this, "continousGrabEvent", Qt::QueuedConnection);
338 }
339 
340 void ImageStreamerOpenCV::grab()
341 {
342 #ifdef CX_USE_OpenCV
343  if (!mVideoCapture->isOpened())
344  {
345  return;
346  }
347  mGrabbing = true;
348  // grab images from camera to opencv internal buffer, do not process
349  mVideoCapture->grab();
350  mLastGrabTime = QDateTime::currentDateTime();
351  mAvailableImage = true;
352  mGrabbing = false;
353 #endif
354 }
355 
356 void ImageStreamerOpenCV::send()
357 {
358  if (!mSender || !mSender->isReady())
359  return;
360  if (!mAvailableImage)
361  {
362 // reportDebug("dropped resend of frame");
363  return;
364  }
365  PackagePtr package(new Package());
366  package->mIgtLinkImageMessage = this->getImageMessage();
367  mSender->send(package);
368  mAvailableImage = false;
369 
370 // static int counter=0;
371 // if (++counter%50==0)
372 // std::cout << "=== ImageStreamerOpenCV send: " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
373 }
374 
375 IGTLinkImageMessage::Pointer ImageStreamerOpenCV::getImageMessage()
376 {
377  IGTLinkImageMessage::Pointer imgMsg = IGTLinkImageMessage::New();
378 
379 #ifdef CX_USE_OpenCV
380  if (!mVideoCapture->isOpened())
382 
383  QTime start = QTime::currentTime();
384 
385  cv::Mat frame_source;
386  // mVideoCapture >> frame_source;
387  if (!mVideoCapture->retrieve(frame_source, 0))
389 
390  if (this->thread() == QCoreApplication::instance()->thread() && !mSender)
391  {
392  cv::imshow("ImageStreamerOpenCV", frame_source);
393  }
394 
395  // std::cout << "grab" << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
396  // return igtl::ImageMessage::Pointer();
397 
398  igtl::TimeStamp::Pointer timestamp;
399  timestamp = igtl::TimeStamp::New();
400  // double now = 1.0/1000*(double)QDateTime::currentDateTime().toMSecsSinceEpoch();
401  double grabTime = 1.0 / 1000 * (double) mLastGrabTime.toMSecsSinceEpoch();
402  timestamp->SetTime(grabTime);
403  static QDateTime lastlastGrabTime = mLastGrabTime;
404 // std::cout << "OpenCV stamp:\t" << mLastGrabTime.toString("hh:mm:ss.zzz").toStdString() << std::endl;
405 // std::cout << "OpenCV diff:\t" <<lastlastGrabTime.msecsTo(mLastGrabTime) << "\tdelay:\t" << mLastGrabTime.msecsTo(QDateTime::currentDateTime()) << std::endl;
406  lastlastGrabTime = mLastGrabTime;
407 
408  cv::Mat frame = frame_source;
409  if (( frame.cols!=mRescaleSize.width() )|| (frame.rows!=mRescaleSize.height()))
410  {
411  cv::resize(frame_source, frame, cv::Size(mRescaleSize.width(), mRescaleSize.height()), 0, 0, CV_INTER_LINEAR);
412  }
413 
414  // std::cout << "grab " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
415 // std::cout << "WH=("<< frame.cols << "," << frame.rows << ")" << ", Channels,Depth=("<< frame.channels() << "," << frame.depth() << ")" << std::endl;
416 
417 
418  int size[] =
419  { 1.0, 1.0, 1.0 }; // spacing (mm/pixel)
420  size[0] = frame.cols;
421  size[1] = frame.rows;
422 
423  float spacingF[] =
424  { 1.0, 1.0, 1.0 }; // spacing (mm/pixel)
425  int* svsize = size;
426  int svoffset[] =
427  { 0, 0, 0 }; // sub-volume offset
428  int scalarType = -1;
429 
430  if (frame.channels() == 3 || frame.channels() == 4)
431  {
432  scalarType = IGTLinkImageMessage::TYPE_UINT32;// scalar type
433  }
434  else if (frame.channels() == 1)
435  {
436  if (frame.depth() == 16)
437  {
438  scalarType = IGTLinkImageMessage::TYPE_UINT16;// scalar type
439  }
440  else if (frame.depth() == 8)
441  {
442  scalarType = IGTLinkImageMessage::TYPE_UINT8;// scalar type
443  }
444  }
445 
446  if (scalarType == -1)
447  {
448  std::cerr << "unknown image type" << std::endl;
450  }
451  //------------------------------------------------------------
452  // Create a new IMAGE type message
453 // IGTLinkImageMessage::Pointer imgMsg = IGTLinkImageMessage::New();
454  imgMsg->SetDimensions(size);
455  imgMsg->SetSpacing(spacingF);
456  imgMsg->SetScalarType(scalarType);
457  imgMsg->SetSubVolume(svsize, svoffset);
458  imgMsg->AllocateScalars();
459  imgMsg->SetTimeStamp(timestamp);
460 
461  unsigned char* destPtr = reinterpret_cast<unsigned char*> (imgMsg->GetScalarPointer());
462  uchar* src = frame.data;
463  // std::cout << "pre copy " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
464  int N = size[0] * size[1];
465  QString colorFormat;
466 
467  if (frame.channels() >= 3)
468  {
469  if (frame.isContinuous())
470  {
471  // 3-channel continous colors
472  for (int i = 0; i < N; ++i)
473  {
474  *destPtr++ = 255;
475  *destPtr++ = src[2];
476  *destPtr++ = src[1];
477  *destPtr++ = src[0];
478  src += 3;
479  }
480  }
481  else
482  {
483 // std::cout << "noncontinous conversion, rows=" << size[1] << std::endl;
484  for (int i=0; i<size[1]; ++i)
485  {
486  const uchar* src = frame.ptr<uchar>(i);
487  for (int j=0; j<size[0]; ++j)
488  {
489  *destPtr++ = 255;
490  *destPtr++ = src[2];
491  *destPtr++ = src[1];
492  *destPtr++ = src[0];
493  src += 3;
494  }
495  }
496  }
497  colorFormat = "ARGB";
498  }
499  if (frame.channels() == 1)
500  {
501  if (!frame.isContinuous())
502  {
503  std::cout << "Error: Non-continous frame data." << std::endl;
505  }
506 
507  // BW camera
508  for (int i = 0; i < N; ++i)
509  {
510  *destPtr++ = *src++;
511  }
512  colorFormat = "R";
513  }
514 
515  imgMsg->SetDeviceName(cstring_cast(QString("cxOpenCV [%1]").arg(colorFormat)));
516 
517  //------------------------------------------------------------
518  // Get randome orientation matrix and set it.
519  igtl::Matrix4x4 matrix;
520  GetRandomTestMatrix(matrix);
521  imgMsg->SetMatrix(matrix);
522 
523 // std::cout << " grab+process " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
524 #endif
525  return imgMsg;
526 }
527 
528 //------------------------------------------------------------
529 //------------------------------------------------------------
530 //------------------------------------------------------------
531 
532 }
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
DoublePropertyBasePtr getVideoPortOption(QDomElement root)
void setSendInterval(int milliseconds)
how often an image should be sent (in milliseconds)
Definition: cxStreamer.cpp:43
virtual void initialize(StringMap arguments)
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:53
virtual QStringList getArgumentDescription()
cstring_cast_Placeholder cstring_cast(const T &val)
BoolPropertyBasePtr getPrintPropertiesOption(QDomElement root)
StringMap convertToCommandLineArguments(QDomElement root)
std::map< QString, QString > StringMap
virtual bool startStreaming(SenderPtr sender)
boost::shared_ptr< class BoolPropertyBase > BoolPropertyBasePtr
boost::shared_ptr< class DoublePropertyBase > DoublePropertyBasePtr
boost::shared_ptr< class DoubleProperty > DoublePropertyPtr
boost::shared_ptr< struct Package > PackagePtr
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
QTimer * mSendTimer
Definition: cxStreamer.h:85
unsigned char uchar
int convertStringWithDefault(QString text, int def)
boost::shared_ptr< class BoolProperty > BoolPropertyPtr
boost::shared_ptr< Sender > SenderPtr
Definition: cxSender.h:88
SenderPtr mSender
Definition: cxStreamer.h:84
virtual void initialize(StringMap arguments)
Definition: cxStreamer.cpp:80
int getSendInterval() const
how often an image should be sent (in milliseconds)
Definition: cxStreamer.cpp:48
virtual std::vector< PropertyPtr > getSettings(QDomElement root)