CustusX  15.3.4-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 // ImageStreamer(parent),
157 // mSendTimer(0),
158 // mGrabTimer(0)
159 {
160  mGrabbing = false;
161  mAvailableImage = false;
162  setSendInterval(40);
163 
164 #ifdef CX_USE_OpenCV
165  mVideoCapture.reset(new cv::VideoCapture());
166 #endif
167 // mGrabTimer = new QTimer(this);
168 // connect(mGrabTimer, SIGNAL(timeout()), this, SLOT(grab())); // this signal will be executed in the thread of THIS, i.e. the main thread.
169  mSendTimer = new QTimer(this);
170  connect(mSendTimer, SIGNAL(timeout()), this, SLOT(send())); // this signal will be executed in the thread of THIS, i.e. the main thread.
171 }
172 
174 {
175  this->deinitialize_local();
176 }
177 
179 {
180  return "OpenCV";
181 }
182 
184 {
186 }
187 
188 
190 {
192 }
193 
194 void ImageStreamerOpenCV::deinitialize_local()
195 {
196 #ifdef CX_USE_OpenCV
197  while (mGrabbing) // grab() method seems to call processmessages itself...
198  qApp->processEvents();
199  mVideoCapture->release();
200  mVideoCapture.reset(new cv::VideoCapture());
201 #endif
202 }
203 
204 void ImageStreamerOpenCV::initialize_local()
205 {
206 // for (StringMap::iterator i=mArguments.begin(); i!=mArguments.end(); ++i)
207 // {
208 // std::cout << "A: " << i->first << " = " << i->second << std::endl;
209 // }
210 
211  if (!mArguments.count("videoport"))
212  mArguments["videoport"] = "0";
213  if (!mArguments.count("out_width"))
214  mArguments["out_width"] = "";
215  if (!mArguments.count("out_height"))
216  mArguments["out_height"] = "";
217  if (!mArguments.count("in_width"))
218  mArguments["in_width"] = "";
219  if (!mArguments.count("in_height"))
220  mArguments["in_height"] = "";
221 
222  QString videoSource = mArguments["videoport"];
223  int videoport = convertStringWithDefault(mArguments["videoport"], 0);
224 
225  bool sourceIsInt = false;
226  videoSource.toInt(&sourceIsInt);
227 
228 #ifdef CX_USE_OpenCV
229  if (sourceIsInt){
230  // open device (camera)
231  mVideoCapture->open(videoport);
232  }
233  else{
234  // open file
235  mVideoCapture->open(videoSource.toStdString().c_str());
236  }
237 
238  if (!mVideoCapture->isOpened())
239  {
240  cerr << "ImageStreamerOpenCV: Failed to open a video device or video file!\n" << endl;
241  return;
242  }
243  else
244  {
245  //determine default values
246  int default_width = mVideoCapture->get(CV_CAP_PROP_FRAME_WIDTH);
247  int default_height = mVideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT);
248 
249  //set input size
250  int in_width = convertStringWithDefault(mArguments["in_width"], default_width);
251  int in_height = convertStringWithDefault(mArguments["in_height"], default_height);
252  mVideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, in_width);
253  mVideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, in_height);
254 
255  //set output size (resize)
256  int out_width = convertStringWithDefault(mArguments["out_width"], in_width);
257  int out_height = convertStringWithDefault(mArguments["out_height"], in_height);
258  mRescaleSize.setWidth(out_width);
259  mRescaleSize.setHeight(out_height);
260 
261  if (mArguments.count("properties"))
262  this->dumpProperties();
263 
264  std::cout << "ImageStreamerOpenCV: Started streaming from openCV device "
265  << videoSource.toStdString()
266  << ", size=(" << in_width << "," << in_height << ")";
267  if (( in_width!=mRescaleSize.width() )|| (in_height!=mRescaleSize.height()))
268  std::cout << ". Scaled to (" << mRescaleSize.width() << "," << mRescaleSize.height() << ")";
269 
270  std::cout << std::endl;
271  }
272 #endif
273 }
274 
276 {
277  this->initialize_local();
278 
279  if (!mSendTimer)
280 // if (!mGrabTimer || !mSendTimer)
281  {
282  std::cout << "ImageStreamerOpenCV: Failed to start streaming: Not initialized." << std::endl;
283  return false;
284  }
285 
286  mSender = sender;
287 // mGrabTimer->start(0);
288  mSendTimer->start(getSendInterval());
289  this->continousGrabEvent(); // instead of grabtimer
290 // mCounter.start();
291 // std::cout << "*** ImageStreamerOpenCV: Started" << std::endl;
292 
293  return true;
294 }
295 
297 {
298  if (!mSendTimer)
299 // if (!mGrabTimer || !mSendTimer)
300  return;
301 // mGrabTimer->stop();
302  mSendTimer->stop();
303  mSender.reset();
304 
305  this->deinitialize_local();
306 }
307 
308 void ImageStreamerOpenCV::dumpProperties()
309 {
310 #ifdef CX_USE_OpenCV
311  this->dumpProperty(CV_CAP_PROP_POS_MSEC, "CV_CAP_PROP_POS_MSEC");
312  this->dumpProperty(CV_CAP_PROP_POS_FRAMES, "CV_CAP_PROP_POS_FRAMES");
313  this->dumpProperty(CV_CAP_PROP_POS_AVI_RATIO, "CV_CAP_PROP_POS_AVI_RATIO");
314  this->dumpProperty(CV_CAP_PROP_FRAME_WIDTH, "CV_CAP_PROP_FRAME_WIDTH");
315  this->dumpProperty(CV_CAP_PROP_FRAME_HEIGHT, "CV_CAP_PROP_FRAME_HEIGHT");
316  this->dumpProperty(CV_CAP_PROP_FPS, "CV_CAP_PROP_FPS");
317  this->dumpProperty(CV_CAP_PROP_FOURCC, "CV_CAP_PROP_FOURCC");
318  this->dumpProperty(CV_CAP_PROP_FRAME_COUNT, "CV_CAP_PROP_FRAME_COUNT");
319  this->dumpProperty(CV_CAP_PROP_FORMAT, "CV_CAP_PROP_FORMAT");
320  this->dumpProperty(CV_CAP_PROP_MODE, "CV_CAP_PROP_MODE");
321  this->dumpProperty(CV_CAP_PROP_BRIGHTNESS, "CV_CAP_PROP_BRIGHTNESS");
322  this->dumpProperty(CV_CAP_PROP_CONTRAST, "CV_CAP_PROP_CONTRAST");
323  this->dumpProperty(CV_CAP_PROP_SATURATION, "CV_CAP_PROP_SATURATION");
324  this->dumpProperty(CV_CAP_PROP_HUE, "CV_CAP_PROP_HUE");
325  this->dumpProperty(CV_CAP_PROP_GAIN, "CV_CAP_PROP_GAIN");
326  this->dumpProperty(CV_CAP_PROP_EXPOSURE, "CV_CAP_PROP_EXPOSURE");
327  this->dumpProperty(CV_CAP_PROP_CONVERT_RGB, "CV_CAP_PROP_CONVERT_RGB");
328  // this->dumpProperty(CV_CAP_PROP_WHITE_BALANCE, "CV_CAP_PROP_WHITE_BALANCE");
329  this->dumpProperty(CV_CAP_PROP_RECTIFICATION, "CV_CAP_PROP_RECTIFICATION");
330 #endif
331 }
332 
333 void ImageStreamerOpenCV::dumpProperty(int val, QString name)
334 {
335 #ifdef CX_USE_OpenCV
336  double value = mVideoCapture->get(val);
337  if (value != -1)
338  std::cout << "Property " << name.toStdString() << " : " << mVideoCapture->get(val) << std::endl;
339 #endif
340 }
341 
347 void ImageStreamerOpenCV::continousGrabEvent()
348 {
349  if (!mSendTimer->isActive())
350  return;
351  this->grab();
352  QMetaObject::invokeMethod(this, "continousGrabEvent", Qt::QueuedConnection);
353 }
354 
355 void ImageStreamerOpenCV::grab()
356 {
357 #ifdef CX_USE_OpenCV
358  if (!mVideoCapture->isOpened())
359  {
360  return;
361  }
362  mGrabbing = true;
363  // grab images from camera to opencv internal buffer, do not process
364  mVideoCapture->grab();
365  mLastGrabTime = QDateTime::currentDateTime();
366  mAvailableImage = true;
367  mGrabbing = false;
368 #endif
369 }
370 
371 void ImageStreamerOpenCV::send()
372 {
373  if (!mSender || !mSender->isReady())
374  return;
375  if (!mAvailableImage)
376  {
377 // reportDebug("dropped resend of frame");
378  return;
379  }
380  PackagePtr package(new Package());
381  package->mIgtLinkImageMessage = this->getImageMessage();
382  mSender->send(package);
383  mAvailableImage = false;
384 
385 // static int counter=0;
386 // if (++counter%50==0)
387 // std::cout << "=== ImageStreamerOpenCV send: " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
388 }
389 
390 IGTLinkImageMessage::Pointer ImageStreamerOpenCV::getImageMessage()
391 {
392  IGTLinkImageMessage::Pointer imgMsg = IGTLinkImageMessage::New();
393 
394 #ifdef CX_USE_OpenCV
395  if (!mVideoCapture->isOpened())
397 
398  QTime start = QTime::currentTime();
399 
400  cv::Mat frame_source;
401  // mVideoCapture >> frame_source;
402  if (!mVideoCapture->retrieve(frame_source, 0))
404 
405  if (this->thread() == QCoreApplication::instance()->thread() && !mSender)
406  {
407  cv::imshow("ImageStreamerOpenCV", frame_source);
408  }
409 
410  // std::cout << "grab" << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
411  // return igtl::ImageMessage::Pointer();
412 
413  igtl::TimeStamp::Pointer timestamp;
414  timestamp = igtl::TimeStamp::New();
415  // double now = 1.0/1000*(double)QDateTime::currentDateTime().toMSecsSinceEpoch();
416  double grabTime = 1.0 / 1000 * (double) mLastGrabTime.toMSecsSinceEpoch();
417  timestamp->SetTime(grabTime);
418  static QDateTime lastlastGrabTime = mLastGrabTime;
419 // std::cout << "OpenCV stamp:\t" << mLastGrabTime.toString("hh:mm:ss.zzz").toStdString() << std::endl;
420 // std::cout << "OpenCV diff:\t" <<lastlastGrabTime.msecsTo(mLastGrabTime) << "\tdelay:\t" << mLastGrabTime.msecsTo(QDateTime::currentDateTime()) << std::endl;
421  lastlastGrabTime = mLastGrabTime;
422 
423  cv::Mat frame = frame_source;
424  if (( frame.cols!=mRescaleSize.width() )|| (frame.rows!=mRescaleSize.height()))
425  {
426  cv::resize(frame_source, frame, cv::Size(mRescaleSize.width(), mRescaleSize.height()), 0, 0, CV_INTER_LINEAR);
427  }
428 
429  // std::cout << "grab " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
430 // std::cout << "WH=("<< frame.cols << "," << frame.rows << ")" << ", Channels,Depth=("<< frame.channels() << "," << frame.depth() << ")" << std::endl;
431 
432 
433  int size[] =
434  { 1.0, 1.0, 1.0 }; // spacing (mm/pixel)
435  size[0] = frame.cols;
436  size[1] = frame.rows;
437 
438  float spacingF[] =
439  { 1.0, 1.0, 1.0 }; // spacing (mm/pixel)
440  int* svsize = size;
441  int svoffset[] =
442  { 0, 0, 0 }; // sub-volume offset
443  int scalarType = -1;
444 
445  if (frame.channels() == 3 || frame.channels() == 4)
446  {
447  scalarType = IGTLinkImageMessage::TYPE_UINT32;// scalar type
448  }
449  else if (frame.channels() == 1)
450  {
451  if (frame.depth() == 16)
452  {
453  scalarType = IGTLinkImageMessage::TYPE_UINT16;// scalar type
454  }
455  else if (frame.depth() == 8)
456  {
457  scalarType = IGTLinkImageMessage::TYPE_UINT8;// scalar type
458  }
459  }
460 
461  if (scalarType == -1)
462  {
463  std::cerr << "unknown image type" << std::endl;
465  }
466  //------------------------------------------------------------
467  // Create a new IMAGE type message
468 // IGTLinkImageMessage::Pointer imgMsg = IGTLinkImageMessage::New();
469  imgMsg->SetDimensions(size);
470  imgMsg->SetSpacing(spacingF);
471  imgMsg->SetScalarType(scalarType);
472  imgMsg->SetSubVolume(svsize, svoffset);
473  imgMsg->AllocateScalars();
474  imgMsg->SetTimeStamp(timestamp);
475 
476  unsigned char* destPtr = reinterpret_cast<unsigned char*> (imgMsg->GetScalarPointer());
477  uchar* src = frame.data;
478  // std::cout << "pre copy " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
479  int N = size[0] * size[1];
480  QString colorFormat;
481 
482  if (frame.channels() >= 3)
483  {
484  if (frame.isContinuous())
485  {
486  // 3-channel continous colors
487  for (int i = 0; i < N; ++i)
488  {
489  *destPtr++ = 255;
490  *destPtr++ = src[2];
491  *destPtr++ = src[1];
492  *destPtr++ = src[0];
493  src += 3;
494  }
495  }
496  else
497  {
498 // std::cout << "noncontinous conversion, rows=" << size[1] << std::endl;
499  for (int i=0; i<size[1]; ++i)
500  {
501  const uchar* src = frame.ptr<uchar>(i);
502  for (int j=0; j<size[0]; ++j)
503  {
504  *destPtr++ = 255;
505  *destPtr++ = src[2];
506  *destPtr++ = src[1];
507  *destPtr++ = src[0];
508  src += 3;
509  }
510  }
511  }
512  colorFormat = "ARGB";
513  }
514  if (frame.channels() == 1)
515  {
516  if (!frame.isContinuous())
517  {
518  std::cout << "Error: Non-continous frame data." << std::endl;
520  }
521 
522  // BW camera
523  for (int i = 0; i < N; ++i)
524  {
525  *destPtr++ = *src++;
526  }
527  colorFormat = "R";
528  }
529 
530  imgMsg->SetDeviceName(cstring_cast(QString("cxOpenCV [%1]").arg(colorFormat)));
531 
532  //------------------------------------------------------------
533  // Get randome orientation matrix and set it.
534  igtl::Matrix4x4 matrix;
535  GetRandomTestMatrix(matrix);
536  imgMsg->SetMatrix(matrix);
537 
538 // std::cout << " grab+process " << start.msecsTo(QTime::currentTime()) << " ms" << std::endl;
539 #endif
540  return imgMsg;
541 }
542 
543 //------------------------------------------------------------
544 //------------------------------------------------------------
545 //------------------------------------------------------------
546 
547 }
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)