PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
PlusFidSegmentation.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 
9 #include "PlusFidSegmentation.h"
10 #include "vtkMath.h"
11 
12 #include <limits.h>
13 #include <iostream>
14 #include <algorithm>
15 
16 #include "itkRGBPixel.h"
17 #include "itkImage.h"
18 #include "itkImageFileReader.h"
19 #include "itkImageFileWriter.h"
20 #include "itkPNGImageIO.h"
21 
22 static const short BLACK = 0;
23 static const short MIN_WINDOW_DIST = 8;
24 static const short MAX_CLUSTER_VALS = 16384;
25 
29 const int PlusFidSegmentation::DEFAULT_CLIP_ORIGIN[2] = { 27, 27 };
30 const int PlusFidSegmentation::DEFAULT_CLIP_SIZE[2] = { 766, 562 };
41 
42 //-----------------------------------------------------------------------------
43 
45  : m_UseOriginalImageIntensityForDotIntensityScore(false)
46  , m_NumberOfMaximumFiducialPointCandidates(DEFAULT_NUMBER_OF_MAXIMUM_FIDUCIAL_POINT_CANDIDATES)
47  , m_ThresholdImagePercent(-1)
48  , m_MorphologicalOpeningBarSizeMm(-1)
49  , m_MorphologicalOpeningCircleRadiusMm(-1)
50  , m_PossibleFiducialsImageFilename("")
51  , m_FiducialGeometry(CALIBRATION_PHANTOM_6_POINT)
52  , m_ApproximateSpacingMmPerPixel(-1)
53  , m_DotsFound(false)
54  , m_NumDots(-1.0)
55  , m_Working(new PlusFidSegmentation::PixelType[1])
56  , m_Dilated(new PlusFidSegmentation::PixelType[1])
57  , m_Eroded(new PlusFidSegmentation::PixelType[1])
58  , m_UnalteredImage(new PlusFidSegmentation::PixelType[1])
59  , m_DebugOutput(false)
60 {
61  //Initialization of member variables
62  m_FrameSize[0] = 0;
63  m_FrameSize[1] = 0;
64  m_FrameSize[2] = 0;
65 
66  m_RegionOfInterest[0] = 0;
67  m_RegionOfInterest[1] = 0;
68  m_RegionOfInterest[2] = 0;
69  m_RegionOfInterest[3] = 0;
70 
71  for (int i = 0 ; i < 4 ; i++)
72  {
74  }
75 
76  for (int i = 0 ; i < 3 ; i++)
77  {
79  }
80 
81  for (int i = 0 ; i < 6 ; i++)
82  {
84  }
85 
86  for (int i = 0 ; i < 16 ; i++)
87  {
89  }
90 }
91 
92 //-----------------------------------------------------------------------------
93 
95 {
96  delete[] m_Dilated;
97  delete[] m_Eroded;
98  delete[] m_Working;
99  delete[] m_UnalteredImage;
100 }
101 
102 //-----------------------------------------------------------------------------
103 
105 {
106  LOG_TRACE("FidSegmentation::UpdateParameters");
107 
108  // Create morphological circle
109  m_MorphologicalCircle.clear();
111  for (int x = -radiuspx; x <= radiuspx; x++)
112  {
113  for (int y = -radiuspx; y <= radiuspx; y++)
114  {
115  if (sqrt(pow(x, 2.0) + pow(y, 2.0)) <= radiuspx)
116  {
117  PlusCoordinate2D dot;
118  dot.X = y;
119  dot.Y = x;
120  m_MorphologicalCircle.push_back(dot);
121  }
122  }
123  }
124 }
125 
126 //-----------------------------------------------------------------------------
127 
129 {
130  LOG_TRACE("FidSegmentation::ReadConfiguration");
131 
132  XML_FIND_NESTED_ELEMENT_REQUIRED(phantomDefinition, configData, "PhantomDefinition");
133  XML_FIND_NESTED_ELEMENT_REQUIRED(description, phantomDefinition, "Description");
134  XML_READ_ENUM3_ATTRIBUTE_OPTIONAL(FiducialGeometry, description, "Multi-N", CALIBRATION_PHANTOM_MULTI_NWIRE, "CIRS", CIRS_PHANTOM_13_POINT, "Double-N", CALIBRATION_PHANTOM_6_POINT);
135 
136  XML_FIND_NESTED_ELEMENT_OPTIONAL(segmentationParameters, configData, "Segmentation");
137  if (!segmentationParameters)
138  {
139  segmentationParameters = igsioXmlUtils::GetNestedElementWithName(configData, "Segmentation");
141  }
142 
143  XML_READ_SCALAR_ATTRIBUTE_WARNING(double, ApproximateSpacingMmPerPixel, segmentationParameters);
144  XML_READ_SCALAR_ATTRIBUTE_WARNING(double, MorphologicalOpeningCircleRadiusMm, segmentationParameters);
145  XML_READ_SCALAR_ATTRIBUTE_WARNING(double, MorphologicalOpeningBarSizeMm, segmentationParameters);
146 
147  // Segmentation search region Y direction
148  int clipOrigin[2] = {0};
149  int clipSize[2] = {0};
150  if (segmentationParameters->GetVectorAttribute("ClipRectangleOrigin", 2, clipOrigin) &&
151  segmentationParameters->GetVectorAttribute("ClipRectangleSize", 2, clipSize))
152  {
153  m_RegionOfInterest[0] = clipOrigin[0];
154  m_RegionOfInterest[1] = clipOrigin[1];
155  m_RegionOfInterest[2] = clipOrigin[0] + clipSize[0];
156  m_RegionOfInterest[3] = clipOrigin[1] + clipSize[1];
157  }
158  else
159  {
160  LOG_INFO("Cannot find ClipRectangleOrigin or ClipRectangleSize attribute in the SegmentationParameters configuration file; Using the largest ROI possible.");
161  }
162 
163  XML_READ_SCALAR_ATTRIBUTE_WARNING(double, ThresholdImagePercent, segmentationParameters);
164 
165  int intUseOriginalImageIntensityForDotIntensityScore = 0;
166  if (segmentationParameters->GetScalarAttribute("UseOriginalImageIntensityForDotIntensityScore", intUseOriginalImageIntensityForDotIntensityScore))
167  {
168  LOG_WARNING("UseOriginalImageIntensityForDotIntensityScore attribute expected to have 'TRUE' or 'FALSE' value. Numerical values ('1' or '0') are deprecated.");
169  SetUseOriginalImageIntensityForDotIntensityScore(intUseOriginalImageIntensityForDotIntensityScore != 0);
170  }
171  else
172  {
173  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(UseOriginalImageIntensityForDotIntensityScore, segmentationParameters);
174  }
175 
176 
177  // If the tolerance parameters are computed automatically
178  int computeSegmentationParametersFromPhantomDefinition(0);
179  if (segmentationParameters->GetScalarAttribute("ComputeSegmentationParametersFromPhantomDefinition", computeSegmentationParametersFromPhantomDefinition)
180  && computeSegmentationParametersFromPhantomDefinition != 0)
181  {
182  double imageScalingTolerancePercent[4] = {0};
183  if (segmentationParameters->GetVectorAttribute("ImageScalingTolerancePercent", 4, imageScalingTolerancePercent))
184  {
185  for (int i = 0; i < 4 ; i++)
186  {
187  m_ImageScalingTolerancePercent[i] = imageScalingTolerancePercent[i];
188  }
189  }
190  else
191  {
192  LOG_WARNING("Could not read imageScalingTolerancePercent from configuration file.");
193  }
194 
195  double imageNormalVectorInPhantomFrameEstimation[3] = {0};
196  if (segmentationParameters->GetVectorAttribute("ImageNormalVectorInPhantomFrameEstimation", 3, imageNormalVectorInPhantomFrameEstimation))
197  {
198  m_ImageNormalVectorInPhantomFrameEstimation[0] = imageNormalVectorInPhantomFrameEstimation[0];
199  m_ImageNormalVectorInPhantomFrameEstimation[1] = imageNormalVectorInPhantomFrameEstimation[1];
200  m_ImageNormalVectorInPhantomFrameEstimation[2] = imageNormalVectorInPhantomFrameEstimation[2];
201  }
202  else
203  {
204  LOG_WARNING("Could not read imageNormalVectorInPhantomFrameEstimation from configuration file.");
205  }
206  }
207 
208  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, NumberOfMaximumFiducialPointCandidates, segmentationParameters);
209 
211 
212  return PLUS_SUCCESS;
213 }
214 
215 //-----------------------------------------------------------------------------
216 
217 void PlusFidSegmentation::SetFrameSize(const FrameSizeType& frameSize)
218 {
219  LOG_TRACE("FidSegmentation::SetFrameSize(" << frameSize[0] << ", " << frameSize[1] << ")");
220 
221  if ((m_FrameSize[0] == frameSize[0]) && (m_FrameSize[1] == frameSize[1]))
222  {
223  return;
224  }
225 
226  if ((m_FrameSize[0] != 0) && (m_FrameSize[1] != 0))
227  {
228  delete[] m_Dilated;
229  delete[] m_Eroded;
230  delete[] m_Working;
231  delete[] m_UnalteredImage;
232  }
233 
234  m_FrameSize[0] = frameSize[0];
235  m_FrameSize[1] = frameSize[1];
236  m_FrameSize[2] = 1;
237 
238  // Create working images (after deleting them in case they were already created)
239  long size = m_FrameSize[0] * m_FrameSize[1];
244 
245  // Set ROI to the largest possible if not already set
246  if ((m_RegionOfInterest[0] == 0) || (m_RegionOfInterest[1] == 0) || (m_RegionOfInterest[2] == 0) || (m_RegionOfInterest[3] == 0))
247  {
248  unsigned int barSize = GetMorphologicalOpeningBarSizePx();
249  m_RegionOfInterest[0] = barSize + 1;
250  m_RegionOfInterest[1] = barSize + 1;
251  m_RegionOfInterest[2] = m_FrameSize[0] - barSize - 1;
252  m_RegionOfInterest[3] = m_FrameSize[1] - barSize - 1;
253  }
254  else
255  {
256  // Check the search region in case it was set to too big (with the additional bar size it would go out of image)
257  unsigned int barSize = GetMorphologicalOpeningBarSizePx();
258  if (m_RegionOfInterest[0] - barSize <= 0)
259  {
260  m_RegionOfInterest[0] = barSize + 1;
261  LOG_WARNING("The region of interest is too big, bar size is " << barSize);
262  }
263  if (m_RegionOfInterest[1] - barSize <= 0)
264  {
265  m_RegionOfInterest[1] = barSize + 1;
266  LOG_WARNING("The region of interest is too big, bar size is " << barSize);
267  }
268  if (m_RegionOfInterest[2] + barSize >= m_FrameSize[0])
269  {
270  m_RegionOfInterest[2] = m_FrameSize[0] - barSize - 1;
271  LOG_WARNING("The region of interest is too big, bar size is " << barSize);
272  }
273  if (m_RegionOfInterest[3] + barSize >= m_FrameSize[1])
274  {
275  m_RegionOfInterest[3] = m_FrameSize[1] - barSize - 1;
276  LOG_WARNING("The region of interest is too big, bar size is " << barSize);
277  }
278  }
279 }
280 
281 //-----------------------------------------------------------------------------
282 
284 {
285  //LOG_TRACE("FidSegmentation::Clear");
286 
287  m_DotsVector.clear();
288  m_CandidateFidValues.clear();
289 }
290 
291 //-----------------------------------------------------------------------------
292 
294 {
295  //LOG_TRACE("FidSegmentation::GetMorphologicalOpeningBarSizePx");
296 
297  return static_cast<unsigned int>(floor(m_MorphologicalOpeningBarSizeMm / m_ApproximateSpacingMmPerPixel + 0.5));
298 }
299 
300 //-----------------------------------------------------------------------------
301 
303 {
304  //LOG_TRACE("FidSegmentation::ErodePoint0");
305 
306  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
307  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
308  unsigned int p = ir * m_FrameSize[0] + ic - barSize; // current pixel - bar size (position of the start of the bar)
309  unsigned int p_max = ir * m_FrameSize[0] + ic + barSize; // current pixel + bar size (position of the end of the bar)
310 
311  //find lowest intensity in bar shaped area in image
312  for (; p <= p_max; p++)
313  {
314  if (image[p] < dval)
315  {
316  dval = image[p];
317  }
318  if (image[p] == 0)
319  {
320  break;
321  }
322  }
323 
324  return dval;
325 }
326 
327 //-----------------------------------------------------------------------------
328 
330 {
331  //LOG_TRACE("FidSegmentation::Erode0");
332 
333  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
334 
335  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
336 
337  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
338  {
339  unsigned int ic = m_RegionOfInterest[0];
340  unsigned int p_base = ir * m_FrameSize[0];
341 
342  PlusFidSegmentation::PixelType dval = ErodePoint0(image, ir, ic); // find lowest pixel intensity in surrounding region ( positions +/- 8 of current pixel position)
343  dest[p_base + ic] = dval; // p_base+ic = current pixel
344 
345  for (ic++; ic < m_RegionOfInterest[2]; ic++)
346  {
347  PlusFidSegmentation::PixelType new_val = image[p_base + ic + barSize];
348  PlusFidSegmentation::PixelType del_val = image[p_base + ic - 1 - barSize];
349 
350  dval = new_val <= dval ? new_val : // dval = new val if new val is less than or equal to dval
351  del_val > dval ? std::min(dval, new_val) : // if del val is greater than dval, dval= min of dval and new val
352  ErodePoint0(image, ir, ic); //else dval = result of erode function
353 
354  dest[ir * m_FrameSize[0] + ic] = dval; // update new "eroded" picture
355  }
356  }
357 }
358 
359 //-----------------------------------------------------------------------------
360 
362 {
363  //LOG_TRACE("FidSegmentation::ErodePoint45");
364 
365  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
366 
367  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
368  unsigned int p = (ir + barSize) * m_FrameSize[0] + ic - barSize;
369  unsigned int p_max = (ir - barSize) * m_FrameSize[0] + ic + barSize;
370 
371  for (; p >= p_max; p = p - m_FrameSize[0] + 1)
372  {
373  if (image[p] < dval)
374  {
375  dval = image[p];
376  }
377  if (image[p] == 0)
378  {
379  break;
380  }
381  }
382  return dval;
383 }
384 
385 //-----------------------------------------------------------------------------
386 
388 {
389  //LOG_TRACE("FidSegmentation::Erode45");
390 
391  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
392  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
393 
394  /* Down the left side. */
395  for (unsigned int sr = m_RegionOfInterest[1]; sr < m_RegionOfInterest[3]; sr++)
396  {
397  unsigned int ir = sr;
398  unsigned int ic = m_RegionOfInterest[0];
399 
400  PlusFidSegmentation::PixelType dval = ErodePoint45(image, ir, ic);
401  dest[ir * m_FrameSize[0] + ic] = dval;
402 
403  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
404  {
405  PlusFidSegmentation::PixelType new_val = image[(ir - barSize) * m_FrameSize[0] + (ic + barSize)];
406  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
407 
408  dval = new_val <= dval ? new_val :
409  del_val > dval ? std::min(dval, new_val) :
410  ErodePoint45(image, ir, ic);
411 
412  dest[ir * m_FrameSize[0] + ic] = dval;
413  }
414  }
415 
416  /* Across the bottom */
417  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
418  {
419  unsigned int ic = sc;
420  unsigned int ir = m_RegionOfInterest[3] - 1;
421 
422  PlusFidSegmentation::PixelType dval = ErodePoint45(image, ir, ic);
423  dest[ir * m_FrameSize[0] + ic] = dval;
424 
425  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
426  {
427  PlusFidSegmentation::PixelType new_val = image[(ir - barSize) * m_FrameSize[0] + (ic + barSize)];
428  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
429 
430  dval = new_val <= dval ? new_val :
431  del_val > dval ? std::min(dval, new_val) :
432  ErodePoint45(image, ir, ic);
433 
434  dest[ir * m_FrameSize[0] + ic] = dval;
435  }
436  }
437 }
438 
439 //-----------------------------------------------------------------------------
440 
442 {
443  //LOG_TRACE("FidSegmentation::ErodePoint90");
444 
445  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
446  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
447  unsigned int p = (ir - barSize) * m_FrameSize[0] + ic;
448  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic;
449 
450  for (; p <= p_max; p += m_FrameSize[0])
451  {
452  if (image[p] < dval)
453  {
454  dval = image[p];
455  }
456  if (image[p] == 0)
457  {
458  break;
459  }
460  }
461  return dval;
462 }
463 
464 //-----------------------------------------------------------------------------
465 
467 {
468  //LOG_TRACE("FidSegmentation::Erode90");
469 
470  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
471 
472  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
473 
474  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
475  {
476  unsigned int ir = m_RegionOfInterest[1];
477 
478  PlusFidSegmentation::PixelType dval = ErodePoint90(image, ir, ic);
479  dest[ir * m_FrameSize[0] + ic] = dval;
480 
481  for (ir++; ir < m_RegionOfInterest[3]; ir++)
482  {
483  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + ic];
484  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + ic];
485 
486  dval = new_val <= dval ? new_val :
487  del_val > dval ? std::min(dval, new_val) :
488  ErodePoint90(image, ir, ic);
489 
490  dest[ir * m_FrameSize[0] + ic] = dval;
491  }
492  }
493 }
494 
495 //-----------------------------------------------------------------------------
496 
498 {
499  //LOG_TRACE("FidSegmentation::ErodePoint135");
500 
501  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
502  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
503  unsigned int p = (ir - barSize) * m_FrameSize[0] + ic - barSize;
504  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic + barSize;
505 
506  for (; p <= p_max; p = p + m_FrameSize[0] + 1)
507  {
508  if (image[p] < dval)
509  {
510  dval = image[p];
511  }
512  if (image[p] == 0)
513  {
514  break;
515  }
516  }
517  return dval;
518 }
519 
520 //-----------------------------------------------------------------------------
521 
523 {
524  //LOG_TRACE("FidSegmentation::Erode135");
525 
526  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
527 
528  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
529 
530  /* Up the left side. */
531  for (unsigned int sr = m_RegionOfInterest[3] - 1; sr >= m_RegionOfInterest[1]; sr--)
532  {
533  unsigned int ir = sr;
534  unsigned int ic = m_RegionOfInterest[0];
535 
536  PlusFidSegmentation::PixelType dval = ErodePoint135(image, ir, ic);
537  dest[ir * m_FrameSize[0] + ic] = dval;
538 
539  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
540  {
541  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
542  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
543 
544  dval = new_val <= dval ? new_val :
545  del_val > dval ? std::min(dval, new_val) :
546  ErodePoint135(image, ir, ic);
547 
548  dest[ir * m_FrameSize[0] + ic] = dval;
549  }
550  }
551 
552  /* Across the top. */
553  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
554  {
555  unsigned int ic = sc;
556  unsigned int ir = m_RegionOfInterest[1];
557 
558  PlusFidSegmentation::PixelType dval = ErodePoint135(image, ir, ic);
559  dest[ir * m_FrameSize[0] + ic] = dval;
560 
561  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
562  {
563  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
564  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
565 
566  dval = new_val <= dval ? new_val :
567  del_val > dval ? std::min(dval, new_val) :
568  ErodePoint135(image, ir, ic);
569 
570  dest[ir * m_FrameSize[0] + ic] = dval;
571  }
572  }
573 }
574 
575 //-----------------------------------------------------------------------------
576 
578 {
579  //LOG_TRACE("FidSegmentation::ErodeCircle");
580 
581  unsigned int slen = m_MorphologicalCircle.size();
582 
583  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
584 
585  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
586  {
587  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
588  {
589  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
590  for (unsigned int sp = 0; sp < slen; sp++)
591  {
592  int sr = ir + m_MorphologicalCircle[sp].X;
593  int sc = ic + m_MorphologicalCircle[sp].Y;
594  PlusFidSegmentation::PixelType pixSrc = image[sr * m_FrameSize[0] + sc];
595 
596  if (pixSrc < dval)
597  {
598  dval = pixSrc;
599  }
600 
601  if (pixSrc == 0)
602  {
603  break;
604  }
605  }
606 
607  dest[ir * m_FrameSize[0] + ic] = dval;
608  }
609  }
610 }
611 
612 //-----------------------------------------------------------------------------
613 
615 {
616  //LOG_TRACE("FidSegmentation::DilatePoint0");
617 
618  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
620  unsigned int p = ir * m_FrameSize[0] + ic - barSize;
621  unsigned int p_max = ir * m_FrameSize[0] + ic + barSize;
622 
623  for (; p <= p_max; p++)
624  {
625  if (image[p] > dval)
626  {
627  dval = image[p];
628  }
629  }
630 
631  return dval;
632 }
633 
634 //-----------------------------------------------------------------------------
635 
637 {
638  //LOG_TRACE("FidSegmentation::Dilate0");
639 
640  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
641 
642  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
643 
644  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
645  {
646  unsigned int ic = m_RegionOfInterest[0];
647  unsigned int p_base = ir * m_FrameSize[0];
648 
649  PlusFidSegmentation::PixelType dval = DilatePoint0(image, ir, ic);
650  dest[ir * m_FrameSize[0] + ic] = dval;
651 
652  for (ic++; ic < m_RegionOfInterest[2]; ic++)
653  {
654  PlusFidSegmentation::PixelType new_val = image[p_base + ic + barSize];
655  PlusFidSegmentation::PixelType del_val = image[p_base + ic - 1 - barSize];
656 
657  dval = new_val >= dval ? new_val :
658  (del_val < dval ? std::max(dval, new_val) :
659  DilatePoint0(image, ir, ic));
660  dest[ir * m_FrameSize[0] + ic] = dval;
661  }
662  }
663 }
664 
665 //-----------------------------------------------------------------------------
666 
668 {
669  //LOG_TRACE("FidSegmentation::DilatePoint45");
670 
671  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
673  unsigned int p = (ir + barSize) * m_FrameSize[0] + ic - barSize;
674  unsigned int p_max = (ir - barSize) * m_FrameSize[0] + ic + barSize;
675 
676  while (p >= p_max)
677  {
678  if (image[p] > dval)
679  {
680  dval = image[p];
681  }
682  if (p - m_FrameSize[0] + 1 > p)
683  {
684  // unsigned int underflow, adjust
685  p = 0;
686  }
687  else
688  {
689  p = p - m_FrameSize[0] + 1;
690  }
691  }
692 
693  return dval;
694 }
695 
696 //-----------------------------------------------------------------------------
697 
699 {
700  //LOG_TRACE("FidSegmentation::Dilate45");
701 
702  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
703  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
704 
705  /* Down the left side. */
706  for (unsigned int sr = m_RegionOfInterest[1]; sr < m_RegionOfInterest[3]; sr++)
707  {
708  unsigned int ir = sr;
709  unsigned int ic = m_RegionOfInterest[0];
710 
711  PlusFidSegmentation::PixelType dval = DilatePoint45(image, ir, ic);
712  dest[ir * m_FrameSize[0] + ic] = dval ;
713  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
714  {
715  PlusFidSegmentation::PixelType new_val = image[(ir - barSize) * m_FrameSize[0] + (ic + barSize)];
716  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
717 
718  dval = new_val >= dval ? new_val :
719  (del_val < dval ? std::max(dval, new_val) :
720  DilatePoint45(image, ir, ic));
721  dest[ir * m_FrameSize[0] + ic] = dval ;
722  }
723  }
724 
725  /* Across the bottom */
726  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
727  {
728  unsigned int ic = sc;
729  unsigned int ir = m_RegionOfInterest[3] - 1;
730 
731  PlusFidSegmentation::PixelType dval = DilatePoint45(image, ir, ic);
732  dest[ir * m_FrameSize[0] + ic] = dval ;
733  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
734  {
735  PlusFidSegmentation::PixelType new_val = image[(ir - barSize) * m_FrameSize[0] + (ic + barSize)];
736  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
737 
738  dval = new_val >= dval ? new_val :
739  (del_val < dval ? std::max(dval, new_val) :
740  DilatePoint45(image, ir, ic));
741  dest[ir * m_FrameSize[0] + ic] = dval ;
742  }
743  }
744 }
745 
746 //-----------------------------------------------------------------------------
747 
749 {
750  //LOG_TRACE("FidSegmentation::DilatePoint90");
751 
752  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
754  unsigned int p = (ir - barSize) * m_FrameSize[0] + ic;
755  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic;
756 
757  for (; p <= p_max; p += m_FrameSize[0])
758  {
759  if (image[p] > dval)
760  {
761  dval = image[p];
762  }
763  }
764  return dval;
765 }
766 
767 //-----------------------------------------------------------------------------
768 
770 {
771  //LOG_TRACE("FidSegmentation::Dilate90");
772 
773  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
774  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
775 
776  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
777  {
778  unsigned int ir = m_RegionOfInterest[1];
779 
780  PlusFidSegmentation::PixelType dval = DilatePoint90(image, ir, ic);
781  dest[ir * m_FrameSize[0] + ic] = dval ;
782  for (ir++; ir < m_RegionOfInterest[3]; ir++)
783  {
784  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + ic];
785  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + ic];
786 
787  dval = new_val >= dval ? new_val :
788  (del_val < dval ? std::max(dval, new_val) :
789  DilatePoint90(image, ir, ic));
790 
791  dest[ir * m_FrameSize[0] + ic] = dval ;
792  }
793  }
794 }
795 
796 //-----------------------------------------------------------------------------
797 
799 {
800  //LOG_TRACE("FidSegmentation::DilatePoint135");
801 
802  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
804  unsigned int p = (ir - barSize) * m_FrameSize[0] + ic - barSize;
805  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic + barSize;
806 
807  for (; p <= p_max; p = p + m_FrameSize[0] + 1)
808  {
809  if (image[p] > dval)
810  {
811  dval = image[p];
812  }
813  }
814  return dval;
815 }
816 
817 //-----------------------------------------------------------------------------
818 
820 {
821  //LOG_TRACE("FidSegmentation::Dilate135");
822 
823  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
824  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
825 
826  /* Up the left side. */
827  for (unsigned int sr = m_RegionOfInterest[3] - 1; sr >= m_RegionOfInterest[1]; sr--)
828  {
829  unsigned int ir = sr;
830  unsigned int ic = m_RegionOfInterest[0];
831 
832  PlusFidSegmentation::PixelType dval = DilatePoint135(image, ir, ic);
833  dest[ir * m_FrameSize[0] + ic] = dval ;
834  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
835  {
836  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
837  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
838 
839  dval = new_val >= dval ? new_val :
840  (del_val < dval ? std::max(dval, new_val) :
841  DilatePoint135(image, ir, ic));
842  dest[ir * m_FrameSize[0] + ic] = dval;
843  }
844  }
845 
846  /* Across the top. */
847  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
848  {
849  unsigned int ic = sc;
850  unsigned int ir = m_RegionOfInterest[1];
851 
852  PlusFidSegmentation::PixelType dval = DilatePoint135(image, ir, ic);
853  dest[ir * m_FrameSize[0] + ic] = dval;
854  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
855  {
856  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
857  PlusFidSegmentation::PixelType del_val = image[(ir - 1 - barSize) * m_FrameSize[0] + (ic - 1 - barSize)];
858 
859  dval = new_val >= dval ? new_val :
860  (del_val < dval ? std::max(dval, new_val) :
861  DilatePoint135(image, ir, ic));
862  dest[ir * m_FrameSize[0] + ic] = dval;
863  }
864  }
865 }
866 
867 //-----------------------------------------------------------------------------
868 
870 {
871  //LOG_TRACE("FidSegmentation::DilatePoint");
872 
874  for (int sp = 0; sp < slen; sp++)
875  {
876  unsigned int sr = ir + shape[sp].Y;
877  unsigned int sc = ic + shape[sp].X;
878 
879  if (image[sr * m_FrameSize[0] + sc] > dval)
880  {
881  dval = image[sr * m_FrameSize[0] + sc];
882  }
883  }
884  return dval;
885 }
886 
887 //-----------------------------------------------------------------------------
888 
890 {
891  //LOG_TRACE("FidSegmentation::DilateCircle");
892 
893  unsigned int slen = m_MorphologicalCircle.size();
894 
895  PlusCoordinate2D* shape = new PlusCoordinate2D[slen];
896 
897  for (unsigned int i = 0; i < slen; i++)
898  {
899  shape[i] = m_MorphologicalCircle[i];
900  }
901 
902  /* Which elements stick around when you shift right? */
903  int n = 0;
904 
905  bool* sr_exist = new bool[slen];
906 
907  memset(sr_exist, 0, slen * sizeof(bool));
908  for (unsigned int si = 0; si < slen; si++)
909  {
910  PlusCoordinate2D dot;
911  dot.X = m_MorphologicalCircle[si].X + 1;
912  dot.Y = m_MorphologicalCircle[si].Y;
913 
915  {
916  sr_exist[si] = true, n++;
917  }
918  }
919  //cout << "shift_exist: " << n << endl;
920 
921  PlusCoordinate2D* newDots = new PlusCoordinate2D[slen];
922  PlusCoordinate2D* oldDots = new PlusCoordinate2D[slen];
923 
924  int nNewDots = 0, nOldDots = 0;
925  for (unsigned int si = 0; si < slen; si++)
926  {
927  if (sr_exist[si])
928  {
929  oldDots[nOldDots++] = shape[si];
930  }
931  else
932  {
933  newDots[nNewDots++] = shape[si];
934  }
935  }
936 
937  delete [] sr_exist;
938 
939  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
940  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
941  {
942  unsigned int ic = m_RegionOfInterest[0];
943 
944  PlusFidSegmentation::PixelType dval = DilatePoint(image, ir, ic, shape, slen);
945  PlusFidSegmentation::PixelType last = dest[ir * m_FrameSize[0] + ic] = dval;
946 
947  for (ic++; ic < m_RegionOfInterest[2]; ic++)
948  {
949  PlusFidSegmentation::PixelType dval = DilatePoint(image, ir, ic, newDots, nNewDots);
950 
951  if (dval < last)
952  {
953  for (int sp = 0; sp < nOldDots; sp++)
954  {
955  unsigned int sr = ir + oldDots[sp].Y;
956  unsigned int sc = ic + oldDots[sp].X;
957  if (image[sr * m_FrameSize[0] + sc] > dval)
958  {
959  dval = image[sr * m_FrameSize[0] + sc];
960  }
961  if (image[sr * m_FrameSize[0] + sc] == last)
962  {
963  break;
964  }
965  }
966  }
967  last = dest[ir * m_FrameSize[0] + ic] = dval ;
968  }
969  }
970  delete [] newDots;
971  delete [] oldDots;
972 }
973 
974 //-----------------------------------------------------------------------------
975 
976 bool PlusFidSegmentation::ShapeContains(std::vector<PlusCoordinate2D>& shape, PlusCoordinate2D point)
977 {
978  //LOG_TRACE("FidSegmentation::ShapeContains");
979 
980  for (unsigned int si = 0; si < shape.size(); si++)
981  {
982  if (shape[si] == point)
983  {
984  return true;
985  }
986  }
987  return false;
988 }
989 
990 //-----------------------------------------------------------------------------
991 
992 void PlusFidSegmentation::WritePng(PlusFidSegmentation::PixelType* modifiedImage, std::string outImageName, int cols, int rows)
993 {
994  //LOG_TRACE("FidSegmentation::WritePng");
995 
996  // output intermediate image
997  const unsigned int Dimension = 2;
998 
999  typedef itk::Image< PlusFidSegmentation::PixelType, Dimension > ImageType;
1000  auto modImage = ImageType::New();
1001  ImageType::SizeType size;
1002  size[0] = cols;
1003  size[1] = rows;
1004 
1005  ImageType::IndexType start;
1006  start[0] = 0;
1007  start[1] = 0;
1008 
1009  ImageType::RegionType wholeImage;
1010  wholeImage.SetSize(size);
1011  wholeImage.SetIndex(start);
1012 
1013  modImage->SetRegions(wholeImage);
1014  modImage->Allocate();
1015 
1016  typedef itk::ImageFileWriter< ImageType > WriterType;
1017  auto pngImageIO = itk::PNGImageIO::New();
1018  pngImageIO->SetCompressionLevel(0);
1019 
1020  auto writer = WriterType::New();
1021  writer->SetImageIO(pngImageIO);
1022  writer->SetFileName(outImageName);
1023 
1024 
1025  typedef itk::ImageRegionIterator<ImageType> IterType;
1026  IterType iter(modImage, modImage->GetRequestedRegion());
1027  iter.GoToBegin();
1028 
1029  int count = 0;
1030 
1031  while (!iter.IsAtEnd())
1032  {
1033  iter.Set(modifiedImage[count]);
1034  count++;
1035  ++iter;
1036  }
1037 
1038  writer->SetInput(modImage); // piping output of reader into input of writer
1039 
1040  try
1041  {
1042  writer->Update(); // change to writing if want writing feature
1043  }
1044  catch (itk::ExceptionObject& err)
1045  {
1046  LOG_ERROR("Exception! writer did not update"); //ditto
1047  LOG_ERROR(err);
1048  }
1049  // end output
1050 
1051 }
1052 
1053 //-----------------------------------------------------------------------------
1054 void PlusFidSegmentation::SetDefaultSegmentationParameters(vtkXMLDataElement* segmentationDataElement)
1055 {
1056  if (!segmentationDataElement)
1057  {
1058  return;
1059  }
1060 
1061  segmentationDataElement->SetName("Segmentation");
1062  segmentationDataElement->SetDoubleAttribute("ApproximateSpacingMmPerPixel", DEFAULT_APPROXIMATE_SPACING_MM_PER_PIXEL);
1063  segmentationDataElement->SetDoubleAttribute("MorphologicalOpeningCircleRadiusMm", DEFAULT_MORPHOLOGICAL_OPENING_CIRCLE_RADIUS_MM);
1064  segmentationDataElement->SetDoubleAttribute("MorphologicalOpeningBarSizeMm", DEFAULT_MORPHOLOGICAL_OPENING_BAR_SIZE_MM);
1065  segmentationDataElement->SetDoubleAttribute("MaxLinePairDistanceErrorPercent", DEFAULT_MAX_LINE_PAIR_DISTANCE_ERROR_PERCENT);
1066  segmentationDataElement->SetDoubleAttribute("AngleToleranceDegrees", DEFAULT_ANGLE_TOLERANCE_DEGREES);
1067  segmentationDataElement->SetDoubleAttribute("MaxAngleDifferenceDegrees", DEFAULT_MAX_ANGLE_DIFFERENCE_DEGREES);
1068  segmentationDataElement->SetDoubleAttribute("MinThetaDegrees", DEFAULT_MIN_THETA_DEGREES);
1069  segmentationDataElement->SetDoubleAttribute("MaxThetaDegrees", DEFAULT_MAX_THETA_DEGREES);
1070  segmentationDataElement->SetDoubleAttribute("MaxLineShiftMm", DEFAULT_MAX_LINE_SHIFT_MM);
1071  segmentationDataElement->SetDoubleAttribute("ThresholdImagePercent", DEFAULT_THRESHOLD_IMAGE_PERCENT);
1072  segmentationDataElement->SetDoubleAttribute("CollinearPointsMaxDistanceFromLineMm", DEFAULT_COLLINEAR_POINTS_MAX_DISTANCE_FROM_LINE_MM);
1073  segmentationDataElement->SetAttribute("UseOriginalImageIntensityForDotIntensityScore", DEFAULT_USE_ORIGINAL_IMAGE_INTENSITY_FOR_DOT_INTENSITY_SCORE);
1074  segmentationDataElement->SetDoubleAttribute("NumberOfMaximumFiducialPointCandidates", DEFAULT_NUMBER_OF_MAXIMUM_FIDUCIAL_POINT_CANDIDATES);
1075 
1076  std::stringstream clipOriginSS;
1077  clipOriginSS << DEFAULT_CLIP_ORIGIN[0] << " " << DEFAULT_CLIP_ORIGIN[1];
1078  std::string clipOriginString = clipOriginSS.str();
1079  segmentationDataElement->SetAttribute("ClipRectangleOrigin", clipOriginString.c_str());
1080 
1081  std::stringstream clipSizeSS;
1082  clipSizeSS << DEFAULT_CLIP_SIZE[0] << " " << DEFAULT_CLIP_SIZE[1];
1083  std::string clipSizeString = clipSizeSS.str();
1084  segmentationDataElement->SetAttribute("ClipRectangleSize", clipSizeString.c_str());
1085 }
1086 
1087 //-----------------------------------------------------------------------------
1088 
1089 void PlusFidSegmentation::WritePossibleFiducialOverlayImage(const std::vector<PlusFidDot>& fiducials, PlusFidSegmentation::PixelType* unalteredImage, const char* namePrefix, int frameIndex)
1090 {
1091  //LOG_TRACE("FidSegmentation::WritePossibleFiducialOverlayImage");
1092 
1093  typedef itk::RGBPixel< unsigned char > ColorPixelType;
1094  typedef itk::Image< ColorPixelType, 2 > ImageType;
1095 
1096  auto possibleFiducials = ImageType::New();
1097 
1098  ImageType::SizeType size;
1099  size[0] = m_FrameSize[0];
1100  size[1] = m_FrameSize[1];
1101 
1102  ImageType::IndexType start;
1103  start[0] = 0;
1104  start[1] = 0;
1105 
1106  ImageType::RegionType wholeImage;
1107  wholeImage.SetSize(size);
1108  wholeImage.SetIndex(start);
1109 
1110  possibleFiducials->SetRegions(wholeImage);
1111  possibleFiducials->Allocate();
1112 
1113  ImageType::IndexType pixelLocation;
1114 
1115  ImageType::PixelType pixelValue;
1116 
1117  // copy pixel by pixel (we need to do gray->RGB conversion and only a ROI is updated)
1118  for (unsigned int r = m_RegionOfInterest[1]; r < m_RegionOfInterest[3]; r++)
1119  {
1120  for (unsigned int c = m_RegionOfInterest[0]; c < m_RegionOfInterest[2]; c++)
1121  {
1122  pixelValue[0] = 0; //unalteredImage[r*cols+c];
1123  pixelValue[1] = unalteredImage[r * m_FrameSize[0] + c];
1124  pixelValue[2] = unalteredImage[r * m_FrameSize[0] + c];
1125  pixelLocation[0] = c;
1126  pixelLocation[1] = r;
1127  possibleFiducials->SetPixel(pixelLocation, pixelValue);
1128  }
1129  }
1130 
1131  // Set pixelValue to red (it will be used to mark the centroid of the clusters)
1132  for (unsigned int numDots = 0; numDots < fiducials.size(); numDots++)
1133  {
1134  const int markerPosCount = 5;
1135  const int markerPos[markerPosCount][2] = {{0, 0}, { +1, 0}, { -1, 0}, {0, +1}, {0, -1}};
1136 
1137  for (int i = 0; i < markerPosCount; i++)
1138  {
1139  pixelLocation[0] = fiducials[numDots].GetX() + markerPos[i][0];
1140  pixelLocation[1] = fiducials[numDots].GetY() + markerPos[i][1];
1141  int clusterMarkerIntensity = fiducials[numDots].GetDotIntensity() * 10;
1142  if (clusterMarkerIntensity > 255)
1143  {
1144  clusterMarkerIntensity = 255;
1145  }
1146  pixelValue[0] = clusterMarkerIntensity;
1147  pixelValue[1] = 0;
1148  pixelValue[2] = 0;
1149  possibleFiducials->SetPixel(pixelLocation, pixelValue);
1150  }
1151  }
1152  std::ostringstream possibleFiducialsImageFilename;
1153  possibleFiducialsImageFilename << namePrefix << std::setw(3) << std::setfill('0') << frameIndex << ".bmp" << std::ends;
1154 
1155  SetPossibleFiducialsImageFilename(possibleFiducialsImageFilename.str());
1156 
1157  typedef itk::ImageFileWriter< ImageType > WriterType;
1158  auto writeImage = WriterType::New();
1159  writeImage->SetFileName(m_PossibleFiducialsImageFilename);
1160 
1161  writeImage->SetInput(possibleFiducials);
1162 
1163  try
1164  {
1165  writeImage->Update();
1166  }
1167  catch (itk::ExceptionObject& err)
1168  {
1169  LOG_ERROR("Exception! writer did not update");
1170  LOG_ERROR(err);
1171  }
1172 }
1173 
1174 //-----------------------------------------------------------------------------
1175 
1176 void PlusFidSegmentation::WritePossibleFiducialOverlayImage(const std::vector< std::vector<double> >& fiducials, PlusFidSegmentation::PixelType* unalteredImage, const char* namePrefix, int frameIndex)
1177 {
1178  std::vector<PlusFidDot> dots;
1179  for (unsigned int numDots = 0; numDots < fiducials.size(); numDots++)
1180  {
1181  PlusFidDot newDot;
1182  newDot.SetX(fiducials[numDots][0]);
1183  newDot.SetY(fiducials[numDots][1]);
1184  newDot.SetDotIntensity(10.0);
1185  dots.push_back(newDot);
1186  }
1187  WritePossibleFiducialOverlayImage(dots, unalteredImage, namePrefix, frameIndex);
1188 }
1189 
1190 //-----------------------------------------------------------------------------
1191 
1193 {
1194  //LOG_TRACE("FidSegmentation::Subtract");
1195 
1196  for (unsigned int pos = m_FrameSize[1] * m_FrameSize[0]; pos > 0; pos--)
1197  {
1198  *image = *vals > *image ? 0 : *image - *vals;
1199  image++;
1200  vals++;
1201  }
1202 }
1203 
1204 //-----------------------------------------------------------------------------
1205 
1206 /* Possible additional criteria:
1207  * 1. Track the frame-to-frame data?
1208  * 2. Lines should be roughly of the same length? */
1209 
1211 {
1212  LOG_TRACE("FidSegmentation::Suppress");
1213 
1214  // Get the minimum and maximum pixel value
1217  PlusFidSegmentation::PixelType* pix = image;
1218  for (unsigned int pos = 0; pos < m_FrameSize[0]*m_FrameSize[1]; pos ++)
1219  {
1220  if (*pix > max)
1221  {
1222  max = *pix;
1223  }
1224  if (*pix < min)
1225  {
1226  min = *pix;
1227  }
1228  pix++;
1229  }
1230 
1231  //We use floor to calculate the round value here.
1232 
1233  PlusFidSegmentation::PixelType thresh = min + (PlusFidSegmentation::PixelType)floor((double)(max - min) * percent_thresh + 0.5);
1234 
1235  //thresholding
1236  int pixelCount = m_FrameSize[0] * m_FrameSize[1];
1237  PlusFidSegmentation::PixelType* pixel = image;
1238  for (int i = 0; i < pixelCount; i++)
1239  {
1240  if (*pixel < thresh)
1241  {
1242  *pixel = BLACK;
1243  }
1244  pixel++;
1245  }
1246 
1247  if (m_DebugOutput)
1248  {
1249  WritePng(image, "seg-suppress.png", m_FrameSize[0], m_FrameSize[1]);
1250  }
1251 
1252 }
1253 
1254 //-----------------------------------------------------------------------------
1255 
1256 inline void PlusFidSegmentation::ClusteringAddNeighbors(PlusFidSegmentation::PixelType* image, int r, int c, std::vector<PlusFidDot>& testPosition, std::vector<PlusFidDot>& setPosition, std::vector<PlusFidSegmentation::PixelType>& valuesOfPosition)
1257 {
1258  //LOG_TRACE("FidSegmentation::ClusteringAddNeighbors");
1259 
1260  if (image[r * m_FrameSize[0] + c] > 0 && testPosition.size() < MAX_CLUSTER_VALS && setPosition.size() < MAX_CLUSTER_VALS)
1261  {
1262  PlusFidDot dot;
1263  dot.SetY(r);
1264  dot.SetX(c);
1265  testPosition.push_back(dot);
1266  setPosition.push_back(dot);
1267  valuesOfPosition.push_back(image[r * m_FrameSize[0] + c]);
1268  image[r * m_FrameSize[0] + c] = 0;
1269  }
1270 }
1271 
1272 //-----------------------------------------------------------------------------
1273 
1274 /* Should we accept a dot? */
1276 {
1277  //LOG_TRACE("FidSegmentation::AcceptDot");
1278 
1279  if (dot.GetY() >= m_RegionOfInterest[1] + MIN_WINDOW_DIST &&
1280  dot.GetY() < m_RegionOfInterest[3] - MIN_WINDOW_DIST &&
1281  dot.GetX() >= m_RegionOfInterest[0] + MIN_WINDOW_DIST &&
1283  {
1284  return true;
1285  }
1286  else
1287  {
1288  return false;
1289  }
1290 }
1291 
1292 //-----------------------------------------------------------------------------
1293 
1294 bool PlusFidSegmentation::Cluster(bool& tooManyCandidates)
1295 {
1296  LOG_TRACE("FidSegmentation::Cluster");
1297  tooManyCandidates = false;
1298 
1299  std::vector<PlusFidDot> testPosition;
1300  std::vector<PlusFidDot> setPosition;
1301  std::vector<PlusFidSegmentation::PixelType> valuesOfPosition;
1302 
1303  for (unsigned int r = m_RegionOfInterest[1]; r < m_RegionOfInterest[3]; r++)
1304  {
1305  for (unsigned int c = m_RegionOfInterest[0]; c < m_RegionOfInterest[2]; c++)
1306  {
1307  if (m_Working[r * m_FrameSize[0] + c] > 0)
1308  {
1309  testPosition.clear();
1310 
1311  PlusFidDot dot;
1312  dot.SetX(c);
1313  dot.SetY(r);
1314  testPosition.push_back(dot);
1315 
1316  setPosition.clear();
1317  setPosition.push_back(dot);
1318 
1319  valuesOfPosition.clear();
1320  valuesOfPosition.push_back(m_Working[r * m_FrameSize[0] + c]);
1321 
1322  m_Working[r * m_FrameSize[0] + c] = 0;
1323 
1324  while (testPosition.size() > 0)
1325  {
1326  PlusFidDot dot = testPosition.back();
1327  testPosition.pop_back();
1328 
1329  ClusteringAddNeighbors(m_Working, dot.GetY() - 1, dot.GetX() - 1, testPosition, setPosition, valuesOfPosition);
1330  ClusteringAddNeighbors(m_Working, dot.GetY() - 1, dot.GetX(), testPosition, setPosition, valuesOfPosition);
1331  ClusteringAddNeighbors(m_Working, dot.GetY() - 1, dot.GetX() + 1, testPosition, setPosition, valuesOfPosition);
1332 
1333  ClusteringAddNeighbors(m_Working, dot.GetY(), dot.GetX() - 1, testPosition, setPosition, valuesOfPosition);
1334  ClusteringAddNeighbors(m_Working, dot.GetY(), dot.GetX() + 1, testPosition, setPosition, valuesOfPosition);
1335 
1336  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX() - 1, testPosition, setPosition, valuesOfPosition);
1337  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX(), testPosition, setPosition, valuesOfPosition);
1338  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX() + 1, testPosition, setPosition, valuesOfPosition);
1339  }
1340 
1341  double dest_r = 0, dest_c = 0, total = 0;
1342  for (unsigned int p = 0; p < setPosition.size(); p++)
1343  {
1344  double amount = (double)valuesOfPosition[p] / (double)UCHAR_MAX;
1345  dest_r += setPosition[p].GetY() * amount;
1346  dest_c += setPosition[p].GetX() * amount;
1347  total += amount;
1348  }
1349 
1350  dot.SetY(dest_r / total);
1351  dot.SetX(dest_c / total);
1352  dot.SetDotIntensity(total);
1353 
1354  if (AcceptDot(dot))
1355  {
1357  {
1358  // Take into account intensities that are close to the dot center
1359  const double dotRadius2 = 3.0 * 3.0;
1360  double dest_r = 0, dest_c = 0, total = 0;
1361  for (unsigned int p = 0; p < setPosition.size(); p++)
1362  {
1363  if ((setPosition[p].GetY() - dot.GetY()) * (setPosition[p].GetY() - dot.GetY()) + (setPosition[p].GetX() - dot.GetX()) * (setPosition[p].GetX() - dot.GetX()) <= dotRadius2)
1364  {
1365  //double amount = (double)vals[p] / (double)UCHAR_MAX;
1366  double amount = (double)m_UnalteredImage[int(setPosition[p].GetY() * m_FrameSize[0] + setPosition[p].GetX())] / (double)UCHAR_MAX;
1367  dest_r += setPosition[p].GetY() * amount;
1368  dest_c += setPosition[p].GetX() * amount;
1369  total += amount;
1370  }
1371  }
1372  dot.SetDotIntensity(total);
1373  }
1374 
1375  m_DotsVector.push_back(dot);
1376  }
1377  }
1378  }
1379  }
1380 
1381  std::sort(m_DotsVector.begin(), m_DotsVector.end(), PlusFidDot::IntensityLessThan);
1383  {
1384  LOG_WARNING("Too many candidates found in segmentation. Consider reducing ROI. " << m_DotsVector.size() << " > " << m_NumberOfMaximumFiducialPointCandidates);
1386  tooManyCandidates = true;
1387  return false;
1388  }
1389  return true;
1390 }
1391 
1392 //-----------------------------------------------------------------------------
1393 
1395 {
1396  LOG_TRACE("FidSegmentation::MorphologicalOperations");
1397 
1398  // Constraint ROI according to the morphological bar size
1400 
1401  if (m_RegionOfInterest[2] == 0 || m_RegionOfInterest[3] == 0 ||
1404  {
1405  // the image is empty, nothing to process
1406  return;
1407  }
1408 
1409  // Morphological operations with a stick-like structuring element
1410  if (m_DebugOutput)
1411  {
1412  WritePng(m_Working, "seg01-initial.png", m_FrameSize[0], m_FrameSize[1]);
1413  }
1414 
1416  if (m_DebugOutput)
1417  {
1418  WritePng(m_Eroded, "seg02-morph-bar-deg0-erode.png", m_FrameSize[0], m_FrameSize[1]);
1419  }
1420 
1422  if (m_DebugOutput)
1423  {
1424  WritePng(m_Dilated, "seg03-morph-bar-deg0-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1425  }
1426 
1428  if (m_DebugOutput)
1429  {
1430  WritePng(m_Working, "seg04-morph-bar-deg0-final.png", m_FrameSize[0], m_FrameSize[1]);
1431  }
1432 
1434  if (m_DebugOutput)
1435  {
1436  WritePng(m_Eroded, "seg05-morph-bar-deg45-erode.png", m_FrameSize[0], m_FrameSize[1]);
1437  }
1438 
1440  if (m_DebugOutput)
1441  {
1442  WritePng(m_Dilated, "seg06-morph-bar-deg45-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1443  }
1444 
1446  if (m_DebugOutput)
1447  {
1448  WritePng(m_Working, "seg07-morph-bar-deg45-final.png", m_FrameSize[0], m_FrameSize[1]);
1449  }
1450 
1452  if (m_DebugOutput)
1453  {
1454  WritePng(m_Eroded, "seg08-morph-bar-deg90-erode.png", m_FrameSize[0], m_FrameSize[1]);
1455  }
1456 
1458  if (m_DebugOutput)
1459  {
1460  WritePng(m_Dilated, "seg09-morph-bar-deg90-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1461  }
1462 
1464  if (m_DebugOutput)
1465  {
1466  WritePng(m_Working, "seg10-morph-bar-deg90-final.png", m_FrameSize[0], m_FrameSize[1]);
1467  }
1468 
1470  if (m_DebugOutput)
1471  {
1472  WritePng(m_Eroded, "seg11-morph-bar-deg135-erode.png", m_FrameSize[0], m_FrameSize[1]);
1473  }
1474 
1476  if (m_DebugOutput)
1477  {
1478  WritePng(m_Dilated, "seg12-morph-bar-deg135-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1479  }
1480 
1482  if (m_DebugOutput)
1483  {
1484  WritePng(m_Working, "seg13-morph-bar-deg135-final.png", m_FrameSize[0], m_FrameSize[1]);
1485  }
1486 
1487  /* Circle operation. */
1489  if (m_DebugOutput)
1490  {
1491  WritePng(m_Eroded, "seg14-morph-circle-erode.png", m_FrameSize[0], m_FrameSize[1]);
1492  }
1493 
1495  if (m_DebugOutput)
1496  {
1497  WritePng(m_Working, "seg15-morph-circle-final.png", m_FrameSize[0], m_FrameSize[1]);
1498  }
1499 
1500 }
1501 
1502 //-----------------------------------------------------------------------------
1503 
1504 void PlusFidSegmentation::SetRegionOfInterest(unsigned int xMin, unsigned int yMin, unsigned int xMax, unsigned int yMax)
1505 {
1506  LOG_TRACE("FidSegmentation::SetRegionOfInterest(" << xMin << ", " << yMin << ", " << xMax << ", " << yMax << ")");
1507 
1508  if (xMin > 0)
1509  {
1510  m_RegionOfInterest[0] = xMin;
1511  }
1512  if (yMin > 0)
1513  {
1514  m_RegionOfInterest[1] = yMin;
1515  }
1516  if (xMax > 0)
1517  {
1518  m_RegionOfInterest[2] = xMax;
1519  }
1520  if (yMax > 0)
1521  {
1522  m_RegionOfInterest[3] = yMax;
1523  }
1524 }
1525 
1526 //-----------------------------------------------------------------------------
1527 
1529 {
1530  LOG_TRACE("FidSegmentation::ValidateRegionOfInterest");
1531 
1532  unsigned int barSize = GetMorphologicalOpeningBarSizePx();
1533 
1534  if (m_FrameSize[0] < barSize * 2 + 1 || m_FrameSize[1] < barSize * 2 + 1)
1535  {
1536  // image is too small
1537  m_RegionOfInterest[0] = 0;
1538  m_RegionOfInterest[1] = 0;
1539  m_RegionOfInterest[2] = 0;
1540  m_RegionOfInterest[3] = 0;
1541  return;
1542  }
1543 
1545  {
1546  std::swap(m_RegionOfInterest[0], m_RegionOfInterest[2]);
1547  }
1549  {
1550  std::swap(m_RegionOfInterest[1], m_RegionOfInterest[3]);
1551  }
1552 
1553  // xmin
1554  if (m_RegionOfInterest[0] - barSize <= 0)
1555  {
1556  m_RegionOfInterest[0] = barSize + 1;
1557  }
1558  if (m_RegionOfInterest[0] + barSize >= m_FrameSize[0])
1559  {
1560  m_RegionOfInterest[0] = m_FrameSize[0] - barSize - 1;
1561  }
1562 
1563  // xmax
1565  {
1567  }
1568  if (m_RegionOfInterest[2] + barSize >= m_FrameSize[0])
1569  {
1570  m_RegionOfInterest[2] = m_FrameSize[0] - barSize - 1;
1571  }
1572 
1573  // ymin
1574  if (m_RegionOfInterest[1] - barSize <= 0)
1575  {
1576  m_RegionOfInterest[1] = barSize + 1;
1577  }
1578  if (m_RegionOfInterest[1] + barSize >= m_FrameSize[1])
1579  {
1580  m_RegionOfInterest[1] = m_FrameSize[1] - barSize - 1;
1581  }
1582 
1583  // ymax
1585  {
1587  }
1588  if (m_RegionOfInterest[3] + barSize >= m_FrameSize[1])
1589  {
1590  m_RegionOfInterest[3] = m_FrameSize[1] - barSize - 1;
1591  }
1592 }
1593 
1594 //-----------------------------------------------------------------------------
1595 void PlusFidSegmentation::GetRegionOfInterest(unsigned int& xMin, unsigned int& yMin, unsigned int& xMax, unsigned int& yMax)
1596 {
1597  xMin = m_RegionOfInterest[0];
1598  yMin = m_RegionOfInterest[1];
1599  xMax = m_RegionOfInterest[2];
1600  yMax = m_RegionOfInterest[3];
1601 }
1602 
1603 //-----------------------------------------------------------------------------
1605 {
1607 }
1608 
1609 //-----------------------------------------------------------------------------
1611 {
1612  m_FiducialGeometry = geometryType;
1613 }
bool ShapeContains(std::vector< PlusCoordinate2D > &shape, PlusCoordinate2D point)
static void WritePng(PlusFidSegmentation::PixelType *modifiedImage, std::string outImageName, int cols, int rows)
unsigned int m_NumberOfMaximumFiducialPointCandidates
static const double DEFAULT_MAX_ANGLE_DIFFERENCE_DEGREES
unsigned int GetMorphologicalOpeningBarSizePx()
PlusFidSegmentation::PixelType * m_Dilated
void SetPossibleFiducialsImageFilename(std::string value)
std::vector< PlusFidDot > m_DotsVector
static const double DEFAULT_THRESHOLD_IMAGE_PERCENT
void ClusteringAddNeighbors(PlusFidSegmentation::PixelType *image, int r, int c, std::vector< PlusFidDot > &m_Test, std::vector< PlusFidDot > &m_Set, std::vector< PlusFidSegmentation::PixelType > &m_Vals)
void SetNumberOfMaximumFiducialPointCandidates(int aValue)
static const short MAX_CLUSTER_VALS
double m_MorphologicalOpeningCircleRadiusMm
void ErodeCircle(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
void Subtract(PlusFidSegmentation::PixelType *image, PlusFidSegmentation::PixelType *vals)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
void SetUseOriginalImageIntensityForDotIntensityScore(bool value)
void GetRegionOfInterest(unsigned int &xMin, unsigned int &yMin, unsigned int &xMax, unsigned int &yMax)
PlusFidSegmentation::PixelType DilatePoint90(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
PlusFidSegmentation::PixelType ErodePoint45(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
void Dilate45(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
void WritePossibleFiducialOverlayImage(const std::vector< std::vector< double > > &fiducials, PlusFidSegmentation::PixelType *unalteredImage, const char *namePrefix, int frameIndex)
PlusFidSegmentation::PixelType ErodePoint0(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
for i
void SetFiducialGeometry(FiducialGeometryType geometryType)
bool AcceptDot(PlusFidDot &dot)
std::string m_PossibleFiducialsImageFilename
static const double DEFAULT_MAX_LINE_PAIR_DISTANCE_ERROR_PERCENT
static const double DEFAULT_MORPHOLOGICAL_OPENING_CIRCLE_RADIUS_MM
PlusFidSegmentation::PixelType DilatePoint45(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
PlusFidSegmentation::PixelType ErodePoint135(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
std::array< unsigned int, 4 > m_RegionOfInterest
PlusFidSegmentation::PixelType * m_UnalteredImage
double m_ImageNormalVectorInPhantomFrameEstimation[3]
void Dilate135(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
Image slice number p
Definition: algo4.m:14
iter
Definition: algo3.m:29
static const double DEFAULT_MAX_LINE_SHIFT_MM
static const int DEFAULT_CLIP_ORIGIN[2]
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
bool m_UseOriginalImageIntensityForDotIntensityScore
double m_ImageToPhantomTransform[16]
double m_ImageScalingTolerancePercent[4]
static const double DEFAULT_APPROXIMATE_SPACING_MM_PER_PIXEL
PlusFidSegmentation::PixelType DilatePoint(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic, PlusCoordinate2D *shape, int slen)
static const short MIN_WINDOW_DIST
static const double DEFAULT_ANGLE_TOLERANCE_DEGREES
void Erode135(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
static const double DEFAULT_COLLINEAR_POINTS_MAX_DISTANCE_FROM_LINE_MM
const char * start
Definition: phidget22.h:5116
int x
Definition: phidget22.h:4265
bool Cluster(bool &tooManyCandidates)
void DilateCircle(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
std::vector< PlusCoordinate2D > m_MorphologicalCircle
double m_ImageNormalVectorInPhantomFrameMaximumRotationAngleDeg[6]
PlusFidSegmentation::PixelType DilatePoint135(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
void Dilate90(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
Phidget_ChannelClass uint32_t * count
Definition: phidget22.h:1321
static const int DEFAULT_NUMBER_OF_MAXIMUM_FIDUCIAL_POINT_CANDIDATES
void Dilate0(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
static const double DEFAULT_MAX_THETA_DEGREES
static void SetDefaultSegmentationParameters(vtkXMLDataElement *segmentationElement)
void Erode0(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
void Erode90(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)
static const double DEFAULT_MORPHOLOGICAL_OPENING_BAR_SIZE_MM
static bool IntensityLessThan(const PlusFidDot &dot1, const PlusFidDot &dot2)
FiducialGeometryType m_FiducialGeometry
Direction vectors of rods y
Definition: algo3.m:15
PlusStatus ReadConfiguration(vtkXMLDataElement *rootConfigElement)
void SetFrameSize(const FrameSizeType &frameSize)
PlusFidSegmentation::PixelType DilatePoint0(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
static const short BLACK
static const char * DEFAULT_USE_ORIGINAL_IMAGE_INTENSITY_FOR_DOT_INTENSITY_SCORE
static const double DEFAULT_MIN_THETA_DEGREES
void Suppress(PlusFidSegmentation::PixelType *image, double percent_thresh)
void SetRegionOfInterest(unsigned int xMin, unsigned int yMin, unsigned int xMax, unsigned int yMax)
void SetDotIntensity(double value)
PlusFidSegmentation::PixelType * m_Eroded
PlusFidSegmentation::PixelType ErodePoint90(PlusFidSegmentation::PixelType *image, unsigned int ir, unsigned int ic)
std::vector< PlusFidDot > m_CandidateFidValues
static const int DEFAULT_CLIP_SIZE[2]
PlusFidSegmentation::PixelType * m_Working
void Erode45(PlusFidSegmentation::PixelType *dest, PlusFidSegmentation::PixelType *image)