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] + clampMinBarSize(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 + clampMinBarSize(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 
369  unsigned int p = (ir + barSize) * m_FrameSize[0] + clampMinBarSize(ic, barSize);
370  unsigned int p_max = clampMinBarSize(ir, barSize) * m_FrameSize[0] + ic + barSize;
371 
372  for (; p >= p_max; p = p - m_FrameSize[0] + 1)
373  {
374  if (image[p] < dval)
375  {
376  dval = image[p];
377  }
378  if (image[p] == 0)
379  {
380  break;
381  }
382  }
383  return dval;
384 }
385 
386 //-----------------------------------------------------------------------------
387 
389 {
390  //LOG_TRACE("FidSegmentation::Erode45");
391 
392  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
393  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
394 
395  /* Down the left side. */
396  for (unsigned int sr = m_RegionOfInterest[1]; sr < m_RegionOfInterest[3]; sr++)
397  {
398  unsigned int ir = sr;
399  unsigned int ic = m_RegionOfInterest[0];
400 
401  PlusFidSegmentation::PixelType dval = ErodePoint45(image, ir, ic);
402  dest[ir * m_FrameSize[0] + ic] = dval;
403 
404  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
405  {
406  PlusFidSegmentation::PixelType new_val = image[ clampMinBarSize(ir, barSize) * m_FrameSize[0] + (ic + barSize)];
407  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
408 
409  dval = new_val <= dval ? new_val :
410  del_val > dval ? std::min(dval, new_val) :
411  ErodePoint45(image, ir, ic);
412 
413  dest[ir * m_FrameSize[0] + ic] = dval;
414  }
415  }
416 
417  /* Across the bottom */
418  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
419  {
420  unsigned int ic = sc;
421  unsigned int ir = m_RegionOfInterest[3] - 1;
422 
423  PlusFidSegmentation::PixelType dval = ErodePoint45(image, ir, ic);
424  dest[ir * m_FrameSize[0] + ic] = dval;
425 
426  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
427  {
428  PlusFidSegmentation::PixelType new_val = image[clampMinBarSize(ir, barSize) * m_FrameSize[0] + (ic + barSize)];
429  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
430 
431  dval = new_val <= dval ? new_val :
432  del_val > dval ? std::min(dval, new_val) :
433  ErodePoint45(image, ir, ic);
434 
435  dest[ir * m_FrameSize[0] + ic] = dval;
436  }
437  }
438 }
439 
440 //-----------------------------------------------------------------------------
441 
443 {
444  //LOG_TRACE("FidSegmentation::ErodePoint90");
445 
446  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
447  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
448  unsigned int p = clampMinBarSize(ir, barSize) * m_FrameSize[0] + ic;
449  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic;
450 
451  for (; p <= p_max; p += m_FrameSize[0])
452  {
453  if (image[p] < dval)
454  {
455  dval = image[p];
456  }
457  if (image[p] == 0)
458  {
459  break;
460  }
461  }
462  return dval;
463 }
464 
465 //-----------------------------------------------------------------------------
466 
468 {
469  //LOG_TRACE("FidSegmentation::Erode90");
470 
471  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
472 
473  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
474 
475  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
476  {
477  unsigned int ir = m_RegionOfInterest[1];
478 
479  PlusFidSegmentation::PixelType dval = ErodePoint90(image, ir, ic);
480  dest[ir * m_FrameSize[0] + ic] = dval;
481 
482  for (ir++; ir < m_RegionOfInterest[3]; ir++)
483  {
484  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + ic];
485  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + ic];
486 
487  dval = new_val <= dval ? new_val :
488  del_val > dval ? std::min(dval, new_val) :
489  ErodePoint90(image, ir, ic);
490 
491  dest[ir * m_FrameSize[0] + ic] = dval;
492  }
493  }
494 }
495 
496 //-----------------------------------------------------------------------------
497 
499 {
500  //LOG_TRACE("FidSegmentation::ErodePoint135");
501 
502  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
503  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
504  unsigned int p = clampMinBarSize(ir, barSize) * m_FrameSize[0] + clampMinBarSize(ic, barSize);
505  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic + barSize;
506 
507  for (; p <= p_max; p = p + m_FrameSize[0] + 1)
508  {
509  if (image[p] < dval)
510  {
511  dval = image[p];
512  }
513  if (image[p] == 0)
514  {
515  break;
516  }
517  }
518  return dval;
519 }
520 
521 //-----------------------------------------------------------------------------
522 
524 {
525  //LOG_TRACE("FidSegmentation::Erode135");
526 
527  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
528 
529  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
530 
531  /* Up the left side. */
532  for (unsigned int sr = m_RegionOfInterest[3] - 1; sr >= m_RegionOfInterest[1]; sr--)
533  {
534  unsigned int ir = sr;
535  unsigned int ic = m_RegionOfInterest[0];
536 
537  PlusFidSegmentation::PixelType dval = ErodePoint135(image, ir, ic);
538  dest[ir * m_FrameSize[0] + ic] = dval;
539 
540  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
541  {
542  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
543  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
544 
545  dval = new_val <= dval ? new_val :
546  del_val > dval ? std::min(dval, new_val) :
547  ErodePoint135(image, ir, ic);
548 
549  dest[ir * m_FrameSize[0] + ic] = dval;
550  }
551  }
552 
553  /* Across the top. */
554  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
555  {
556  unsigned int ic = sc;
557  unsigned int ir = m_RegionOfInterest[1];
558 
559  PlusFidSegmentation::PixelType dval = ErodePoint135(image, ir, ic);
560  dest[ir * m_FrameSize[0] + ic] = dval;
561 
562  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
563  {
564  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
565  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
566 
567  dval = new_val <= dval ? new_val :
568  del_val > dval ? std::min(dval, new_val) :
569  ErodePoint135(image, ir, ic);
570 
571  dest[ir * m_FrameSize[0] + ic] = dval;
572  }
573  }
574 }
575 
576 //-----------------------------------------------------------------------------
577 
579 {
580  //LOG_TRACE("FidSegmentation::ErodeCircle");
581 
582  unsigned int slen = m_MorphologicalCircle.size();
583 
584  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
585 
586  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
587  {
588  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
589  {
590  PlusFidSegmentation::PixelType dval = UCHAR_MAX;
591  for (unsigned int sp = 0; sp < slen; sp++)
592  {
593  int sr = ir + m_MorphologicalCircle[sp].X;
594  int sc = ic + m_MorphologicalCircle[sp].Y;
595  PlusFidSegmentation::PixelType pixSrc = image[sr * m_FrameSize[0] + sc];
596 
597  if (pixSrc < dval)
598  {
599  dval = pixSrc;
600  }
601 
602  if (pixSrc == 0)
603  {
604  break;
605  }
606  }
607 
608  dest[ir * m_FrameSize[0] + ic] = dval;
609  }
610  }
611 }
612 
613 //-----------------------------------------------------------------------------
614 
616 {
617  //LOG_TRACE("FidSegmentation::DilatePoint0");
618 
619  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
621  unsigned int p = ir * m_FrameSize[0] + clampMinBarSize(ic, barSize);
622  unsigned int p_max = ir * m_FrameSize[0] + ic + barSize;
623 
624  for (; p <= p_max; p++)
625  {
626  if (image[p] > dval)
627  {
628  dval = image[p];
629  }
630  }
631 
632  return dval;
633 }
634 
635 //-----------------------------------------------------------------------------
636 
638 {
639  //LOG_TRACE("FidSegmentation::Dilate0");
640 
641  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
642 
643  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
644 
645  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
646  {
647  unsigned int ic = m_RegionOfInterest[0];
648  unsigned int p_base = ir * m_FrameSize[0];
649 
650  PlusFidSegmentation::PixelType dval = DilatePoint0(image, ir, ic);
651  dest[ir * m_FrameSize[0] + ic] = dval;
652 
653  for (ic++; ic < m_RegionOfInterest[2]; ic++)
654  {
655  PlusFidSegmentation::PixelType new_val = image[p_base + ic + barSize];
656  PlusFidSegmentation::PixelType del_val = image[p_base + clampMinBarSize(ic - 1, barSize)];
657 
658  dval = new_val >= dval ? new_val :
659  (del_val < dval ? std::max(dval, new_val) :
660  DilatePoint0(image, ir, ic));
661  dest[ir * m_FrameSize[0] + ic] = dval;
662  }
663  }
664 }
665 
666 //-----------------------------------------------------------------------------
667 
669 {
670  //LOG_TRACE("FidSegmentation::DilatePoint45");
671 
672  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
674  unsigned int p = (ir + barSize) * m_FrameSize[0] + clampMinBarSize(ic, barSize);
675  unsigned int p_max = clampMinBarSize(ir, barSize) * m_FrameSize[0] + ic + barSize;
676 
677  while (p >= p_max)
678  {
679  if (image[p] > dval)
680  {
681  dval = image[p];
682  }
683  if (p - m_FrameSize[0] + 1 > p)
684  {
685  // unsigned int underflow, adjust
686  p = 0;
687  }
688  else
689  {
690  p = p - m_FrameSize[0] + 1;
691  }
692  }
693 
694  return dval;
695 }
696 
697 inline unsigned int PlusFidSegmentation::clampMinBarSize(unsigned int ir, unsigned int barSize) {
698  int irMinusBar = (int)ir - (int)barSize;
699  return (unsigned int)irMinusBar < 0 ? 0 : irMinusBar;
700 }
701 
702 //-----------------------------------------------------------------------------
703 
705 {
706  //LOG_TRACE("FidSegmentation::Dilate45");
707 
708  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
709  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
710 
711  /* Down the left side. */
712  for (unsigned int sr = m_RegionOfInterest[1]; sr < m_RegionOfInterest[3]; sr++)
713  {
714  unsigned int ir = sr;
715  unsigned int ic = m_RegionOfInterest[0];
716 
717 
718  PlusFidSegmentation::PixelType dval = DilatePoint45(image, ir, ic);
719  dest[ir * m_FrameSize[0] + ic] = dval ;
720  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
721  {
722  PlusFidSegmentation::PixelType new_val = image[clampMinBarSize(ir, barSize) * m_FrameSize[0] + (ic + barSize)];
723  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
724 
725  dval = new_val >= dval ? new_val :
726  (del_val < dval ? std::max(dval, new_val) :
727  DilatePoint45(image, ir, ic));
728  dest[ir * m_FrameSize[0] + ic] = dval ;
729  }
730  }
731 
732  /* Across the bottom */
733  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
734  {
735  unsigned int ic = sc;
736  unsigned int ir = m_RegionOfInterest[3] - 1;
737 
738  PlusFidSegmentation::PixelType dval = DilatePoint45(image, ir, ic);
739  dest[ir * m_FrameSize[0] + ic] = dval ;
740  for (ir--, ic++; ir >= m_RegionOfInterest[1] && ic < m_RegionOfInterest[2]; ir--, ic++)
741  {
742  PlusFidSegmentation::PixelType new_val = image[clampMinBarSize(ir, barSize) * m_FrameSize[0] + (ic + barSize)];
743  PlusFidSegmentation::PixelType del_val = image[(ir + 1 + barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
744 
745  dval = new_val >= dval ? new_val :
746  (del_val < dval ? std::max(dval, new_val) :
747  DilatePoint45(image, ir, ic));
748  dest[ir * m_FrameSize[0] + ic] = dval ;
749  }
750  }
751 }
752 
753 //-----------------------------------------------------------------------------
754 
756 {
757  //LOG_TRACE("FidSegmentation::DilatePoint90");
758 
759  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
761  unsigned int p = clampMinBarSize(ir, barSize) * m_FrameSize[0] + ic;
762  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic;
763 
764  for (; p <= p_max; p += m_FrameSize[0])
765  {
766  if (image[p] > dval)
767  {
768  dval = image[p];
769  }
770  }
771  return dval;
772 }
773 
774 //-----------------------------------------------------------------------------
775 
777 {
778  //LOG_TRACE("FidSegmentation::Dilate90");
779 
780  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
781  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
782 
783  for (unsigned int ic = m_RegionOfInterest[0]; ic < m_RegionOfInterest[2]; ic++)
784  {
785  unsigned int ir = m_RegionOfInterest[1];
786 
787  PlusFidSegmentation::PixelType dval = DilatePoint90(image, ir, ic);
788  dest[ir * m_FrameSize[0] + ic] = dval ;
789  for (ir++; ir < m_RegionOfInterest[3]; ir++)
790  {
791  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + ic];
792  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + ic];
793 
794  dval = new_val >= dval ? new_val :
795  (del_val < dval ? std::max(dval, new_val) :
796  DilatePoint90(image, ir, ic));
797 
798  dest[ir * m_FrameSize[0] + ic] = dval ;
799  }
800  }
801 }
802 
803 //-----------------------------------------------------------------------------
804 
806 {
807  //LOG_TRACE("FidSegmentation::DilatePoint135");
808 
809  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
811  unsigned int p = clampMinBarSize(ir, barSize) * m_FrameSize[0] + clampMinBarSize(ic, barSize);
812  unsigned int p_max = (ir + barSize) * m_FrameSize[0] + ic + barSize;
813 
814  for (; p <= p_max; p = p + m_FrameSize[0] + 1)
815  {
816  if (image[p] > dval)
817  {
818  dval = image[p];
819  }
820  }
821  return dval;
822 }
823 
824 //-----------------------------------------------------------------------------
825 
827 {
828  //LOG_TRACE("FidSegmentation::Dilate135");
829 
830  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
831  const unsigned int barSize = GetMorphologicalOpeningBarSizePx();
832 
833  /* Up the left side. */
834  for (unsigned int sr = m_RegionOfInterest[3] - 1; sr >= m_RegionOfInterest[1]; sr--)
835  {
836  unsigned int ir = sr;
837  unsigned int ic = m_RegionOfInterest[0];
838 
839  PlusFidSegmentation::PixelType dval = DilatePoint135(image, ir, ic);
840  dest[ir * m_FrameSize[0] + ic] = dval ;
841  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
842  {
843  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
844  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
845 
846  dval = new_val >= dval ? new_val :
847  (del_val < dval ? std::max(dval, new_val) :
848  DilatePoint135(image, ir, ic));
849  dest[ir * m_FrameSize[0] + ic] = dval;
850  }
851  }
852 
853  /* Across the top. */
854  for (unsigned int sc = m_RegionOfInterest[0]; sc < m_RegionOfInterest[2]; sc++)
855  {
856  unsigned int ic = sc;
857  unsigned int ir = m_RegionOfInterest[1];
858 
859  PlusFidSegmentation::PixelType dval = DilatePoint135(image, ir, ic);
860  dest[ir * m_FrameSize[0] + ic] = dval;
861  for (ir++, ic++; ir < m_RegionOfInterest[3] && ic < m_RegionOfInterest[2]; ir++, ic++)
862  {
863  PlusFidSegmentation::PixelType new_val = image[(ir + barSize) * m_FrameSize[0] + (ic + barSize)];
864  PlusFidSegmentation::PixelType del_val = image[clampMinBarSize(ir -1, barSize) * m_FrameSize[0] + clampMinBarSize(ic -1, barSize)];
865 
866  dval = new_val >= dval ? new_val :
867  (del_val < dval ? std::max(dval, new_val) :
868  DilatePoint135(image, ir, ic));
869  dest[ir * m_FrameSize[0] + ic] = dval;
870  }
871  }
872 }
873 
874 //-----------------------------------------------------------------------------
875 
877 {
878  //LOG_TRACE("FidSegmentation::DilatePoint");
879 
881  for (int sp = 0; sp < slen; sp++)
882  {
883  unsigned int sr = ir + shape[sp].Y;
884  unsigned int sc = ic + shape[sp].X;
885 
886  if (image[sr * m_FrameSize[0] + sc] > dval)
887  {
888  dval = image[sr * m_FrameSize[0] + sc];
889  }
890  }
891  return dval;
892 }
893 
894 //-----------------------------------------------------------------------------
895 
897 {
898  //LOG_TRACE("FidSegmentation::DilateCircle");
899 
900  unsigned int slen = m_MorphologicalCircle.size();
901 
902  PlusCoordinate2D* shape = new PlusCoordinate2D[slen];
903 
904  for (unsigned int i = 0; i < slen; i++)
905  {
906  shape[i] = m_MorphologicalCircle[i];
907  }
908 
909  /* Which elements stick around when you shift right? */
910  int n = 0;
911 
912  bool* sr_exist = new bool[slen];
913 
914  memset(sr_exist, 0, slen * sizeof(bool));
915  for (unsigned int si = 0; si < slen; si++)
916  {
917  PlusCoordinate2D dot;
918  dot.X = m_MorphologicalCircle[si].X + 1;
919  dot.Y = m_MorphologicalCircle[si].Y;
920 
922  {
923  sr_exist[si] = true, n++;
924  }
925  }
926  //cout << "shift_exist: " << n << endl;
927 
928  PlusCoordinate2D* newDots = new PlusCoordinate2D[slen];
929  PlusCoordinate2D* oldDots = new PlusCoordinate2D[slen];
930 
931  int nNewDots = 0, nOldDots = 0;
932  for (unsigned int si = 0; si < slen; si++)
933  {
934  if (sr_exist[si])
935  {
936  oldDots[nOldDots++] = shape[si];
937  }
938  else
939  {
940  newDots[nNewDots++] = shape[si];
941  }
942  }
943 
944  delete [] sr_exist;
945 
946  memset(dest, 0, m_FrameSize[1]*m_FrameSize[0]*sizeof(PlusFidSegmentation::PixelType));
947  for (unsigned int ir = m_RegionOfInterest[1]; ir < m_RegionOfInterest[3]; ir++)
948  {
949  unsigned int ic = m_RegionOfInterest[0];
950 
951  PlusFidSegmentation::PixelType dval = DilatePoint(image, ir, ic, shape, slen);
952  PlusFidSegmentation::PixelType last = dest[ir * m_FrameSize[0] + ic] = dval;
953 
954  for (ic++; ic < m_RegionOfInterest[2]; ic++)
955  {
956  PlusFidSegmentation::PixelType dval = DilatePoint(image, ir, ic, newDots, nNewDots);
957 
958  if (dval < last)
959  {
960  for (int sp = 0; sp < nOldDots; sp++)
961  {
962  unsigned int sr = ir + oldDots[sp].Y;
963  unsigned int sc = ic + oldDots[sp].X;
964  if (image[sr * m_FrameSize[0] + sc] > dval)
965  {
966  dval = image[sr * m_FrameSize[0] + sc];
967  }
968  if (image[sr * m_FrameSize[0] + sc] == last)
969  {
970  break;
971  }
972  }
973  }
974  last = dest[ir * m_FrameSize[0] + ic] = dval ;
975  }
976  }
977  delete [] newDots;
978  delete [] oldDots;
979 }
980 
981 //-----------------------------------------------------------------------------
982 
983 bool PlusFidSegmentation::ShapeContains(std::vector<PlusCoordinate2D>& shape, PlusCoordinate2D point)
984 {
985  //LOG_TRACE("FidSegmentation::ShapeContains");
986 
987  for (unsigned int si = 0; si < shape.size(); si++)
988  {
989  if (shape[si] == point)
990  {
991  return true;
992  }
993  }
994  return false;
995 }
996 
997 //-----------------------------------------------------------------------------
998 
999 void PlusFidSegmentation::WritePng(PlusFidSegmentation::PixelType* modifiedImage, std::string outImageName, int cols, int rows)
1000 {
1001  //LOG_TRACE("FidSegmentation::WritePng");
1002 
1003  // output intermediate image
1004  const unsigned int Dimension = 2;
1005 
1006  typedef itk::Image< PlusFidSegmentation::PixelType, Dimension > ImageType;
1007  auto modImage = ImageType::New();
1008  ImageType::SizeType size;
1009  size[0] = cols;
1010  size[1] = rows;
1011 
1012  ImageType::IndexType start;
1013  start[0] = 0;
1014  start[1] = 0;
1015 
1016  ImageType::RegionType wholeImage;
1017  wholeImage.SetSize(size);
1018  wholeImage.SetIndex(start);
1019 
1020  modImage->SetRegions(wholeImage);
1021  modImage->Allocate();
1022 
1023  typedef itk::ImageFileWriter< ImageType > WriterType;
1024  auto pngImageIO = itk::PNGImageIO::New();
1025  pngImageIO->SetCompressionLevel(0);
1026 
1027  auto writer = WriterType::New();
1028  writer->SetImageIO(pngImageIO);
1029  writer->SetFileName(outImageName);
1030 
1031 
1032  typedef itk::ImageRegionIterator<ImageType> IterType;
1033  IterType iter(modImage, modImage->GetRequestedRegion());
1034  iter.GoToBegin();
1035 
1036  int count = 0;
1037 
1038  while (!iter.IsAtEnd())
1039  {
1040  iter.Set(modifiedImage[count]);
1041  count++;
1042  ++iter;
1043  }
1044 
1045  writer->SetInput(modImage); // piping output of reader into input of writer
1046 
1047  try
1048  {
1049  writer->Update(); // change to writing if want writing feature
1050  }
1051  catch (itk::ExceptionObject& err)
1052  {
1053  LOG_ERROR("Exception! writer did not update"); //ditto
1054  LOG_ERROR(err);
1055  }
1056  // end output
1057 
1058 }
1059 
1060 //-----------------------------------------------------------------------------
1061 void PlusFidSegmentation::SetDefaultSegmentationParameters(vtkXMLDataElement* segmentationDataElement)
1062 {
1063  if (!segmentationDataElement)
1064  {
1065  return;
1066  }
1067 
1068  segmentationDataElement->SetName("Segmentation");
1069  segmentationDataElement->SetDoubleAttribute("ApproximateSpacingMmPerPixel", DEFAULT_APPROXIMATE_SPACING_MM_PER_PIXEL);
1070  segmentationDataElement->SetDoubleAttribute("MorphologicalOpeningCircleRadiusMm", DEFAULT_MORPHOLOGICAL_OPENING_CIRCLE_RADIUS_MM);
1071  segmentationDataElement->SetDoubleAttribute("MorphologicalOpeningBarSizeMm", DEFAULT_MORPHOLOGICAL_OPENING_BAR_SIZE_MM);
1072  segmentationDataElement->SetDoubleAttribute("MaxLinePairDistanceErrorPercent", DEFAULT_MAX_LINE_PAIR_DISTANCE_ERROR_PERCENT);
1073  segmentationDataElement->SetDoubleAttribute("AngleToleranceDegrees", DEFAULT_ANGLE_TOLERANCE_DEGREES);
1074  segmentationDataElement->SetDoubleAttribute("MaxAngleDifferenceDegrees", DEFAULT_MAX_ANGLE_DIFFERENCE_DEGREES);
1075  segmentationDataElement->SetDoubleAttribute("MinThetaDegrees", DEFAULT_MIN_THETA_DEGREES);
1076  segmentationDataElement->SetDoubleAttribute("MaxThetaDegrees", DEFAULT_MAX_THETA_DEGREES);
1077  segmentationDataElement->SetDoubleAttribute("MaxLineShiftMm", DEFAULT_MAX_LINE_SHIFT_MM);
1078  segmentationDataElement->SetDoubleAttribute("ThresholdImagePercent", DEFAULT_THRESHOLD_IMAGE_PERCENT);
1079  segmentationDataElement->SetDoubleAttribute("CollinearPointsMaxDistanceFromLineMm", DEFAULT_COLLINEAR_POINTS_MAX_DISTANCE_FROM_LINE_MM);
1080  segmentationDataElement->SetAttribute("UseOriginalImageIntensityForDotIntensityScore", DEFAULT_USE_ORIGINAL_IMAGE_INTENSITY_FOR_DOT_INTENSITY_SCORE);
1081  segmentationDataElement->SetDoubleAttribute("NumberOfMaximumFiducialPointCandidates", DEFAULT_NUMBER_OF_MAXIMUM_FIDUCIAL_POINT_CANDIDATES);
1082 
1083  std::stringstream clipOriginSS;
1084  clipOriginSS << DEFAULT_CLIP_ORIGIN[0] << " " << DEFAULT_CLIP_ORIGIN[1];
1085  std::string clipOriginString = clipOriginSS.str();
1086  segmentationDataElement->SetAttribute("ClipRectangleOrigin", clipOriginString.c_str());
1087 
1088  std::stringstream clipSizeSS;
1089  clipSizeSS << DEFAULT_CLIP_SIZE[0] << " " << DEFAULT_CLIP_SIZE[1];
1090  std::string clipSizeString = clipSizeSS.str();
1091  segmentationDataElement->SetAttribute("ClipRectangleSize", clipSizeString.c_str());
1092 }
1093 
1094 //-----------------------------------------------------------------------------
1095 
1096 void PlusFidSegmentation::WritePossibleFiducialOverlayImage(const std::vector<PlusFidDot>& fiducials, PlusFidSegmentation::PixelType* unalteredImage, const char* namePrefix, int frameIndex)
1097 {
1098  //LOG_TRACE("FidSegmentation::WritePossibleFiducialOverlayImage");
1099 
1100  typedef itk::RGBPixel< unsigned char > ColorPixelType;
1101  typedef itk::Image< ColorPixelType, 2 > ImageType;
1102 
1103  auto possibleFiducials = ImageType::New();
1104 
1105  ImageType::SizeType size;
1106  size[0] = m_FrameSize[0];
1107  size[1] = m_FrameSize[1];
1108 
1109  ImageType::IndexType start;
1110  start[0] = 0;
1111  start[1] = 0;
1112 
1113  ImageType::RegionType wholeImage;
1114  wholeImage.SetSize(size);
1115  wholeImage.SetIndex(start);
1116 
1117  possibleFiducials->SetRegions(wholeImage);
1118  possibleFiducials->Allocate();
1119 
1120  ImageType::IndexType pixelLocation;
1121 
1122  ImageType::PixelType pixelValue;
1123 
1124  // copy pixel by pixel (we need to do gray->RGB conversion and only a ROI is updated)
1125  for (unsigned int r = m_RegionOfInterest[1]; r < m_RegionOfInterest[3]; r++)
1126  {
1127  for (unsigned int c = m_RegionOfInterest[0]; c < m_RegionOfInterest[2]; c++)
1128  {
1129  pixelValue[0] = 0; //unalteredImage[r*cols+c];
1130  pixelValue[1] = unalteredImage[r * m_FrameSize[0] + c];
1131  pixelValue[2] = unalteredImage[r * m_FrameSize[0] + c];
1132  pixelLocation[0] = c;
1133  pixelLocation[1] = r;
1134  possibleFiducials->SetPixel(pixelLocation, pixelValue);
1135  }
1136  }
1137 
1138  // Set pixelValue to red (it will be used to mark the centroid of the clusters)
1139  for (unsigned int numDots = 0; numDots < fiducials.size(); numDots++)
1140  {
1141  const int markerPosCount = 5;
1142  const int markerPos[markerPosCount][2] = {{0, 0}, { +1, 0}, { -1, 0}, {0, +1}, {0, -1}};
1143 
1144  for (int i = 0; i < markerPosCount; i++)
1145  {
1146  pixelLocation[0] = fiducials[numDots].GetX() + markerPos[i][0];
1147  pixelLocation[1] = fiducials[numDots].GetY() + markerPos[i][1];
1148  int clusterMarkerIntensity = fiducials[numDots].GetDotIntensity() * 10;
1149  if (clusterMarkerIntensity > 255)
1150  {
1151  clusterMarkerIntensity = 255;
1152  }
1153  pixelValue[0] = clusterMarkerIntensity;
1154  pixelValue[1] = 0;
1155  pixelValue[2] = 0;
1156  possibleFiducials->SetPixel(pixelLocation, pixelValue);
1157  }
1158  }
1159  std::ostringstream possibleFiducialsImageFilename;
1160  possibleFiducialsImageFilename << namePrefix << std::setw(3) << std::setfill('0') << frameIndex << ".bmp" << std::ends;
1161 
1162  SetPossibleFiducialsImageFilename(possibleFiducialsImageFilename.str());
1163 
1164  typedef itk::ImageFileWriter< ImageType > WriterType;
1165  auto writeImage = WriterType::New();
1166  writeImage->SetFileName(m_PossibleFiducialsImageFilename);
1167 
1168  writeImage->SetInput(possibleFiducials);
1169 
1170  try
1171  {
1172  writeImage->Update();
1173  }
1174  catch (itk::ExceptionObject& err)
1175  {
1176  LOG_ERROR("Exception! writer did not update");
1177  LOG_ERROR(err);
1178  }
1179 }
1180 
1181 //-----------------------------------------------------------------------------
1182 
1183 void PlusFidSegmentation::WritePossibleFiducialOverlayImage(const std::vector< std::vector<double> >& fiducials, PlusFidSegmentation::PixelType* unalteredImage, const char* namePrefix, int frameIndex)
1184 {
1185  std::vector<PlusFidDot> dots;
1186  for (unsigned int numDots = 0; numDots < fiducials.size(); numDots++)
1187  {
1188  PlusFidDot newDot;
1189  newDot.SetX(fiducials[numDots][0]);
1190  newDot.SetY(fiducials[numDots][1]);
1191  newDot.SetDotIntensity(10.0);
1192  dots.push_back(newDot);
1193  }
1194  WritePossibleFiducialOverlayImage(dots, unalteredImage, namePrefix, frameIndex);
1195 }
1196 
1197 //-----------------------------------------------------------------------------
1198 
1200 {
1201  //LOG_TRACE("FidSegmentation::Subtract");
1202 
1203  for (unsigned int pos = m_FrameSize[1] * m_FrameSize[0]; pos > 0; pos--)
1204  {
1205  *image = *vals > *image ? 0 : *image - *vals;
1206  image++;
1207  vals++;
1208  }
1209 }
1210 
1211 //-----------------------------------------------------------------------------
1212 
1213 /* Possible additional criteria:
1214  * 1. Track the frame-to-frame data?
1215  * 2. Lines should be roughly of the same length? */
1216 
1218 {
1219  LOG_TRACE("FidSegmentation::Suppress");
1220 
1221  // Get the minimum and maximum pixel value
1224  PlusFidSegmentation::PixelType* pix = image;
1225  for (unsigned int pos = 0; pos < m_FrameSize[0]*m_FrameSize[1]; pos ++)
1226  {
1227  if (*pix > max)
1228  {
1229  max = *pix;
1230  }
1231  if (*pix < min)
1232  {
1233  min = *pix;
1234  }
1235  pix++;
1236  }
1237 
1238  //We use floor to calculate the round value here.
1239 
1240  PlusFidSegmentation::PixelType thresh = min + (PlusFidSegmentation::PixelType)floor((double)(max - min) * percent_thresh + 0.5);
1241 
1242  //thresholding
1243  int pixelCount = m_FrameSize[0] * m_FrameSize[1];
1244  PlusFidSegmentation::PixelType* pixel = image;
1245  for (int i = 0; i < pixelCount; i++)
1246  {
1247  if (*pixel < thresh)
1248  {
1249  *pixel = BLACK;
1250  }
1251  pixel++;
1252  }
1253 
1254  if (m_DebugOutput)
1255  {
1256  WritePng(image, "seg-suppress.png", m_FrameSize[0], m_FrameSize[1]);
1257  }
1258 
1259 }
1260 
1261 //-----------------------------------------------------------------------------
1262 
1263 inline void PlusFidSegmentation::ClusteringAddNeighbors(PlusFidSegmentation::PixelType* image, int r, int c, std::vector<PlusFidDot>& testPosition, std::vector<PlusFidDot>& setPosition, std::vector<PlusFidSegmentation::PixelType>& valuesOfPosition)
1264 {
1265  //LOG_TRACE("FidSegmentation::ClusteringAddNeighbors");
1266 
1267  if (image[r * m_FrameSize[0] + c] > 0 && testPosition.size() < MAX_CLUSTER_VALS && setPosition.size() < MAX_CLUSTER_VALS)
1268  {
1269  PlusFidDot dot;
1270  dot.SetY(r);
1271  dot.SetX(c);
1272  testPosition.push_back(dot);
1273  setPosition.push_back(dot);
1274  valuesOfPosition.push_back(image[r * m_FrameSize[0] + c]);
1275  image[r * m_FrameSize[0] + c] = 0;
1276  }
1277 }
1278 
1279 //-----------------------------------------------------------------------------
1280 
1281 /* Should we accept a dot? */
1283 {
1284  //LOG_TRACE("FidSegmentation::AcceptDot");
1285 
1286  if (dot.GetY() >= m_RegionOfInterest[1] + MIN_WINDOW_DIST &&
1287  dot.GetY() < m_RegionOfInterest[3] - MIN_WINDOW_DIST &&
1288  dot.GetX() >= m_RegionOfInterest[0] + MIN_WINDOW_DIST &&
1290  {
1291  return true;
1292  }
1293  else
1294  {
1295  return false;
1296  }
1297 }
1298 
1299 //-----------------------------------------------------------------------------
1300 
1301 bool PlusFidSegmentation::Cluster(bool& tooManyCandidates)
1302 {
1303  LOG_TRACE("FidSegmentation::Cluster");
1304  tooManyCandidates = false;
1305 
1306  std::vector<PlusFidDot> testPosition;
1307  std::vector<PlusFidDot> setPosition;
1308  std::vector<PlusFidSegmentation::PixelType> valuesOfPosition;
1309 
1310  for (unsigned int r = m_RegionOfInterest[1]; r < m_RegionOfInterest[3]; r++)
1311  {
1312  for (unsigned int c = m_RegionOfInterest[0]; c < m_RegionOfInterest[2]; c++)
1313  {
1314  if (m_Working[r * m_FrameSize[0] + c] > 0)
1315  {
1316  testPosition.clear();
1317 
1318  PlusFidDot dot;
1319  dot.SetX(c);
1320  dot.SetY(r);
1321  testPosition.push_back(dot);
1322 
1323  setPosition.clear();
1324  setPosition.push_back(dot);
1325 
1326  valuesOfPosition.clear();
1327  valuesOfPosition.push_back(m_Working[r * m_FrameSize[0] + c]);
1328 
1329  m_Working[r * m_FrameSize[0] + c] = 0;
1330 
1331  while (testPosition.size() > 0)
1332  {
1333  PlusFidDot dot = testPosition.back();
1334  testPosition.pop_back();
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  ClusteringAddNeighbors(m_Working, dot.GetY(), dot.GetX() - 1, testPosition, setPosition, valuesOfPosition);
1341  ClusteringAddNeighbors(m_Working, dot.GetY(), dot.GetX() + 1, testPosition, setPosition, valuesOfPosition);
1342 
1343  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX() - 1, testPosition, setPosition, valuesOfPosition);
1344  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX(), testPosition, setPosition, valuesOfPosition);
1345  ClusteringAddNeighbors(m_Working, dot.GetY() + 1, dot.GetX() + 1, testPosition, setPosition, valuesOfPosition);
1346  }
1347 
1348  double dest_r = 0, dest_c = 0, total = 0;
1349  for (unsigned int p = 0; p < setPosition.size(); p++)
1350  {
1351  double amount = (double)valuesOfPosition[p] / (double)UCHAR_MAX;
1352  dest_r += setPosition[p].GetY() * amount;
1353  dest_c += setPosition[p].GetX() * amount;
1354  total += amount;
1355  }
1356 
1357  dot.SetY(dest_r / total);
1358  dot.SetX(dest_c / total);
1359  dot.SetDotIntensity(total);
1360 
1361  if (AcceptDot(dot))
1362  {
1364  {
1365  // Take into account intensities that are close to the dot center
1366  const double dotRadius2 = 3.0 * 3.0;
1367  double dest_r = 0, dest_c = 0, total = 0;
1368  for (unsigned int p = 0; p < setPosition.size(); p++)
1369  {
1370  if ((setPosition[p].GetY() - dot.GetY()) * (setPosition[p].GetY() - dot.GetY()) + (setPosition[p].GetX() - dot.GetX()) * (setPosition[p].GetX() - dot.GetX()) <= dotRadius2)
1371  {
1372  //double amount = (double)vals[p] / (double)UCHAR_MAX;
1373  double amount = (double)m_UnalteredImage[int(setPosition[p].GetY() * m_FrameSize[0] + setPosition[p].GetX())] / (double)UCHAR_MAX;
1374  dest_r += setPosition[p].GetY() * amount;
1375  dest_c += setPosition[p].GetX() * amount;
1376  total += amount;
1377  }
1378  }
1379  dot.SetDotIntensity(total);
1380  }
1381 
1382  m_DotsVector.push_back(dot);
1383  }
1384  }
1385  }
1386  }
1387 
1388  std::sort(m_DotsVector.begin(), m_DotsVector.end(), PlusFidDot::IntensityLessThan);
1390  {
1391  LOG_WARNING("Too many candidates found in segmentation. Consider reducing ROI. " << m_DotsVector.size() << " > " << m_NumberOfMaximumFiducialPointCandidates);
1393  tooManyCandidates = true;
1394  return false;
1395  }
1396  return true;
1397 }
1398 
1399 //-----------------------------------------------------------------------------
1400 
1402 {
1403  LOG_TRACE("FidSegmentation::MorphologicalOperations");
1404 
1405  // Constraint ROI according to the morphological bar size
1407 
1408  if (m_RegionOfInterest[2] == 0 || m_RegionOfInterest[3] == 0 ||
1411  {
1412  // the image is empty, nothing to process
1413  return;
1414  }
1415 
1416  // Morphological operations with a stick-like structuring element
1417  if (m_DebugOutput)
1418  {
1419  WritePng(m_Working, "seg01-initial.png", m_FrameSize[0], m_FrameSize[1]);
1420  }
1421 
1423  if (m_DebugOutput)
1424  {
1425  WritePng(m_Eroded, "seg02-morph-bar-deg0-erode.png", m_FrameSize[0], m_FrameSize[1]);
1426  }
1427 
1429  if (m_DebugOutput)
1430  {
1431  WritePng(m_Dilated, "seg03-morph-bar-deg0-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1432  }
1433 
1435  if (m_DebugOutput)
1436  {
1437  WritePng(m_Working, "seg04-morph-bar-deg0-final.png", m_FrameSize[0], m_FrameSize[1]);
1438  }
1439 
1441  if (m_DebugOutput)
1442  {
1443  WritePng(m_Eroded, "seg05-morph-bar-deg45-erode.png", m_FrameSize[0], m_FrameSize[1]);
1444  }
1445 
1447  if (m_DebugOutput)
1448  {
1449  WritePng(m_Dilated, "seg06-morph-bar-deg45-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1450  }
1451 
1453  if (m_DebugOutput)
1454  {
1455  WritePng(m_Working, "seg07-morph-bar-deg45-final.png", m_FrameSize[0], m_FrameSize[1]);
1456  }
1457 
1459  if (m_DebugOutput)
1460  {
1461  WritePng(m_Eroded, "seg08-morph-bar-deg90-erode.png", m_FrameSize[0], m_FrameSize[1]);
1462  }
1463 
1465  if (m_DebugOutput)
1466  {
1467  WritePng(m_Dilated, "seg09-morph-bar-deg90-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1468  }
1469 
1471  if (m_DebugOutput)
1472  {
1473  WritePng(m_Working, "seg10-morph-bar-deg90-final.png", m_FrameSize[0], m_FrameSize[1]);
1474  }
1475 
1477  if (m_DebugOutput)
1478  {
1479  WritePng(m_Eroded, "seg11-morph-bar-deg135-erode.png", m_FrameSize[0], m_FrameSize[1]);
1480  }
1481 
1483  if (m_DebugOutput)
1484  {
1485  WritePng(m_Dilated, "seg12-morph-bar-deg135-dilated.png", m_FrameSize[0], m_FrameSize[1]);
1486  }
1487 
1489  if (m_DebugOutput)
1490  {
1491  WritePng(m_Working, "seg13-morph-bar-deg135-final.png", m_FrameSize[0], m_FrameSize[1]);
1492  }
1493 
1494  /* Circle operation. */
1496  if (m_DebugOutput)
1497  {
1498  WritePng(m_Eroded, "seg14-morph-circle-erode.png", m_FrameSize[0], m_FrameSize[1]);
1499  }
1500 
1502  if (m_DebugOutput)
1503  {
1504  WritePng(m_Working, "seg15-morph-circle-final.png", m_FrameSize[0], m_FrameSize[1]);
1505  }
1506 
1507 }
1508 
1509 //-----------------------------------------------------------------------------
1510 
1511 void PlusFidSegmentation::SetRegionOfInterest(unsigned int xMin, unsigned int yMin, unsigned int xMax, unsigned int yMax)
1512 {
1513  LOG_TRACE("FidSegmentation::SetRegionOfInterest(" << xMin << ", " << yMin << ", " << xMax << ", " << yMax << ")");
1514 
1515  if (xMin > 0)
1516  {
1517  m_RegionOfInterest[0] = xMin;
1518  }
1519  if (yMin > 0)
1520  {
1521  m_RegionOfInterest[1] = yMin;
1522  }
1523  if (xMax > 0)
1524  {
1525  m_RegionOfInterest[2] = xMax;
1526  }
1527  if (yMax > 0)
1528  {
1529  m_RegionOfInterest[3] = yMax;
1530  }
1531 }
1532 
1533 //-----------------------------------------------------------------------------
1534 
1536 {
1537  LOG_TRACE("FidSegmentation::ValidateRegionOfInterest");
1538 
1539  unsigned int barSize = GetMorphologicalOpeningBarSizePx();
1540 
1541  if (m_FrameSize[0] < barSize * 2 + 1 || m_FrameSize[1] < barSize * 2 + 1)
1542  {
1543  // image is too small
1544  m_RegionOfInterest[0] = 0;
1545  m_RegionOfInterest[1] = 0;
1546  m_RegionOfInterest[2] = 0;
1547  m_RegionOfInterest[3] = 0;
1548  return;
1549  }
1550 
1552  {
1553  std::swap(m_RegionOfInterest[0], m_RegionOfInterest[2]);
1554  }
1556  {
1557  std::swap(m_RegionOfInterest[1], m_RegionOfInterest[3]);
1558  }
1559 
1560  // xmin
1561  if (m_RegionOfInterest[0] - barSize <= 0)
1562  {
1563  m_RegionOfInterest[0] = barSize + 1;
1564  }
1565  if (m_RegionOfInterest[0] + barSize >= m_FrameSize[0])
1566  {
1567  m_RegionOfInterest[0] = m_FrameSize[0] - barSize - 1;
1568  }
1569 
1570  // xmax
1572  {
1574  }
1575  if (m_RegionOfInterest[2] + barSize >= m_FrameSize[0])
1576  {
1577  m_RegionOfInterest[2] = m_FrameSize[0] - barSize - 1;
1578  }
1579 
1580  // ymin
1581  if (m_RegionOfInterest[1] - barSize <= 0)
1582  {
1583  m_RegionOfInterest[1] = barSize + 1;
1584  }
1585  if (m_RegionOfInterest[1] + barSize >= m_FrameSize[1])
1586  {
1587  m_RegionOfInterest[1] = m_FrameSize[1] - barSize - 1;
1588  }
1589 
1590  // ymax
1592  {
1594  }
1595  if (m_RegionOfInterest[3] + barSize >= m_FrameSize[1])
1596  {
1597  m_RegionOfInterest[3] = m_FrameSize[1] - barSize - 1;
1598  }
1599 }
1600 
1601 //-----------------------------------------------------------------------------
1602 void PlusFidSegmentation::GetRegionOfInterest(unsigned int& xMin, unsigned int& yMin, unsigned int& xMax, unsigned int& yMax)
1603 {
1604  xMin = m_RegionOfInterest[0];
1605  yMin = m_RegionOfInterest[1];
1606  xMax = m_RegionOfInterest[2];
1607  yMax = m_RegionOfInterest[3];
1608 }
1609 
1610 //-----------------------------------------------------------------------------
1612 {
1614 }
1615 
1616 //-----------------------------------------------------------------------------
1618 {
1619  m_FiducialGeometry = geometryType;
1620 }
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)
int
Definition: phidget22.h:3069
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
unsigned int clampMinBarSize(unsigned int ir, unsigned int barSize)
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)