@@ -18,8 +18,11 @@ vtkStandardNewMacro(vtkPlusAndorVideoSource);
18
18
// put these here so there is no public dependence on OpenCV
19
19
cv::Mat cvCameraIntrinsics;
20
20
cv::Mat cvDistanceCoefficients;
21
+ cv::Mat cvBadPixelImage;
21
22
cv::Mat cvFlatCorrection;
22
23
cv::Mat cvBiasDarkCorrection;
24
+ using CellIndices = std::vector<uint >;
25
+ std::map<int , CellIndices> cellsToCorrect;
23
26
24
27
// ----------------------------------------------------------------------------
25
28
void vtkPlusAndorVideoSource::PrintSelf (ostream& os, vtkIndent indent)
@@ -46,6 +49,7 @@ void vtkPlusAndorVideoSource::PrintSelf(ostream& os, vtkIndent indent)
46
49
os << indent << " UseFrameCorrections: " << UseFrameCorrections << std::endl;
47
50
os << indent << " FlatCorrection: " << flatCorrection << std::endl;
48
51
os << indent << " BiasDarkCorrection: " << biasDarkCorrection << std::endl;
52
+ os << indent << " BadPixelCorrection: " << badPixelCorrection << std::endl;
49
53
}
50
54
51
55
// ----------------------------------------------------------------------------
@@ -129,11 +133,13 @@ PlusStatus vtkPlusAndorVideoSource::ReadConfiguration(vtkXMLDataElement* rootCon
129
133
deviceConfig->GetVectorAttribute (" OutputSpacing" , 3 , OutputSpacing);
130
134
deviceConfig->GetVectorAttribute (" CameraIntrinsics" , 9 , cameraIntrinsics);
131
135
deviceConfig->GetVectorAttribute (" DistanceCoefficients" , 4 , distanceCoefficients);
136
+ badPixelCorrection = deviceConfig->GetAttribute (" BadPixelCorrection" );
132
137
flatCorrection = deviceConfig->GetAttribute (" FlatCorrection" );
133
138
biasDarkCorrection = deviceConfig->GetAttribute (" BiasDarkCorrection" );
134
139
135
140
cvCameraIntrinsics = cv::Mat (3 , 3 , CV_64FC1, cameraIntrinsics);
136
141
cvDistanceCoefficients = cv::Mat (1 , 4 , CV_64FC1, distanceCoefficients);
142
+ this ->SetBadPixelCorrectionImage (badPixelCorrection); // load the image
137
143
this ->SetFlatCorrectionImage (flatCorrection); // load and normalize if needed
138
144
this ->SetBiasDarkCorrectionImage (biasDarkCorrection); // load the image
139
145
@@ -164,6 +170,7 @@ PlusStatus vtkPlusAndorVideoSource::WriteConfiguration(vtkXMLDataElement* rootCo
164
170
deviceConfig->SetVectorAttribute (" DistanceCoefficients" , 4 , distanceCoefficients);
165
171
deviceConfig->SetAttribute (" FlatCorrection" , flatCorrection.c_str ());
166
172
deviceConfig->SetAttribute (" BiasDarkCorrection" , biasDarkCorrection.c_str ());
173
+ deviceConfig->SetAttribute (" BadPixelCorrection" , badPixelCorrection.c_str ());
167
174
168
175
XML_WRITE_BOOL_ATTRIBUTE (UseFrameCorrections, deviceConfig);
169
176
@@ -539,11 +546,101 @@ void vtkPlusAndorVideoSource::AddFrameToDataSource(DataSourceArray& ds)
539
546
}
540
547
}
541
548
549
+ // ----------------------------------------------------------------------------
550
+ void vtkPlusAndorVideoSource::FindBadCells (int binning)
551
+ {
552
+ std::vector<cv::Point > badIndicesXY;
553
+ cv::findNonZero (cvBadPixelImage, badIndicesXY);
554
+
555
+ std::map<uint , int > badPixelCount;
556
+ for (int i = 0 ; i < badIndicesXY.size (); i++)
557
+ {
558
+ uint resolutionCellIndexX = badIndicesXY[i].x / binning;
559
+ uint resolutionCellIndexY = badIndicesXY[i].y / binning;
560
+ uint resolutionCellIndex = frameSize[1 ] * resolutionCellIndexY + resolutionCellIndexX;
561
+ badPixelCount[resolutionCellIndex] += 1 ;
562
+ }
563
+
564
+ std::vector<uint > resolutionCellsToCorrect;
565
+ for (auto const & bpc : badPixelCount)
566
+ {
567
+ if (binning * binning / bpc.second < 5 ) // tolerate up to 20% dead pixels
568
+ {
569
+ resolutionCellsToCorrect.push_back (bpc.first );
570
+ }
571
+ }
572
+
573
+ cellsToCorrect[binning] = resolutionCellsToCorrect;
574
+ }
542
575
543
576
// ----------------------------------------------------------------------------
544
- void vtkPlusAndorVideoSource::ApplyFrameCorrections ()
577
+ void vtkPlusAndorVideoSource::CorrectBadPixels (int binning, cv::Mat& cvIMG)
578
+ {
579
+ if (cellsToCorrect.find (binning) == cellsToCorrect.end ()) // it needs to be calculated
580
+ {
581
+ FindBadCells (binning);
582
+ }
583
+ std::vector<uint > resolutionCellsToCorrect = cellsToCorrect[binning];
584
+ uint resolutionCellIndexX, resolutionCellIndexY;
585
+ std::vector<uint > valuesForMedian, correctedCells;
586
+ uint medianValue;
587
+ int startX, startY;
588
+ unsigned endX, endY;
589
+ int numCellsToCorrect = resolutionCellsToCorrect.size ();
590
+ for (uint cell : resolutionCellsToCorrect)
591
+ {
592
+ resolutionCellIndexX = cell - frameSize[0 ] * (cell / frameSize[0 ]);
593
+ resolutionCellIndexY = cell / frameSize[0 ];
594
+ startX = resolutionCellIndexX - 1 ;
595
+ endX = resolutionCellIndexX + 1 ;
596
+ startY = resolutionCellIndexY - 1 ;
597
+ endY = resolutionCellIndexY + 1 ;
598
+ if (startX < 0 ) { startX = 0 ; }
599
+ if (startY < 0 ) { startY = 0 ; }
600
+ if (endX > frameSize[0 ]) { endX = frameSize[0 ]; }
601
+ if (endY > frameSize[0 ]) { endY = frameSize[0 ]; }
602
+
603
+ for (uint x = startX; x <= endX; x++)
604
+ {
605
+ for (uint y = startY; y <= endY; y++)
606
+ {
607
+ if (std::find (resolutionCellsToCorrect.begin (), resolutionCellsToCorrect.end (), frameSize[0 ] * y + x) != resolutionCellsToCorrect.end ())
608
+ {
609
+ if (std::find (correctedCells.begin (), correctedCells.end (), frameSize[0 ] * y + x) != correctedCells.end ())
610
+ {
611
+ valuesForMedian.push_back (cvIMG.at <ushort >(y, x));
612
+ }
613
+ }
614
+ else
615
+ {
616
+ valuesForMedian.push_back (cvIMG.at <ushort >(y, x));
617
+ }
618
+ }
619
+ }
620
+
621
+ sort (valuesForMedian.begin (), valuesForMedian.end ());
622
+ if (valuesForMedian.size () % 2 == 0 )
623
+ {
624
+ medianValue = (valuesForMedian[valuesForMedian.size () / 2 - 1 ] + valuesForMedian[valuesForMedian.size () / 2 ]) / 2 ;
625
+ }
626
+ else
627
+ {
628
+ medianValue = valuesForMedian[valuesForMedian.size () / 2 ];
629
+ }
630
+
631
+ cvIMG.at <ushort >(resolutionCellIndexY, resolutionCellIndexX) = medianValue;
632
+ correctedCells.push_back (cell);
633
+ valuesForMedian.clear ();
634
+ }
635
+ }
636
+
637
+ // ----------------------------------------------------------------------------
638
+ void vtkPlusAndorVideoSource::ApplyFrameCorrections (int binning)
545
639
{
546
640
cv::Mat cvIMG (frameSize[0 ], frameSize[1 ], CV_16UC1, &rawFrame[0 ]); // uses rawFrame as buffer
641
+ CorrectBadPixels (binning, cvIMG);
642
+ LOG_INFO (" Applied bad pixel correction" );
643
+
547
644
cv::Mat floatImage;
548
645
cvIMG.convertTo (floatImage, CV_32FC1);
549
646
cv::Mat result;
@@ -571,7 +668,7 @@ PlusStatus vtkPlusAndorVideoSource::AcquireBLIFrame(int binning, int vsSpeed, in
571
668
572
669
if (this ->UseFrameCorrections )
573
670
{
574
- ApplyFrameCorrections ();
671
+ ApplyFrameCorrections (binning );
575
672
AddFrameToDataSource (BLICorrected);
576
673
}
577
674
@@ -588,7 +685,7 @@ PlusStatus vtkPlusAndorVideoSource::AcquireGrayscaleFrame(int binning, int vsSpe
588
685
589
686
if (this ->UseFrameCorrections )
590
687
{
591
- ApplyFrameCorrections ();
688
+ ApplyFrameCorrections (binning );
592
689
AddFrameToDataSource (GrayCorrected);
593
690
}
594
691
@@ -600,8 +697,35 @@ PlusStatus vtkPlusAndorVideoSource::AcquireCorrectionFrame(const std::string cor
600
697
{
601
698
AcquireFrame (exposureTime, shutter, binning, vsSpeed, hsSpeed);
602
699
++this ->FrameNumber ;
603
- cv::Mat saveImage (frameSize[0 ], frameSize[1 ], CV_16UC1, &rawFrame[0 ]);
604
- cv::imwrite (correctionFilePath, saveImage);
700
+
701
+ cv::Mat cvIMG (frameSize[0 ], frameSize[1 ], CV_16UC1, &rawFrame[0 ]); // uses rawFrame as buffer
702
+ if (this ->UseFrameCorrections )
703
+ {
704
+ CorrectBadPixels (binning, cvIMG);
705
+ LOG_INFO (" Applied bad pixel correction" );
706
+ }
707
+
708
+ cv::imwrite (correctionFilePath, cvIMG);
709
+ return PLUS_SUCCESS;
710
+ }
711
+
712
+ // -----------------------------------------------------------------------------
713
+ PlusStatus vtkPlusAndorVideoSource::SetBadPixelCorrectionImage (const std::string badPixelFilePath)
714
+ {
715
+ try
716
+ {
717
+ cellsToCorrect.clear ();
718
+ cvBadPixelImage = cv::imread (badPixelFilePath, cv::IMREAD_GRAYSCALE);
719
+ if (cvBadPixelImage.empty ())
720
+ {
721
+ throw " Bad pixel image empty!" ;
722
+ }
723
+ }
724
+ catch (...)
725
+ {
726
+ LOG_ERROR (" Could not load bad pixel image from file: " << badPixelFilePath);
727
+ return PLUS_FAIL;
728
+ }
605
729
return PLUS_SUCCESS;
606
730
}
607
731
0 commit comments