PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusAndorVideoSource.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
8 #include "vtkPlusDataSource.h"
10 #include "ATMCD32D.h"
11 #include "igtlOSUtil.h" // for Sleep
12 #if CV_MAJOR_VERSION > 3
13 #include "opencv2/calib3d.hpp"
14 #else
15 #include "opencv2/imgproc.hpp"
16 #endif
17 #include "opencv2/imgcodecs.hpp"
18 
19 
21 
22 // put these here so there is no public dependence on OpenCV
28 using CellIndices = std::vector<uint>;
29 std::map<int, CellIndices> cellsToCorrect;
30 using resizedFlatImage = cv::Mat;
31 std::map<int, resizedFlatImage> cvResizedFlatCorrection;
32 
33 // ----------------------------------------------------------------------------
34 void vtkPlusAndorVideoSource::PrintSelf(ostream& os, vtkIndent indent)
35 {
36  this->Superclass::PrintSelf(os, indent);
37 
38  os << indent << "Shutter: " << Shutter << std::endl;
39  os << indent << "ExposureTime: " << ExposureTime << std::endl;
40  os << indent << "Binning: " << HorizontalBins << " " << VerticalBins << std::endl;
41  os << indent << "HSSpeed: " << HSSpeed[0] << HSSpeed[1] << std::endl;
42  os << indent << "VSSpeedIndex: " << VSSpeedIndex << std::endl;
43  os << indent << "OutputSpacing: " << OutputSpacing[0] << " " << OutputSpacing[1] << std::endl;
44  os << indent << "PreAmpGainIndex: " << PreAmpGainIndex << std::endl;
45  os << indent << "AcquisitionMode: " << m_AcquisitionMode << std::endl;
46  os << indent << "ReadMode: " << m_ReadMode << std::endl;
47  os << indent << "TriggerMode: " << m_TriggerMode << std::endl;
48  os << indent << "RequireCoolTemp: " << RequireCoolTemp << std::endl;
49  os << indent << "CoolerMode: " << CoolerMode << std::endl;
50  os << indent << "CoolTemperature: " << CoolTemperature << std::endl;
51  os << indent << "SafeTemperature: " << SafeTemperature << std::endl;
52  os << indent << "CurrentTemperature: " << CurrentTemperature << std::endl;
53  os << indent << "CameraIntrinsics: " << cvCameraIntrinsics << std::endl;
54  os << indent << "DistortionCoefficients: " << cvDistortionCoefficients << std::endl;
55  os << indent << "UseFrameCorrections: " << UseFrameCorrections << std::endl;
56  os << indent << "UseCosmicRayCorrection: " << UseCosmicRayCorrection << std::endl;
57  os << indent << "FlatCorrection: " << flatCorrection << std::endl;
58  os << indent << "BiasDarkCorrection: " << biasDarkCorrection << std::endl;
59  os << indent << "BadPixelCorrection: " << badPixelCorrection << std::endl;
60 }
61 
62 // ----------------------------------------------------------------------------
63 PlusStatus vtkPlusAndorVideoSource::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
64 {
65  LOG_TRACE("vtkPlusAndorVideoSource::ReadConfiguration");
66  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
67 
68  long totalCameras = 0;
69  // It is possible to call GetAvailableCameras before any of the cameras are initialized.
70  unsigned availableCamerasResult = checkStatus(GetAvailableCameras(&totalCameras), "GetAvailableCameras");
71  if(availableCamerasResult == DRV_SUCCESS)
72  {
73  if(totalCameras == 0)
74  {
75  LOG_ERROR("Unable to find any Andor cameras devices installed.");
76  }
77  }
78 
79  // Must initialize the system before setting parameters
80  if(this->InitializeAndorCamera() != PLUS_SUCCESS)
81  {
82  return PLUS_FAIL;
83  }
84 
85  const char * shutterString = deviceConfig->GetAttribute("Shutter");
86  if(shutterString)
87  {
88  Shutter = static_cast<vtkPlusAndorVideoSource::ShutterMode>(std::atoi(shutterString));
89  }
90  else
91  {
92  Shutter = vtkPlusAndorVideoSource::ShutterMode::FullyAuto;
93  }
94 
95  const char * acquisitionModeString = deviceConfig->GetAttribute("AcquisitionMode");
96  if(acquisitionModeString)
97  {
98  m_AcquisitionMode = static_cast<vtkPlusAndorVideoSource::AcquisitionMode>(std::atoi(acquisitionModeString));
99  }
100  else
101  {
102  m_AcquisitionMode = vtkPlusAndorVideoSource::AcquisitionMode::SingleScan;
103  }
104 
105  const char * readModeString = deviceConfig->GetAttribute("ReadMode");
106  if(readModeString)
107  {
108  m_ReadMode = static_cast<vtkPlusAndorVideoSource::ReadMode>(std::atoi(readModeString));
109  }
110  else
111  {
112  m_ReadMode = vtkPlusAndorVideoSource::ReadMode::Image;
113  }
114 
115  const char * triggerModeString = deviceConfig->GetAttribute("TriggerMode");
116  if(triggerModeString)
117  {
118  m_TriggerMode = static_cast<vtkPlusAndorVideoSource::TriggerMode>(std::atoi(triggerModeString));
119  }
120  else
121  {
122  m_TriggerMode = vtkPlusAndorVideoSource::TriggerMode::Internal;
123  }
124 
125  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(float, ExposureTime, deviceConfig);
126  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, PreAmpGainIndex, deviceConfig);
127  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, CoolerMode, deviceConfig);
128  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, CoolTemperature, deviceConfig);
129  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, SafeTemperature, deviceConfig);
130  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, VSSpeedIndex, deviceConfig);
131  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, HorizontalBins, deviceConfig);
132  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, VerticalBins, deviceConfig);
133  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, ShutterClosingTimeMilliseconds, deviceConfig);
134  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, ShutterOpeningTimeMilliseconds, deviceConfig);
135 
136  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(InitializeCoolerState, deviceConfig);
137  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(RequireCoolTemp, deviceConfig);
138  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(UseFrameCorrections, deviceConfig);
139  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(UseCosmicRayCorrection, deviceConfig)
140 
141  deviceConfig->GetVectorAttribute("HSSpeed", 2, HSSpeed);
142  deviceConfig->GetVectorAttribute("OutputSpacing", 3, OutputSpacing);
143  deviceConfig->GetVectorAttribute("CameraIntrinsics", 9, cameraIntrinsics);
144  deviceConfig->GetVectorAttribute("DistortionCoefficients", 4, distortionCoefficients);
145  badPixelCorrection = deviceConfig->GetAttribute("BadPixelCorrection");
146  flatCorrection = deviceConfig->GetAttribute("FlatCorrection");
147  biasDarkCorrection = deviceConfig->GetAttribute("BiasDarkCorrection");
148 
149  cvCameraIntrinsics = cv::Mat(3, 3, CV_64FC1, cameraIntrinsics);
150  cvDistortionCoefficients = cv::Mat(1, 4, CV_64FC1, distortionCoefficients);
151  this->SetBadPixelCorrectionImage(badPixelCorrection); // load the image
152  this->SetFlatCorrectionImage(flatCorrection); // load and normalize if needed
153  this->SetBiasDarkCorrectionImage(biasDarkCorrection); // load the image
154 
155  return PLUS_SUCCESS;
156 }
157 
158 // ----------------------------------------------------------------------------
159 PlusStatus vtkPlusAndorVideoSource::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
160 {
161  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
162 
163  deviceConfig->SetIntAttribute("Shutter", this->Shutter);
164  deviceConfig->SetFloatAttribute("ExposureTime", this->ExposureTime);
165  deviceConfig->SetIntAttribute("PreAmpGainIndex", this->PreAmpGainIndex);
166  deviceConfig->SetIntAttribute("AcquisitionMode", this->m_AcquisitionMode);
167  deviceConfig->SetIntAttribute("ReadMode", this->m_ReadMode);
168  deviceConfig->SetIntAttribute("TriggerMode", this->m_TriggerMode);
169  deviceConfig->SetIntAttribute("CoolerMode", this->CoolerMode);
170  deviceConfig->SetIntAttribute("CoolTemperature", this->CoolTemperature);
171  deviceConfig->SetIntAttribute("SafeTemperature", this->SafeTemperature);
172  deviceConfig->SetIntAttribute("VSSpeedIndex", this->VSSpeedIndex);
173  deviceConfig->SetIntAttribute("HorizontalBins", this->HorizontalBins);
174  deviceConfig->SetIntAttribute("VerticalBins", this->VerticalBins);
175  deviceConfig->SetIntAttribute("ShutterClosingTimeMilliseconds", this->ShutterClosingTimeMilliseconds);
176  deviceConfig->SetIntAttribute("ShutterOpeningTimeMilliseconds", this->ShutterOpeningTimeMilliseconds);
177 
178  deviceConfig->SetVectorAttribute("HSSpeed", 2, HSSpeed);
179  deviceConfig->SetVectorAttribute("OutputSpacing", 3, OutputSpacing);
180  deviceConfig->SetVectorAttribute("CameraIntrinsics", 9, cameraIntrinsics);
181  deviceConfig->SetVectorAttribute("DistortionCoefficients", 4, distortionCoefficients);
182  deviceConfig->SetAttribute("FlatCorrection", flatCorrection.c_str());
183  deviceConfig->SetAttribute("BiasDarkCorrection", biasDarkCorrection.c_str());
184  deviceConfig->SetAttribute("BadPixelCorrection", badPixelCorrection.c_str());
185 
186  XML_WRITE_BOOL_ATTRIBUTE(UseFrameCorrections, deviceConfig);
187  XML_WRITE_BOOL_ATTRIBUTE(UseCosmicRayCorrection, deviceConfig);
188 
189  return PLUS_SUCCESS;
190 }
191 
192 
193 // ----------------------------------------------------------------------------
195 {
196  if(this->OutputChannels.empty())
197  {
198  LOG_ERROR("No output channels defined for vtkPlusAndorVideoSource. Cannot proceed.");
199  this->CorrectlyConfigured = false;
200  return PLUS_FAIL;
201  }
202 
203  return PLUS_SUCCESS;
204 }
205 
206 // ----------------------------------------------------------------------------
208 {
209  std::ostringstream versionString;
210 
211  char SDKVersion[256];
212  checkStatus(GetVersionInfo(AT_SDKVersion, SDKVersion, sizeof(SDKVersion)), "GetVersionInfo");
213  versionString << "Andor SDK version: " << SDKVersion << std::ends;
214 
215  return versionString.str();
216 }
217 
218 
219 //----------------------------------------------------------------------------
221 {
223 
224  this->StartThreadForInternalUpdates = false; // frames should not be acquired automatically
225  this->AcquisitionRate = 1.0; // this controls the frequency
226 }
227 
228 // ----------------------------------------------------------------------------
230 {
231  if(!this->Connected)
232  {
233  this->Disconnect();
234  }
235 }
236 
237 // ----------------------------------------------------------------------------
239 {
240  long totalCameras = 0;
241  // It is possible to call GetAvailableCameras before any of the cameras are initialized.
242  unsigned availableCamerasResult = checkStatus(GetAvailableCameras(&totalCameras), "GetAvailableCameras");
243  if(availableCamerasResult == DRV_SUCCESS)
244  {
245  if(totalCameras == 0)
246  {
247  LOG_ERROR("Unable to find any Andor cameras devices installed.");
248  return PLUS_FAIL;
249  }
250  }
251 
252  unsigned initializeResult = checkStatus(Initialize(""), "Initialize");
253  if(initializeResult != DRV_SUCCESS)
254  {
255  return PLUS_FAIL;
256  }
257 
258  char headModel[_MAX_PATH];
259  unsigned headModelResult = checkStatus(GetHeadModel(headModel), "GetHeadModel");
260  if(headModelResult == DRV_SUCCESS)
261  {
262  LOG_INFO("Andor Camera Model: " << headModel);
263  }
264 
265  int serialNumber;
266  unsigned cameraSNResult = checkStatus(GetCameraSerialNumber(&serialNumber), "GetCameraSerialNumber");
267  if(cameraSNResult == DRV_SUCCESS)
268  {
269  LOG_INFO("Andor Camera Serial Number: " << serialNumber);
270  }
271 
272  // Check the safe temperature, and the maximum allowable temperature on the camera.
273  // Use the min of the two as the safe temp.
274  int MinTemp, MaxTemp;
275  unsigned result = checkStatus(GetTemperatureRange(&MinTemp, &MaxTemp), "GetTemperatureRange");
276  if(result == DRV_SUCCESS)
277  {
278  LOG_INFO("The temperature range for the connected Andor Camera is: " << MinTemp << " and " << MaxTemp);
279  }
280 
281  if(MaxTemp < this->SafeTemperature)
282  {
283  this->SafeTemperature = MaxTemp;
284  }
285  if(this->CoolTemperature < MinTemp || this->CoolTemperature > MaxTemp)
286  {
287  LOG_ERROR("Requested temperature for Andor camera is out of range");
288  return PLUS_FAIL;
289  }
290 
292  this->CustomFields.clear();
294 
295  int x, y;
296  checkStatus(GetDetector(&x, &y), "GetDetector");
297  frameSize[0] = static_cast<unsigned>(x);
298  frameSize[1] = static_cast<unsigned>(y);
299 
300  // init to binning of 1 (meaning no binning), and full sensor size
301  checkStatus(SetImage(this->HorizontalBins, this->VerticalBins, 1, x, 1, y), "SetImage");
302 
303  return PLUS_SUCCESS;
304 }
305 
306 // ----------------------------------------------------------------------------
308 {
309  for(unsigned i = 0; i < port.size(); i++)
310  {
311  port[i]->SetPixelType(VTK_UNSIGNED_SHORT);
312  port[i]->SetImageType(US_IMG_BRIGHTNESS);
313  port[i]->SetOutputImageOrientation(US_IMG_ORIENT_MF);
314  port[i]->SetInputImageOrientation(US_IMG_ORIENT_MF);
315  port[i]->SetInputFrameSize(frameSize);
316 
317  LOG_INFO("Andor source initialized. ID: " << port[i]->GetId());
318  }
319 }
320 
321 // ----------------------------------------------------------------------------
323 {
324  LOG_TRACE("vtkPlusAndorVideoSource::InternalConnect");
325  if(this->InitializeAndorCamera() != PLUS_SUCCESS)
326  {
327  LOG_ERROR("Andor camera failed to initialize.");
328  return PLUS_FAIL;
329  }
330 
331  this->GetVideoSourcesByPortName("BLIRaw", BLIRaw);
332  this->GetVideoSourcesByPortName("BLICorrected", BLICorrected);
333  this->GetVideoSourcesByPortName("GrayRaw", GrayRaw);
334  this->GetVideoSourcesByPortName("GrayCorrected", GrayCorrected);
335 
336  if(BLIRaw.size() + BLICorrected.size() + GrayRaw.size() + GrayCorrected.size() == 0)
337  {
338  vtkPlusDataSource* aSource = nullptr;
339  if(this->GetFirstActiveOutputVideoSource(aSource) != PLUS_SUCCESS || aSource == nullptr)
340  {
341  LOG_ERROR("Standard data sources are not defined, and unable to retrieve the video source in the capturing device.");
342  return PLUS_FAIL;
343  }
344  BLIRaw.push_back(aSource); // this is the default port
345  }
346 
347  this->InitializePort(BLIRaw);
349  this->InitializePort(GrayRaw);
351 
352  this->PrepareAcquisition();
353 
354  return PLUS_SUCCESS;
355 }
356 
357 // ----------------------------------------------------------------------------
359 {
360  LOG_DEBUG("Disconnecting from Andor");
361  if(IsRecording())
362  {
363  this->InternalStopRecording();
364  }
365 
366  // // From Andor Employee:
367  // // Only Classic, ICCD and cameras with a fibre attached must have their cooling and warming up controlled at a particular rate.
368  // // For everything else you can just call ShutDown and the camera will safely return to room temperature.
369  // // Classic systems are cameras that use our original PCI controller cards eg CCI-010 or CCI-001.
370  // int status;
371  // checkStatus(::IsCoolerOn(&status), "IsCoolerOn");
372 
373  // if(status && this->CoolerMode == 0)
374  // {
375  // LOG_INFO("CoolerMode 0 and Cooler is still ON. Turning off the cooler and waiting for warmup. Do not unplug the camera from power.");
376  // WaitForWarmup();
377  // }
378 
379  // in case we quit before an acquisition is complete, close the acquisition thread
381 
382  checkStatus(FreeInternalMemory(), "FreeInternalMemory");
383 
384  unsigned result = checkStatus(ShutDown(), "ShutDown");
385  if(result == DRV_SUCCESS)
386  {
387  LOG_INFO("Andor camera shut down successfully.");
388  }
389 
390  return PLUS_SUCCESS;
391 }
392 
393 
394 // ----------------------------------------------------------------------------
396 {
397  return PLUS_SUCCESS;
398 }
399 
400 // ----------------------------------------------------------------------------
402 {
403  return PLUS_SUCCESS;
404 }
405 
406 // ----------------------------------------------------------------------------
407 void vtkPlusAndorVideoSource::AdjustBuffers(int horizontalBins, int verticalBins)
408 {
409  int x, y;
410  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
411 
412  frameSize[0] = x / horizontalBins;
413  frameSize[1] = y / verticalBins;
414 
415  for(unsigned i = 0; i < BLIRaw.size(); i++)
416  {
417  BLIRaw[i]->SetInputFrameSize(frameSize);
418  }
419  for(unsigned i = 0; i < BLICorrected.size(); i++)
420  {
421  BLICorrected[i]->SetInputFrameSize(frameSize);
422  }
423  for(unsigned i = 0; i < GrayRaw.size(); i++)
424  {
425  GrayRaw[i]->SetInputFrameSize(frameSize);
426  }
427  for(unsigned i = 0; i < GrayCorrected.size(); i++)
428  {
429  GrayCorrected[i]->SetInputFrameSize(frameSize);
430  }
431 }
432 
433 // ----------------------------------------------------------------------------
434 void vtkPlusAndorVideoSource::AdjustSpacing(int horizontalBins, int verticalBins)
435 {
436  std::ostringstream spacingStream;
437  spacingStream << this->OutputSpacing[0] * horizontalBins << " ";
438  spacingStream << this->OutputSpacing[1] * verticalBins << " ";
439  spacingStream << this->OutputSpacing[2];
440 
441  this->CustomFields["ElementSpacing"].first = FRAMEFIELD_FORCE_SERVER_SEND;
442  this->CustomFields["ElementSpacing"].second = spacingStream.str();
443  LOG_DEBUG("Adjusted spacing: " << spacingStream.str());
444 }
445 
446 // ----------------------------------------------------------------------------
448 {
449  this->CustomFields["ExposureTime"].first = FRAMEFIELD_FORCE_SERVER_SEND;
450  this->CustomFields["ExposureTime"].second = std::to_string(this->effectiveExpTime);
451  this->CustomFields["HorizontalBins"].first = FRAMEFIELD_FORCE_SERVER_SEND;
452  this->CustomFields["HorizontalBins"].second = std::to_string(this->effectiveHBins);
453  this->CustomFields["VerticalBins"].first = FRAMEFIELD_FORCE_SERVER_SEND;
454  this->CustomFields["VerticalBins"].second = std::to_string(this->effectiveVBins);
455 
457  this->CustomFields["Temperature"].first = FRAMEFIELD_FORCE_SERVER_SEND;
458  this->CustomFields["Temperature"].second = std::to_string(this->CurrentTemperature);
459 
460  std::ostringstream transformStream;
461  float transformValue;
462  for(unsigned i = 0; i < 15; i++)
463  {
464  transformValue = this->imageToReferenceTransform.at(i);
465  transformStream << this->imageToReferenceTransform.at(i) << " ";
466  }
467  transformStream << this->imageToReferenceTransform.back();
468 
469  this->CustomFields["ImageToReferenceTransform"].first = FRAMEFIELD_FORCE_SERVER_SEND;
470  this->CustomFields["ImageToReferenceTransform"].second = transformStream.str();
471  this->CustomFields["ImageToReferenceTransformStatus"].first = FRAMEFIELD_FORCE_SERVER_SEND;
472  this->CustomFields["ImageToReferenceTransformStatus"].second = "OK";
473 }
474 
475 // ----------------------------------------------------------------------------
477 {
478  this->imageToReferenceTransform = transform;
479  return PLUS_SUCCESS;
480 }
481 
482 // ----------------------------------------------------------------------------
484 {
485  std::vector<double> spacing = { 0.0, 0.0, 1.0 };
486  spacing[0] = this->OutputSpacing[0] * this->effectiveHBins;
487  spacing[1] = this->OutputSpacing[1] * this->effectiveVBins;
488  return spacing;
489 }
490 
491 // ----------------------------------------------------------------------------
493 {
494  int status = GetTemperatureF(temperature);
496  checkStatus(status, "GetTemperatureF");
497  return status;
498 }
499 
500 // ----------------------------------------------------------------------------
502 {
503  if(this->RequireCoolTemp == false)
504  {
505  return;
506  }
507  int status;
508  checkStatus(::IsCoolerOn(&status), "IsCoolerOn");
509  if(!status)
510  {
511  TurnCoolerON();
512  }
513  while(checkStatus(GetTemperatureF(&this->CurrentTemperature), "GetTemperatureF") != DRV_TEMPERATURE_STABILIZED)
514  {
515  igtl::Sleep(5000); // wait a bit
516  }
517 }
518 
519 // ----------------------------------------------------------------------------
521 {
522  int status;
523  checkStatus(::IsCoolerOn(&status), "IsCoolerOn");
524  if(status)
525  {
526  TurnCoolerOFF();
527  }
528  GetCurrentTemperature(&this->CurrentTemperature); // updates this->CurrentTemperature
529  if(this->CurrentTemperature < this->SafeTemperature)
530  {
531  while(this->CurrentTemperature < this->SafeTemperature)
532  {
533  igtl::Sleep(5000); // wait a bit
534  GetCurrentTemperature(&this->CurrentTemperature); // logs the status and temperature
535  }
536  }
537 }
538 
539 // ----------------------------------------------------------------------------
541 {
542  int x, y;
543  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
544  checkStatus(::SetExposureTime(this->effectiveExpTime), "SetExposureTime");
546  checkStatus(::SetImage(this->effectiveHBins, this->effectiveVBins, 1, x, 1, y), "Binning");
547  checkStatus(::SetVSSpeed(this->effectiveVSInd), "SetVSSpeed");
548  checkStatus(::SetHSSpeed(this->HSSpeed[0], this->effectiveHSInd), "SetHSSpeed");
549 
552 
554 
555  unsigned rawFrameSize = frameSize[0] * frameSize[1];
556  rawFrame.resize(rawFrameSize, 0);
557 
558  checkStatus(StartAcquisition(), "StartAcquisition");
559  unsigned result = checkStatus(::WaitForAcquisition(), "WaitForAcquisition");
560  if(result != DRV_SUCCESS)
561  {
562  LOG_ERROR("Acquisition failed or cancelled.");
563  return PLUS_FAIL;
564  }
565  this->currentTime = vtkIGSIOAccurateTimer::GetSystemTime();
566 
567  // iKon-M 934 has 16-bit digitization
568  // https://andor.oxinst.com/assets/uploads/products/andor/documents/andor-ikon-m-934-specifications.pdf
569  // so we choose 16-bit unsigned
570  // GetMostRecentImage() is 32 bit signed variant
571  checkStatus(GetMostRecentImage16(&rawFrame[0], rawFrameSize), "GetMostRecentImage16");
572 
573  return PLUS_SUCCESS;
574 }
575 
576 // ----------------------------------------------------------------------------
578 {
579  for(unsigned i = 0; i < ds.size(); i++)
580  {
581  if(ds[i]->AddItem(&rawFrame[0],
582  US_IMG_ORIENT_MF,
583  frameSize, VTK_UNSIGNED_SHORT,
584  1, US_IMG_BRIGHTNESS, 0,
585  this->FrameNumber,
586  currentTime,
587  currentTime, // just use the unfiltered timestamp so we don't drop frames
588  &this->CustomFields
589  ) != PLUS_SUCCESS)
590  {
591  LOG_WARNING("Error adding item to AndorCamera video source " << ds[i]->GetSourceId());
592  }
593  else
594  {
595  LOG_INFO("Success adding item to AndorCamera video source " << ds[i]->GetSourceId());
596  }
597  }
598 }
599 
600 // ----------------------------------------------------------------------------
602 {
603  std::vector<cv::Point> badIndicesXY;
604  cv::findNonZero(cvBadPixelImage, badIndicesXY);
605 
606  std::map<uint, int> badPixelCount;
607  for (int i = 0; i < badIndicesXY.size(); i++)
608  {
609  uint resolutionCellIndexX = badIndicesXY[i].x / binning;
610  uint resolutionCellIndexY = badIndicesXY[i].y / binning;
611  uint resolutionCellIndex = frameSize[1] * resolutionCellIndexY + resolutionCellIndexX;
612  badPixelCount[resolutionCellIndex] += 1;
613  }
614 
615  std::vector<uint> resolutionCellsToCorrect;
616  for (auto const& bpc : badPixelCount)
617  {
618  if (binning * binning / bpc.second < 5) // tolerate up to 20% dead pixels
619  {
620  resolutionCellsToCorrect.push_back(bpc.first);
621  }
622  }
623 
624  cellsToCorrect[binning] = resolutionCellsToCorrect;
625 }
626 
627 // ----------------------------------------------------------------------------
628 void vtkPlusAndorVideoSource::CorrectBadPixels(int binning, cv::Mat& cvIMG)
629 {
630  if (cellsToCorrect.find(binning) == cellsToCorrect.end()) // it needs to be calculated
631  {
632  FindBadCells(binning);
633  }
634  std::vector<uint> resolutionCellsToCorrect = cellsToCorrect[binning];
635  uint resolutionCellIndexX, resolutionCellIndexY;
636  std::vector<uint> valuesForMedian, correctedCells;
637  uint medianValue;
638  int startX, startY;
639  unsigned endX, endY;
640  int numCellsToCorrect = resolutionCellsToCorrect.size();
641  for (uint cell : resolutionCellsToCorrect)
642  {
643  resolutionCellIndexX = cell - frameSize[0] * (cell / frameSize[0]);
644  resolutionCellIndexY = cell / frameSize[0];
645  startX = resolutionCellIndexX - 1;
646  endX = resolutionCellIndexX + 1;
647  startY = resolutionCellIndexY - 1;
648  endY = resolutionCellIndexY + 1;
649  if (startX < 0) { startX = 0; }
650  if (startY < 0) { startY = 0; }
651  if (endX > frameSize[0]) { endX = frameSize[0]; }
652  if (endY > frameSize[0]) { endY = frameSize[0]; }
653 
654  for (uint x = startX; x <= endX; x++)
655  {
656  for (uint y = startY; y <= endY; y++)
657  {
658  if (std::find(resolutionCellsToCorrect.begin(), resolutionCellsToCorrect.end(), frameSize[0] * y + x) != resolutionCellsToCorrect.end())
659  {
660  if (std::find(correctedCells.begin(), correctedCells.end(), frameSize[0] * y + x) != correctedCells.end())
661  {
662  valuesForMedian.push_back(cvIMG.at<ushort>(y, x));
663  }
664  }
665  else
666  {
667  valuesForMedian.push_back(cvIMG.at<ushort>(y, x));
668  }
669  }
670  }
671 
672  sort(valuesForMedian.begin(), valuesForMedian.end());
673  if (valuesForMedian.size() % 2 == 0)
674  {
675  medianValue = (valuesForMedian[valuesForMedian.size() / 2 - 1] + valuesForMedian[valuesForMedian.size() / 2]) / 2;
676  }
677  else
678  {
679  medianValue = valuesForMedian[valuesForMedian.size() / 2];
680  }
681 
682  cvIMG.at<ushort>(resolutionCellIndexY, resolutionCellIndexX) = medianValue;
683  correctedCells.push_back(cell);
684  valuesForMedian.clear();
685  }
686 }
687 
688 // ----------------------------------------------------------------------------
689 void vtkPlusAndorVideoSource::ApplyCosmicRayCorrection(int bin, cv::Mat& floatImage)
690 {
691  int kernelSize = 3;
692  if (bin < 3)
693  {
694  kernelSize = 5;
695  }
696 
697  cv::Mat medianImage, diffImage, medianPixels;
698 
699  // idenfify cosmice ray indices
700  cv::medianBlur(floatImage, medianImage, kernelSize);
701  cv::subtract(floatImage, medianImage, diffImage);
702  cv::Mat cosmicInd = (diffImage > 50) & (diffImage > 4 * medianImage);
703  cv::Mat notCosmicInd = ~cosmicInd;
704  cosmicInd.convertTo(cosmicInd, floatImage.type());
705  notCosmicInd.convertTo(notCosmicInd, floatImage.type());
706  cosmicInd /= 255;
707  notCosmicInd /= 255;
708 
709  // use maskes to replace cosmic ray indices with values from median image
710  medianImage.convertTo(medianImage, floatImage.type());
711  cv::multiply(notCosmicInd, floatImage, floatImage);
712  cv::multiply(cosmicInd, medianImage, medianPixels);
713  floatImage += medianPixels;
714 }
715 
716 // ----------------------------------------------------------------------------
718 {
719  cv::Mat cvIMG(frameSize[0], frameSize[1], CV_16UC1, &rawFrame[0]); // uses rawFrame as buffer
720  CorrectBadPixels(binning, cvIMG);
721  LOG_INFO("Applied bad pixel correction");
722 
723  cv::Mat floatImage;
724  cvIMG.convertTo(floatImage, CV_32FC1);
725  cv::Mat result;
726 
727  if (cvBiasDarkCorrection.cols != frameSize[0] || cvBiasDarkCorrection.rows != frameSize[1])
728  {
729  LOG_ERROR("BiasDarkCorrectionImage size " << cvBiasDarkCorrection.size()
730  << " does not match the current frame size " << frameSize[0] << " x " << frameSize[1]);
731  }
732  else
733  {
734  cv::subtract(floatImage, cvBiasDarkCorrection, floatImage, cv::noArray(), CV_32FC1);
735  LOG_INFO("Applied constant bias+dark correction");
736  }
737 
738  if(this->UseCosmicRayCorrection)
739  {
740  ApplyCosmicRayCorrection(binning, floatImage);
741  LOG_INFO("Applied cosmic ray correction");
742  }
743 
744  // OpenCV's lens distortion correction
745  cv::undistort(floatImage, result, cvCameraIntrinsics, cvDistortionCoefficients);
746  LOG_INFO("Applied lens distortion correction");
747 
748  if (cvResizedFlatCorrection.find(binning) == cvResizedFlatCorrection.end()) // it needs to be calculated
749  {
750  ResizeFlatCorrectionImage(binning);
751  }
752 
753  if (cvResizedFlatCorrection[binning].cols != frameSize[0] || cvResizedFlatCorrection[binning].rows != frameSize[1])
754  {
755  LOG_ERROR("Flat correction image size " << cvFlatCorrection.size()
756  << " does not match the current frame size " << frameSize[0] << " x " << frameSize[1]);
757  }
758  else
759  {
760  // Divide the image by the 32-bit floating point correction image
761  cv::divide(result, cvResizedFlatCorrection[binning], result, 1, CV_32FC1);
762  LOG_INFO("Applied multiplicative flat correction");
763  }
764 
765  result.convertTo(cvIMG, CV_16UC1);
766 }
767 
768 // ----------------------------------------------------------------------------
769 PlusStatus vtkPlusAndorVideoSource::StartBLIFrameAcquisition(int binning, int vsSpeedIndex, int hsSpeed, float exposureTime, int shutterCloseTime /* =0 */, int shutterOpenTime /* =0 */)
770 {
771  if (this->IsAcquisitionThreadRunning())
772  {
773  LOG_ERROR("An acquisition thread is already running!");
774  return PLUS_FAIL;
775  }
776 
777  this->effectiveHBins = binning > 0 ? binning : this->HorizontalBins;
778  this->effectiveVBins = binning > 0 ? binning : this->VerticalBins;
779  this->effectiveVSInd = vsSpeedIndex > -1 ? vsSpeedIndex : this->VSSpeedIndex;
780  this->effectiveHSInd = hsSpeed > -1 ? hsSpeed : this->HSSpeed[1];
781  this->effectiveExpTime = exposureTime > -1 ? exposureTime : this->ExposureTime;
782  this->effectiveShutter = ShutterMode::FullyAuto;
783  this->effectiveShutterOpeningTimeMilliseconds = shutterOpenTime > 0 ? shutterOpenTime : this->ShutterOpeningTimeMilliseconds;
784  this->effectiveShutterClosingTimeMilliseconds = shutterCloseTime > 0 ? shutterCloseTime : this->ShutterClosingTimeMilliseconds;
785 
786  this->threadID = this->Threader->SpawnThread((vtkThreadFunctionType)&vtkPlusAndorVideoSource::AcquireBLIFrameThread, this);
787  return PLUS_SUCCESS;
788 }
789 
790 // ----------------------------------------------------------------------------
791 void* vtkPlusAndorVideoSource::AcquireBLIFrameThread(vtkMultiThreader::ThreadInfo* info)
792 {
793  vtkPlusAndorVideoSource* device = static_cast<vtkPlusAndorVideoSource*>(info->UserData);
794 
795  device->WaitForCooldown();
796  if (device->AcquireFrame() == PLUS_FAIL)
797  {
798  int tempID = device->threadID;
799  device->threadID = -1;
800  device->Threader->TerminateThread(tempID);
801  return NULL;
802  }
803  ++device->FrameNumber;
804  device->AddFrameToDataSource(device->BLIRaw);
805 
806  if(device->UseFrameCorrections)
807  {
808  device->ApplyFrameCorrections(device->effectiveHBins);
809  device->AddFrameToDataSource(device->BLICorrected);
810  }
811 
812  int tempID = device->threadID;
813  device->threadID = -1;
814  device->Threader->TerminateThread(tempID);
815  return NULL;
816 }
817 
818 // ----------------------------------------------------------------------------
819 PlusStatus vtkPlusAndorVideoSource::StartGrayscaleFrameAcquisition(int binning, int vsSpeedIndex, int hsSpeed, float exposureTime, int shutterCloseTime /* =0 */, int shutterOpenTime /* =0 */)
820 {
821  if (this->IsAcquisitionThreadRunning())
822  {
823  LOG_ERROR("An acquisition thread is already running!");
824  return PLUS_FAIL;
825  }
826 
827  this->effectiveHBins = binning > 0 ? binning : this->HorizontalBins;
828  this->effectiveVBins = binning > 0 ? binning : this->VerticalBins;
829  this->effectiveVSInd = vsSpeedIndex > -1 ? vsSpeedIndex : this->VSSpeedIndex;
830  this->effectiveHSInd = hsSpeed > -1 ? hsSpeed : this->HSSpeed[1];
831  this->effectiveExpTime = exposureTime > -1 ? exposureTime : this->ExposureTime;
832  this->effectiveShutter = ShutterMode::FullyAuto;
833  this->effectiveShutterOpeningTimeMilliseconds = shutterOpenTime > 0 ? shutterOpenTime : this->ShutterOpeningTimeMilliseconds;
834  this->effectiveShutterClosingTimeMilliseconds = shutterCloseTime > 0 ? shutterCloseTime : this->ShutterClosingTimeMilliseconds;
835 
836  this->threadID = this->Threader->SpawnThread((vtkThreadFunctionType)&vtkPlusAndorVideoSource::AcquireGrayscaleFrameThread, this);
837  return PLUS_SUCCESS;
838 
839 }
840 
841 // ----------------------------------------------------------------------------
842 void* vtkPlusAndorVideoSource::AcquireGrayscaleFrameThread(vtkMultiThreader::ThreadInfo* info)
843 {
844  vtkPlusAndorVideoSource* device = static_cast<vtkPlusAndorVideoSource*>(info->UserData);
845 
846  device->WaitForCooldown();
847  if (device->AcquireFrame() == PLUS_FAIL)
848  {
849  int tempID = device->threadID;
850  device->threadID = -1;
851  device->Threader->TerminateThread(tempID);
852  return NULL;
853  }
854  ++device->FrameNumber;
855  device->AddFrameToDataSource(device->GrayRaw);
856 
857  if(device->UseFrameCorrections)
858  {
859  device->ApplyFrameCorrections(device->effectiveHBins);
860  device->AddFrameToDataSource(device->GrayCorrected);
861  }
862 
863  int tempID = device->threadID;
864  device->threadID = -1;
865  device->Threader->TerminateThread(tempID);
866  return NULL;
867 }
868 
869 // ----------------------------------------------------------------------------
870 PlusStatus vtkPlusAndorVideoSource::StartCorrectionFrameAcquisition(const std::string correctionFilePath, ShutterMode shutter, int binning, int vsSpeedIndex, int hsSpeed, float exposureTime, int shutterCloseTime /* =0 */, int shutterOpenTime /* =0 */)
871 {
872  if (this->threadID > -1)
873  {
874  LOG_ERROR("An acquisition thread is already running!");
875  return PLUS_FAIL;
876  }
877 
878  this->effectiveHBins = binning > 0 ? binning : this->HorizontalBins;
879  this->effectiveVBins = binning > 0 ? binning : this->VerticalBins;
880  this->effectiveVSInd = vsSpeedIndex > -1 ? vsSpeedIndex : this->VSSpeedIndex;
881  this->effectiveHSInd = hsSpeed > -1 ? hsSpeed : this->HSSpeed[1];
882  this->effectiveExpTime = exposureTime > -1 ? exposureTime : this->ExposureTime;
883  this->effectiveShutter = shutter;
884  this->effectiveShutterOpeningTimeMilliseconds = shutterOpenTime > 0 ? shutterOpenTime : this->ShutterOpeningTimeMilliseconds;
885  this->effectiveShutterClosingTimeMilliseconds = shutterCloseTime > 0 ? shutterCloseTime : this->ShutterClosingTimeMilliseconds;
886  this->saveCorrectionPath = correctionFilePath;
887 
888  this->threadID = this->Threader->SpawnThread((vtkThreadFunctionType)&vtkPlusAndorVideoSource::AcquireCorrectionFrameThread, this);
889  return PLUS_SUCCESS;
890 }
891 
892 // ----------------------------------------------------------------------------
893 void* vtkPlusAndorVideoSource::AcquireCorrectionFrameThread(vtkMultiThreader::ThreadInfo* info)
894 {
895  vtkPlusAndorVideoSource* device = static_cast<vtkPlusAndorVideoSource*>(info->UserData);
896 
897  if (device->AcquireFrame() == PLUS_FAIL)
898  {
899  int tempID = device->threadID;
900  device->threadID = -1;
901  device->Threader->TerminateThread(tempID);
902  return NULL;
903  }
904  ++device->FrameNumber;
905 
906  cv::Mat cvIMG(device->frameSize[0], device->frameSize[1], CV_16UC1, &(device->rawFrame[0])); // uses rawFrame as buffer
907  if(device->UseFrameCorrections)
908  {
909  device->CorrectBadPixels(device->effectiveHBins, cvIMG);
910  LOG_INFO("Applied bad pixel correction");
911  }
912 
913  cv::imwrite(device->saveCorrectionPath, cvIMG);
914  int tempID = device->threadID;
915  device->threadID = -1;
916  device->Threader->TerminateThread(tempID);
917  return NULL;
918 }
919 
920 //-----------------------------------------------------------------------------
922 {
923  checkStatus(::CancelWait(), "CancelWait");
924  unsigned result = checkStatus(::AbortAcquisition(), "AbortAcquisition");
925  if ((result != DRV_SUCCESS) && (result != DRV_IDLE))
926  {
927  LOG_ERROR("Unable to abort acquisition.");
928  return PLUS_FAIL;
929  }
930  return PLUS_SUCCESS;
931 }
932 
933 //-----------------------------------------------------------------------------
935 {
936  int x, y;
937  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
938  try
939  {
940  cellsToCorrect.clear();
941  cvBadPixelImage = cv::imread(badPixelFilePath, cv::IMREAD_GRAYSCALE);
942  if (cvBadPixelImage.empty())
943  {
944  throw "Bad pixel image empty!";
945  }
946  if (cvBadPixelImage.cols != x || cvBadPixelImage.rows != y)
947  {
948  LOG_ERROR("BadPixelCorrectionImage size " << cvBadPixelImage.size()
949  << " does not match the detector size " << x << " x " << y);
950  }
951  }
952  catch (...)
953  {
954  LOG_ERROR("Could not load bad pixel image from file: " << badPixelFilePath);
955  return PLUS_FAIL;
956  }
957  return PLUS_SUCCESS;
958 }
959 
960 // ----------------------------------------------------------------------------
962 {
963  try
964  {
965  cvBiasDarkCorrection = cv::imread(biasDarkFilePath, cv::IMREAD_UNCHANGED);
966  if(cvBiasDarkCorrection.empty())
967  {
968  throw "Bias+dark correction image empty!";
969  }
970  if (cvBiasDarkCorrection.cols != frameSize[0] || cvBiasDarkCorrection.rows != frameSize[1])
971  {
972  LOG_INFO("BiasDarkCorrectionImage size " << cvBiasDarkCorrection.size()
973  << " does not match the current frame size " << frameSize[0] << " x " << frameSize[1]);
974  return PLUS_FAIL;
975  }
976  }
977  catch(...)
978  {
979  LOG_ERROR("Could not load bias+dark correction image from file: " << biasDarkFilePath);
980  return PLUS_FAIL;
981  }
982  return PLUS_SUCCESS;
983 }
984 
985 // ----------------------------------------------------------------------------
987 {
988  int x, y;
989  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
990  try
991  {
992  cvResizedFlatCorrection.clear();
993  cvFlatCorrection = cv::imread(flatFilePath, cv::IMREAD_UNCHANGED);
994  if(cvFlatCorrection.empty())
995  {
996  throw "Flat correction image empty!";
997  }
998  if (cvFlatCorrection.cols != x || cvFlatCorrection.rows != y)
999  {
1000  LOG_ERROR("FlatCorrectionImage size " << cvFlatCorrection.size()
1001  << " does not match the detector size " << x << " x " << y);
1002  return PLUS_FAIL;
1003  }
1004  double maxVal = 0.0;
1005  cv::minMaxLoc(cvFlatCorrection, nullptr, &maxVal);
1006  if(maxVal > 1.0) // we need to normalize the image to [0.0, 1.0] range
1007  {
1008  cv::Mat temp;
1009  cvFlatCorrection.convertTo(temp, CV_32FC1, 1.0 / maxVal);
1010  cvFlatCorrection = temp;
1011  }
1012  }
1013  catch(...)
1014  {
1015  LOG_ERROR("Could not load flat correction image from file: " << flatFilePath);
1016  return PLUS_FAIL;
1017  }
1018  return PLUS_SUCCESS;
1019 }
1020 
1021 // ----------------------------------------------------------------------------
1023 {
1024  int x, y;
1025  cv::Mat resizedImage;
1026  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
1027  cv::resize(cvFlatCorrection, resizedImage, cv::Size(x / binning, y / binning) , 0, 0, cv::INTER_CUBIC);
1028  cvResizedFlatCorrection[binning] = resizedImage;
1029 }
1030 
1031 // Setup the Andor camera parameters ----------------------------------------------
1032 
1033 // ----------------------------------------------------------------------------
1035 {
1036  this->Shutter = shutter;
1038  return PLUS_SUCCESS;
1039 }
1040 
1041 // ----------------------------------------------------------------------------
1043 {
1044  unsigned status = checkStatus(::SetShutter(1, this->Shutter, closingTime, this->ShutterOpeningTimeMilliseconds), "SetShutter");
1045  if (status == DRV_SUCCESS)
1046  {
1047  this->ShutterClosingTimeMilliseconds = closingTime;
1048  return PLUS_SUCCESS;
1049  }
1050  LOG_ERROR("SetShutter failed with the given closing time.");
1051  return PLUS_FAIL;
1052 }
1053 
1054 // ----------------------------------------------------------------------------
1056 {
1057  unsigned status = checkStatus(::SetShutter(1, this->Shutter, this->ShutterClosingTimeMilliseconds, openingTime), "SetShutter");
1058  if (status == DRV_SUCCESS)
1059  {
1060  this->ShutterOpeningTimeMilliseconds = openingTime;
1061  return PLUS_SUCCESS;
1062  }
1063  LOG_ERROR("SetShutter failed with the given opening time.");
1064  return PLUS_FAIL;
1065 }
1066 
1067 // ----------------------------------------------------------------------------
1069 {
1070  return this->Shutter;
1071 }
1072 
1073 // ----------------------------------------------------------------------------
1075 {
1076  this->ExposureTime = exposureTime;
1077  checkStatus(::SetExposureTime(this->ExposureTime), "SetExposureTime");
1078  return PLUS_SUCCESS;
1079 }
1080 
1081 // ----------------------------------------------------------------------------
1083 {
1084  return this->ExposureTime;
1085 }
1086 
1087 // ----------------------------------------------------------------------------
1089 {
1090  int x, y;
1091  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
1092  unsigned status = checkStatus(::SetImage(bins, this->VerticalBins, 1, x, 1, y), "SetImage");
1093  if(status != DRV_SUCCESS)
1094  {
1095  LOG_ERROR("SetImage failed while changing horizontal bins.");
1096  return PLUS_FAIL;
1097  }
1098  this->HorizontalBins = bins;
1099  return PLUS_SUCCESS;
1100 }
1101 
1102 // ----------------------------------------------------------------------------
1104 {
1105  int x, y;
1106  checkStatus(GetDetector(&x, &y), "GetDetector"); // full sensor size
1107  unsigned status = checkStatus(::SetImage(this->HorizontalBins, bins, 1, x, 1, y), "SetImage");
1108  if(status != DRV_SUCCESS)
1109  {
1110  LOG_ERROR("SetImage failed while changing vertical bins.");
1111  return PLUS_FAIL;
1112  }
1113  this->VerticalBins = bins;
1114  return PLUS_SUCCESS;
1115 }
1116 
1117 // ----------------------------------------------------------------------------
1119 {
1120  unsigned status = checkStatus(::SetHSSpeed(type, index), "SetHSSpeed");
1121  if(status != DRV_SUCCESS)
1122  {
1123  LOG_ERROR("SetHSSpeed command failed.");
1124  return PLUS_FAIL;
1125  }
1126  HSSpeed[0] = type;
1127  HSSpeed[1] = index;
1128  return PLUS_SUCCESS;
1129 }
1130 
1131 // ----------------------------------------------------------------------------
1133 {
1134  float speed;
1135  unsigned status = checkStatus(::GetHSSpeed(0, HSSpeed[0], HSSpeed[1], &speed), "GetHSSpeed");
1136  if(status != DRV_SUCCESS)
1137  {
1138  LOG_ERROR("GetHSSpeed command failed.");
1139  }
1140  return speed;
1141 }
1142 
1143 // ----------------------------------------------------------------------------
1145 {
1146  unsigned status = checkStatus(::SetVSSpeed(index), "SetVSSpeed");
1147  if(status != DRV_SUCCESS)
1148  {
1149  LOG_ERROR("SetVSSpeed command failed.");
1150  return PLUS_FAIL;
1151  }
1152  this->VSSpeedIndex = index;
1153  return PLUS_SUCCESS;
1154 }
1155 
1156 // ----------------------------------------------------------------------------
1158 {
1159  return this->VSSpeedIndex;
1160 }
1161 
1162 // ----------------------------------------------------------------------------
1164 {
1165  float speed;
1166  unsigned status = checkStatus(::GetVSSpeed(this->VSSpeedIndex, &speed), "GetVSSpeed");
1167  if(status != DRV_SUCCESS)
1168  {
1169  LOG_ERROR("GetVSSpeed command failed.");
1170  }
1171  return speed;
1172 }
1173 
1174 // ----------------------------------------------------------------------------
1176 {
1177  this->PreAmpGainIndex = PreAmpGainIndex;
1178  unsigned status = checkStatus(::SetPreAmpGain(this->PreAmpGainIndex), "SetPreAmpGain");
1179  if(status == DRV_P1INVALID)
1180  {
1181  LOG_ERROR("Index out of range.");
1182  return PLUS_FAIL;
1183  }
1184  return PLUS_SUCCESS;
1185 }
1186 
1187 // ----------------------------------------------------------------------------
1189 {
1190  return this->PreAmpGainIndex;
1191 }
1192 
1193 // ----------------------------------------------------------------------------
1195 {
1196  float gain;
1197  unsigned status = checkStatus(::GetPreAmpGain(this->PreAmpGainIndex, &gain), "GetPreAmpGain");
1198  if(status != DRV_SUCCESS)
1199  {
1200  LOG_ERROR("GetPreAmpGain command failed.");
1201  }
1202  return gain;
1203 }
1204 
1205 // ----------------------------------------------------------------------------
1207 {
1208  this->m_AcquisitionMode = acquisitionMode;
1209  checkStatus(::SetAcquisitionMode(this->m_AcquisitionMode), "SetAcquisitionMode");
1210  return PLUS_SUCCESS;
1211 }
1212 
1213 // ----------------------------------------------------------------------------
1215 {
1216  return this->m_AcquisitionMode;
1217 }
1218 
1219 // ----------------------------------------------------------------------------
1221 {
1222  this->m_ReadMode = readMode;
1223  checkStatus(::SetReadMode(this->m_ReadMode), "SetReadMode");
1224  return PLUS_SUCCESS;
1225 }
1226 
1227 // ----------------------------------------------------------------------------
1229 {
1230  return this->m_ReadMode;
1231 }
1232 
1233 // ----------------------------------------------------------------------------
1235 {
1236  this->m_TriggerMode = triggerMode;
1237  checkStatus(::SetTriggerMode(this->m_TriggerMode), "SetTriggerMode");
1238  return PLUS_SUCCESS;
1239 }
1240 
1241 // ----------------------------------------------------------------------------
1243 {
1244  return this->m_TriggerMode;
1245 }
1246 
1247 // ----------------------------------------------------------------------------
1249 {
1250  this->UseFrameCorrections = useFrameCorrections;
1251  return PLUS_SUCCESS;
1252 }
1253 
1254 // ----------------------------------------------------------------------------
1256 {
1257  return this->UseFrameCorrections;
1258 }
1259 
1260 // ----------------------------------------------------------------------------
1262 {
1263  this->UseCosmicRayCorrection = useCosmicRayCorrection;
1264  return PLUS_SUCCESS;
1265 }
1266 
1267 // ----------------------------------------------------------------------------
1269 {
1270  return this->UseCosmicRayCorrection;
1271 }
1272 
1273 // ----------------------------------------------------------------------------
1275 {
1276  std::copy(std::begin(intrinsics), std::end(intrinsics), this->cameraIntrinsics);
1277  return PLUS_SUCCESS;
1278 }
1279 
1280 // ----------------------------------------------------------------------------
1282 {
1283  std::array<double, 9> returnIntrinsics;
1284  std::copy(this->cameraIntrinsics, this->cameraIntrinsics + 9, std::begin(returnIntrinsics));
1285  return returnIntrinsics;
1286 }
1287 
1288 // ----------------------------------------------------------------------------
1290 {
1291  std::copy(std::begin(coefficients), std::end(coefficients), this->distortionCoefficients);
1292  return PLUS_SUCCESS;
1293 }
1294 
1295 // ----------------------------------------------------------------------------
1297 {
1298  std::array<double, 4> returnCoefficients;
1299  std::copy(this->distortionCoefficients, this->distortionCoefficients + 4, std::begin(returnCoefficients));
1300  return returnCoefficients;
1301 }
1302 
1303 // ----------------------------------------------------------------------------
1305 {
1306  this->RequireCoolTemp = requireCoolTemp;
1307  return PLUS_SUCCESS;
1308 }
1309 
1310 // ----------------------------------------------------------------------------
1312 {
1313  return this->RequireCoolTemp;
1314 }
1315 
1316 // ----------------------------------------------------------------------------
1318 {
1320  {
1322  }
1323  return PLUS_SUCCESS;
1324 }
1325 
1326 // ----------------------------------------------------------------------------
1328 {
1329  int coolerStatus = IsCoolerOn();
1330  if(coolerState) // Wanting to turn cooler on
1331  {
1332  if (coolerStatus == 0)
1333  {
1334  TurnCoolerON();
1335  }
1336  else
1337  {
1338  LOG_INFO("Cooler is already on. Setting temperature to configured " << this->CoolTemperature << " °C.");
1339  checkStatus(SetTemperature(this->CoolTemperature), "SetTemperature");
1340  }
1341  }
1342  else if(coolerState == false && coolerStatus == 1) // Wanting to turn cooler off and cooler is currently on
1343  {
1344  TurnCoolerOFF();
1345  }
1346 
1347  return PLUS_SUCCESS;
1348 }
1349 
1350 // ----------------------------------------------------------------------------
1352 {
1353  int coolerStatus = 1;
1354  unsigned result = checkStatus(::IsCoolerOn(&coolerStatus), "IsCoolerOn");
1355  if(result == DRV_SUCCESS)
1356  {
1357  return bool(coolerStatus);
1358  }
1359  return -1;
1360 }
1361 
1362 // ----------------------------------------------------------------------------
1364 {
1365  unsigned result = checkStatus(CoolerON(), "CoolerON");
1366  if(result == DRV_SUCCESS)
1367  {
1368  LOG_INFO("Temperature controller switched ON.");
1369  checkStatus(SetTemperature(this->CoolTemperature), "SetTemperature");
1370  return PLUS_SUCCESS;
1371  }
1372  LOG_ERROR("CoolerON command failed to execute.");
1373  return PLUS_FAIL;
1374 }
1375 
1376 // ----------------------------------------------------------------------------
1378 {
1379  unsigned result = checkStatus(CoolerOFF(), "CoolerOFF");
1380  if(result == DRV_SUCCESS)
1381  {
1382  LOG_INFO("Temperature controller switched OFF.");
1383  return PLUS_SUCCESS;
1384  }
1385  LOG_ERROR("CoolerOFF command failed to execute.");
1386  return PLUS_FAIL;
1387 }
1388 
1389 // ----------------------------------------------------------------------------
1391 {
1392  unsigned result = checkStatus(::SetCoolerMode(mode), "SetCoolerMode");
1393  if(result == DRV_SUCCESS)
1394  {
1395  this->CoolerMode = mode;
1396  if(mode == 1)
1397  {
1398  LOG_INFO("Cooler mode set to 1. If the cooler is ON, temperature will be maintained on Shutdown, otherwise Camera will return to ambient temperature.");
1399  }
1400  else
1401  {
1402  LOG_INFO("Cooler mode set to 0. Camera will return to ambient temperature on ShutDown.");
1403  }
1404  return PLUS_SUCCESS;
1405  }
1406  LOG_ERROR("SetCoolerMode command failed to execute.");
1407  return PLUS_FAIL;
1408 }
1409 
1410 // ----------------------------------------------------------------------------
1412 {
1413  return this->CoolerMode;
1414 }
1415 
1416 // ----------------------------------------------------------------------------
1418 {
1419  unsigned result = checkStatus(SetTemperature(coolTemp), "SetTemperature");
1420  if(result != DRV_SUCCESS)
1421  {
1422  LOG_ERROR("SetCoolTemperature command failed.");
1423  return PLUS_FAIL;
1424  }
1425  this->CoolTemperature = coolTemp;
1426 
1427  return PLUS_SUCCESS;
1428 }
1429 
1430 // ----------------------------------------------------------------------------
1432 {
1433  return this->CoolTemperature;
1434 }
1435 
1436 // ----------------------------------------------------------------------------
1438 {
1439  this->SafeTemperature = safeTemp;
1440 
1441  return PLUS_SUCCESS;
1442 }
1443 
1444 // ----------------------------------------------------------------------------
1446 {
1447  return this->SafeTemperature;
1448 }
1449 
1450 // ----------------------------------------------------------------------------
1452 {
1453  int status;
1454  GetStatus(&status);
1455 
1456  return status;
1457 }
1458 
1459 // ----------------------------------------------------------------------------
1461 {
1462  int status = GetCCDStatus();
1463  return status == DRV_ACQUIRING;
1464 }
1465 
1467 {
1468  checkStatus(::PrepareAcquisition(), "PrepareAcquisition");
1469 }
1470 
1471 bool vtkPlusAndorVideoSource::WaitForAcquisitionWithTimeout(double maximumWaitTimeInSeconds, int sleepQuantumMilliseconds)
1472 {
1473  if (this->threadID > -1)
1474  {
1475  double startOfWait = vtkIGSIOAccurateTimer::GetSystemTime();
1476  double now = 0.0;
1477  do
1478  {
1479  igtl::Sleep(sleepQuantumMilliseconds);
1480  now = vtkIGSIOAccurateTimer::GetSystemTime();
1481  }
1482  while (now < startOfWait + maximumWaitTimeInSeconds && Threader->IsThreadActive(this->threadID));
1483  }
1484  return !Threader->IsThreadActive(this->threadID);
1485 }
1486 
1487 // ----------------------------------------------------------------------------
1489 {
1490  return this->threadID > -1;
1491 }
1492 
1493 // ----------------------------------------------------------------------------
1494 unsigned int vtkPlusAndorVideoSource::checkStatus(unsigned int returnStatus, std::string functionName)
1495 {
1496  if(returnStatus == DRV_SUCCESS)
1497  {
1498  return returnStatus;
1499  }
1500  else if(returnStatus == DRV_NOT_INITIALIZED)
1501  {
1502  LOG_ERROR("Failed AndorSDK operation: " << functionName
1503  << "; Driver is not initialized.");
1504  }
1505  else if(returnStatus == DRV_ACQUIRING)
1506  {
1507  LOG_ERROR("Failed AndorSDK operation: " << functionName
1508  << "; Not allowed. Currently acquiring data.");
1509  }
1510  else if(returnStatus == DRV_P1INVALID)
1511  {
1512  LOG_ERROR("Failed AndorSDK operation: " << functionName
1513  << "; Parameter 1 not valid.");
1514  }
1515  else if(returnStatus == DRV_P2INVALID)
1516  {
1517  LOG_ERROR("Failed AndorSDK operation: " << functionName
1518  << "; Parameter 2 not valid.");
1519  }
1520  else if(returnStatus == DRV_P3INVALID)
1521  {
1522  LOG_ERROR("Failed AndorSDK operation: " << functionName
1523  << "; Parameter 3 not valid.");
1524  }
1525  else if(returnStatus == DRV_P4INVALID)
1526  {
1527  LOG_ERROR("Failed AndorSDK operation: " << functionName
1528  << "; Parameter 4 not valid.");
1529  }
1530  else if(returnStatus == DRV_P5INVALID)
1531  {
1532  LOG_ERROR("Failed AndorSDK operation: " << functionName
1533  << "; Parameter 5 not valid.");
1534  }
1535  else if(returnStatus == DRV_P6INVALID)
1536  {
1537  LOG_ERROR("Failed AndorSDK operation: " << functionName
1538  << "; Parameter 6 not valid.");
1539  }
1540  else if(returnStatus == DRV_P7INVALID)
1541  {
1542  LOG_ERROR("Failed AndorSDK operation: " << functionName
1543  << "; Parameter 7 not valid.");
1544  }
1545  else if(returnStatus == DRV_ERROR_ACK)
1546  {
1547  LOG_ERROR("Failed AndorSDK operation: " << functionName
1548  << "; Unable to communicate with card.");
1549  }
1550  else if(returnStatus == DRV_TEMP_OFF)
1551  {
1552  LOG_INFO("Cooler is OFF. Current temperature is " << this->CurrentTemperature << " °C");
1553  }
1554  else if(returnStatus == DRV_TEMPERATURE_STABILIZED)
1555  {
1556  LOG_INFO("Temperature has stabilized at " << this->CurrentTemperature << " °C");
1557  }
1558  else if(returnStatus == DRV_TEMPERATURE_NOT_REACHED)
1559  {
1560  LOG_INFO("Cooling down, current temperature is " << this->CurrentTemperature << " °C");
1561  }
1562  else if(returnStatus == DRV_TEMP_DRIFT)
1563  {
1564  LOG_INFO("Temperature had stabilised but has since drifted. Current temperature is " << this->CurrentTemperature << " °C");
1565  }
1566  else if(returnStatus == DRV_TEMP_NOT_STABILIZED)
1567  {
1568  LOG_INFO("Temperature reached but not stabilized. Current temperature is " << this->CurrentTemperature << " °C");
1569  }
1570  else if(returnStatus == DRV_VXDNOTINSTALLED)
1571  {
1572  LOG_ERROR("Failed AndorSDK operation: " << functionName
1573  << "; VxD not loaded.");
1574  }
1575  else if(returnStatus == DRV_INIERROR)
1576  {
1577  LOG_ERROR("Failed AndorSDK operation: " << functionName
1578  << "; Unable to load DETECTOR.INI.");
1579  }
1580  else if(returnStatus == DRV_COFERROR)
1581  {
1582  LOG_ERROR("Failed AndorSDK operation: " << functionName
1583  << "; Unable to load *.COF.");
1584  }
1585  else if(returnStatus == DRV_FLEXERROR)
1586  {
1587  LOG_ERROR("Failed AndorSDK operation: " << functionName
1588  << "; Unable to load *.RBF.");
1589  }
1590  else if(returnStatus == DRV_ERROR_FILELOAD)
1591  {
1592  LOG_ERROR("Failed AndorSDK operation: " << functionName
1593  << "; Unable to load *.COF or *.RBF files.");
1594  }
1595  else if(returnStatus == DRV_USBERROR)
1596  {
1597  LOG_ERROR("Failed AndorSDK operation: " << functionName
1598  << "; Unable to detect USB device or not USB 2.0.");
1599  }
1600  else if(returnStatus == DRV_ERROR_NOCAMERA)
1601  {
1602  LOG_ERROR("Failed AndorSDK operation: " << functionName
1603  << "; No camera found.");
1604  }
1605  else if(returnStatus == DRV_GENERAL_ERRORS)
1606  {
1607  LOG_ERROR("Failed AndorSDK operation: " << functionName
1608  << "; An error occured while obtaining the number of available cameras.");
1609  }
1610  else if(returnStatus == DRV_INVALID_MODE)
1611  {
1612  LOG_ERROR("Failed AndorSDK operation: " << functionName
1613  << "; Invalid mode or mode not available.");
1614  }
1615  else if(returnStatus == DRV_ERROR_PAGELOCK)
1616  {
1617  LOG_ERROR("Failed AndorSDK operation: " << functionName
1618  << "; Unable to allocate memory.");
1619  }
1620  else if(returnStatus == DRV_INVALID_FILTER)
1621  {
1622  LOG_ERROR("Failed AndorSDK operation: " << functionName
1623  << "; Filter not available for current acquisition.");
1624  }
1625  else if(returnStatus == DRV_BINNING_ERROR)
1626  {
1627  LOG_ERROR("Failed AndorSDK operation: " << functionName
1628  << "; Range not a multiple of horizontal binning.");
1629  }
1630  else if(returnStatus == DRV_SPOOLSETUPERROR)
1631  {
1632  LOG_ERROR("Failed AndorSDK operation: " << functionName
1633  << "; Error with spool settings.");
1634  }
1635  else if(returnStatus == DRV_IDLE)
1636  {
1637  LOG_ERROR("Failed AndorSDK operation: " << functionName
1638  << "; The system is not currently acquiring.");
1639  }
1640  else if(returnStatus == DRV_NO_NEW_DATA)
1641  {
1642  LOG_ERROR("Failed AndorSDK operation: " << functionName
1643  << "; There is no new data yet.");
1644  }
1645  else if(returnStatus == DRV_ERROR_CODES)
1646  {
1647  LOG_ERROR("Failed AndorSDK operation: " << functionName
1648  << "; Problem communicating with camera.");
1649  }
1650  else if(returnStatus == DRV_LOAD_FIRMWARE_ERROR)
1651  {
1652  LOG_ERROR("Failed AndorSDK operation: " << functionName
1653  << "; Error loading firmware.");
1654  }
1655  else if(returnStatus == DRV_NOT_SUPPORTED)
1656  {
1657  LOG_ERROR("Failed AndorSDK operation: " << functionName
1658  << "; Feature not supported.");
1659  }
1660  else if(returnStatus == DRV_RANDOM_TRACK_ERROR)
1661  {
1662  LOG_ERROR("Failed AndorSDK operation: " << functionName
1663  << "; Invalid combination of tracks.");
1664  }
1665  else
1666  {
1667  LOG_WARNING("Possible failed AndorSDK operation: " << functionName
1668  << "; Unknown return code " << returnStatus << "returned.");
1669  }
1670 
1671  return returnStatus;
1672 }
std::vector< uint16_t > rawFrame
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
PlusStatus InternalStopRecording() override
virtual PlusStatus InternalConnect()
Class for acquiring images from Andor cameras.
vtkPlusAndorVideoSource::ShutterMode effectiveShutter
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
Phidget_MeshMode mode
Definition: phidget22.h:1332
vtkPlusAndorVideoSource::ShutterMode Shutter
PlusStatus SetHorizontalBins(int bins)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
PlusStatus SetAcquisitionMode(AcquisitionMode acquisitionMode)
void AdjustSpacing(int horizontalBins, int verticalBins)
static void * AcquireGrayscaleFrameThread(vtkMultiThreader::ThreadInfo *info)
PlusStatus SetDistortionCoefficients(std::array< double, 4 > coefficients)
std::array< float, 16 > imageToReferenceTransform
void ResizeFlatCorrectionImage(int binning)
bool RequirePortNameInDeviceSetConfiguration
std::string to_string(ClariusAvailability avail)
cv::Mat cvFlatCorrection
PlusStatus SetExposureTime(float exposureTime)
for i
double AcquisitionRate
static void * AcquireBLIFrameThread(vtkMultiThreader::ThreadInfo *info)
void ApplyCosmicRayCorrection(int binning, cv::Mat &floatImage)
PlusStatus SetShutter(ShutterMode shutter)
int GetCurrentTemperature(float *temperature)
void AddFrameToDataSource(DataSourceArray &ds)
vtkStandardNewMacro(vtkPlusAndorVideoSource)
#define PLUS_FAIL
Definition: PlusCommon.h:43
PlusStatus SetBiasDarkCorrectionImage(const std::string biasDarkFilePath)
PlusStatus GetVideoSourcesByPortName(const char *aPortName, std::vector< vtkPlusDataSource * > &sources)
unsigned int checkStatus(unsigned int returnStatus, std::string functionName)
int port
Definition: phidget22.h:2454
virtual std::string GetSdkVersion()
PlusStatus SetInitializeCoolerState(bool InitializeCoolerState)
PlusStatus SetUseCosmicRayCorrection(bool UseCosmicRayCorrection)
PlusStatus StartGrayscaleFrameAcquisition(int binning, int vsSpeed, int hsSpeed, float exposureTime, int shutterCloseTime=0, int shutterOpenTime=0)
virtual PlusStatus Disconnect()
void AdjustBuffers(int horizontalBins, int verticalBins)
std::vector< double > GetSpacing()
cv::Mat cvCameraIntrinsics
PlusStatus GetFirstActiveOutputVideoSource(vtkPlusDataSource *&aVideoSource)
void CorrectBadPixels(int binning, cv::Mat &cvIMG)
std::map< int, CellIndices > cellsToCorrect
PlusStatus SetTriggerMode(TriggerMode triggerMode)
unsigned long FrameNumber
PlusStatus SetCameraIntrinsics(std::array< double, 9 > intrinsics)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PlusStatus SetVerticalBins(int bins)
PlusStatus SetSafeTemperature(int safeTemp)
PlusStatus SetFrameFieldImageToReferenceTransform(std::array< float, 16 > transform)
cv::Mat resizedFlatImage
cv::Mat cvDistortionCoefficients
std::map< int, resizedFlatImage > cvResizedFlatCorrection
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
PlusStatus SetVSSpeedIndex(int index)
std::array< double, 9 > GetCameraIntrinsics()
std::array< double, 4 > GetDistortionCoefficients()
PlusStatus SetRequireCoolTemp(bool RequireCoolTemp)
std::vector< uint > CellIndices
void InitializePort(DataSourceArray &port)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
int x
Definition: phidget22.h:4265
bool WaitForAcquisitionWithTimeout(double maximumWaitTimeInSeconds, int sleepQuantumMilliseconds=10)
PlusStatus SetShutterOpeningTimeMilliseconds(int openingTime)
static void * AcquireCorrectionFrameThread(vtkMultiThreader::ThreadInfo *info)
virtual PlusStatus NotifyConfigured()
virtual bool IsRecording() const
bool StartThreadForInternalUpdates
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
virtual PlusStatus InternalDisconnect()
std::vector< vtkPlusDataSource * > DataSourceArray
PlusStatus InternalStartRecording() override
cv::Mat cvBiasDarkCorrection
PlusStatus SetBadPixelCorrectionImage(const std::string badPixelFilePath)
ChannelContainer OutputChannels
cv::Mat cvBadPixelImage
PlusStatus SetPreAmpGainIndex(int preAmpGainIndex)
Direction vectors of rods y
Definition: algo3.m:15
PlusStatus SetHSSpeed(int type, int index)
PlusStatus SetShutterClosingTimeMilliseconds(int closingTime)
PlusStatus SetCoolTemperature(int coolTemp)
PlusStatus StartBLIFrameAcquisition(int binning, int vsSpeed, int hsSpeed, float exposureTime, int shutterCloseTime=0, int shutterOpenTime=0)
PlusStatus SetCoolerState(bool coolerState)
PlusStatus SetReadMode(ReadMode setReadMode)
PlusStatus StartCorrectionFrameAcquisition(std::string correctionFilePath, ShutterMode shutter, int binning, int vsSpeed, int hsSpeed, float exposureTime, int shutterCloseTime=0, int shutterOpenTime=0)
PlusStatus SetFlatCorrectionImage(const std::string flatFilePath)
PlusStatus SetUseFrameCorrections(bool UseFrameCorrections)
double * temperature
Definition: phidget22.h:3821
bool CorrectlyConfigured
Interface to a 3D positioning tool, video source, or generalized data stream.