KSquare Utilities
SegmentorOTSU.cpp
Go to the documentation of this file.
1 /* SegmentorOTSU.cpp -- Used to segment Raster images using OTSU algorithm.
2  * Copyright (C) 1994-2014 Kurt Kramer
3  * For conditions of distribution and use, see copyright notice in KKB.h
4  */
5 #include "FirstIncludes.h"
6 
7 #include <memory>
8 #include <math.h>
9 
10 #include <float.h>
11 #include <limits.h>
12 #include <algorithm>
13 #include <fstream>
14 #include <map>
15 #include <string>
16 #include <iostream>
17 #include <vector>
18 using namespace std;
19 
20 #include "MemoryDebug.h"
21 
22 #include "SegmentorOTSU.h"
23 
24 #include "KKBaseTypes.h"
25 #include "Raster.h"
26 using namespace KKB;
27 
28 
30  log (_log),
31  threshold1 (0),
32  threshold2 (0)
33 {
34  double z = 0.0;
35  NaN = 1.0 / z;
36 }
37 
38 
40 {
41 }
42 
43 
44 
45 void SegmentorOTSU::LabelRaster (RasterPtr result,
46  uchar pixelValue,
47  uchar label,
48  RasterPtr srcImage
49  )
50 {
51  uchar* resultArea = result->GreenArea ();
52  uchar* srcArea = srcImage->GreenArea ();
53  kkint32 totPixels = srcImage->TotPixels ();
54  for (kkint32 x = 0; x < totPixels; ++x)
55  {
56  if (srcArea[x] == pixelValue)
57  {
58  resultArea[x] = label;
59  }
60  }
61 } /* LabelRaster */
62 
63 
64 void SegmentorOTSU::LabelRaster (RasterPtr result,
65  RasterPtr mask,
66  uchar pixelValue,
67  uchar label,
68  RasterPtr srcImage
69  )
70 {
71  uchar* resultArea = result->GreenArea();
72  uchar* srcArea = srcImage->GreenArea();
73  kkint32 totPixels = srcImage->TotPixels();
74  if (mask)
75  {
76  uchar* maskArea = mask->GreenArea ();
77  uchar maskTh = mask->BackgroundPixelTH ();
78  for (kkint32 x = 0; x < totPixels; ++x)
79  {
80  if ((srcArea[x] == pixelValue) && (maskArea[x] > maskTh))
81  {
82  resultArea[x] = label;
83  }
84  }
85  }
86  else
87  {
88  for (kkint32 x = 0; x < totPixels; ++x)
89  if (srcArea[x] == pixelValue)
90  resultArea[x] = label;
91  }
92 } /* LabelRaster */
93 
94 
95 template<typename T>
96 vector<T> SegmentorOTSU::BDV (T start,
97  T inc,
98  T end
99  )
100 {
101  vector<T> r;
102  while (start <= end)
103  {
104  r.push_back (start);
105  start += inc;
106  }
107  return r;
108 } /* BDV */
109 
110 
111 
112 /**
113  *@brief Returns a vector that represents the accumulated sum of the input vector.
114  */
115 template<typename T>
116 vector<T> SegmentorOTSU::CumSum (const vector<T>& v)
117 {
118  vector<T> r;
119 
120  T total = 0.0;
121  for (kkuint32 x = 0; x < v.size (); ++x)
122  {
123  total += v[x];
124  r.push_back (total);
125  }
126 
127  return r;
128 } /* CumSum */
129 
130 
131 
132 VectorDouble SegmentorOTSU::DotMult (const VectorDouble& left,
133  const VectorDouble& right
134  )
135 {
136  kkuint32 lenMax = (kkuint32)Max (left.size (), right.size ());
137  kkuint32 lenMin = (kkuint32)Min (left.size (), right.size ());
138 
139  VectorDouble r (lenMax, 0.0);
140  for (kkuint32 x = 0; x < lenMin; ++x)
141  r[x] = left[x] * right[x];
142 
143  return r;
144 } /* DotMult */
145 
146 
147 
148 Matrix SegmentorOTSU::DotMult (const Matrix& left,
149  const Matrix& right
150  )
151 {
152  kkint32 maxNumOfRows = Max (left.NumOfRows (), right.NumOfRows ());
153  kkint32 maxNumOfCols = Max (left.NumOfCols (), right.NumOfCols ());
154 
155  kkint32 minNumOfRows = Min (left.NumOfRows (), right.NumOfRows ());
156  kkint32 minNumOfCols = Min (left.NumOfCols (), right.NumOfCols ());
157 
158  Matrix result (maxNumOfRows, maxNumOfCols);
159 
160  double** leftData = left.Data ();
161  double** rightData = right.Data ();
162  double** resultData = result.Data ();
163 
164  kkint32 r, c;
165 
166  for (r = 0; r < minNumOfRows; ++r)
167  {
168  double* leftDataRow = leftData[r];
169  double* rightDataRow = rightData[r];
170  double* resultDataRow = resultData[r];
171 
172  for (c = 0; c < minNumOfCols; ++c)
173  resultDataRow[c] = leftDataRow[c] * rightDataRow[c];
174  }
175 
176  for (r = minNumOfRows; r < maxNumOfRows; ++r)
177  {
178  double* resultDataRow = resultData[r];
179  for (c = minNumOfCols; c < maxNumOfCols; ++c)
180  resultDataRow[c] = 0.0;
181  }
182  return result;
183 } /* DotMult */
184 
185 
186 VectorDouble SegmentorOTSU::DotDiv (const VectorDouble& left,
187  const VectorDouble& right
188  )
189 {
190  kkuint32 x = 0;
191  kkuint32 lenMax = (kkuint32)Max (left.size (), right.size ());
192  kkuint32 lenMin = (kkuint32)Min (left.size (), right.size ());
193 
194  VectorDouble r (lenMax, 0.0);
195  for (x = 0; x < lenMin; ++x)
196  {
197  double rs = right[x];
198  if (rs == 0.0)
199  r[x] = NaN;
200  else
201  r[x] = left[x] / rs;
202  }
203 
204  for (x = lenMin; x < lenMax; ++x)
205  r[x] = (double)NaN;
206 
207  return r;
208 } /* DotDiv */
209 
210 
211 
212 Matrix SegmentorOTSU::DotDiv (const Matrix& left,
213  const Matrix& right
214  )
215 {
216  kkint32 maxNumOfRows = Max (left.NumOfRows (), right.NumOfRows ());
217  kkint32 maxNumOfCols = Max (left.NumOfCols (), right.NumOfCols ());
218 
219  kkint32 minNumOfRows = Min (left.NumOfRows (), right.NumOfRows ());
220  kkint32 minNumOfCols = Min (left.NumOfCols (), right.NumOfCols ());
221 
222  Matrix result (maxNumOfRows, maxNumOfCols);
223 
224  double** leftData = left.Data ();
225  double** rightData = right.Data ();
226  double** resultData = result.Data ();
227 
228  kkint32 r, c;
229 
230  for (r = 0; r < minNumOfRows; ++r)
231  {
232  double* leftDataRow = leftData[r];
233  double* rightDataRow = rightData[r];
234  double* resultDataRow = resultData[r];
235 
236  for (c = 0; c < minNumOfCols; ++c)
237  resultDataRow[c] = leftDataRow[c] / rightDataRow[c];
238  }
239 
240  for (r = minNumOfRows; r < maxNumOfRows; ++r)
241  {
242  double* resultDataRow = resultData[r];
243  for (c = minNumOfCols; c < maxNumOfCols; ++c)
244  {
245  if ((r >= right.NumOfRows ()) || (c >= right.NumOfCols ()))
246  resultDataRow[c] = NaN;
247 
248  else if (rightData[r][c] == 0.0)
249  resultDataRow[c] = NaN;
250 
251  else
252  resultDataRow[c] = 0.0;
253  }
254  }
255 
256  return result;
257 } /* DotDiv */
258 
259 
260 
261 template<typename T>
262 vector<T> SegmentorOTSU::FlipLeftRight (const vector<T>& v)
263 {
264 
265  vector<T> r(v.size (), (T)0);
266  reverse_copy (v.begin (), v.end (), r.begin ());
267  return r;
268 } /* FlipLeftRight */
269 
270 
271 
272 
273 KKB::VectorDouble SegmentorOTSU::Add (const VectorDouble& left,
274  double right
275  )
276 {
277  kkuint32 x = 0;
278  kkuint32 len = (kkuint32)left.size ();
279 
280  VectorDouble r (len, 0.0);
281  for (x = 0; x < len; ++x)
282  {
283  double z1 = left[x];
284  double z2 = z1 + right;
285  r[x] = z2;
286  //r[x] = (left[r] - right);
287  }
288 
289  return r;
290 } /* Add */
291 
292 
293 
294 KKB::VectorDouble SegmentorOTSU::Subt (const VectorDouble& left,
295  const VectorDouble& right
296  )
297 {
298  kkuint32 x = 0;
299  kkuint32 lenMax = (kkuint32)Max (left.size (), right.size ());
300  kkuint32 lenMin = (kkuint32)Min (left.size (), right.size ());
301 
302  VectorDouble r (lenMax, 0.0);
303  for (x = 0; x < lenMin; ++x)
304  r[x] = left[x] - right[x];
305 
306  for (x = lenMin; x < lenMax; ++x)
307  {
308  if (x < left.size ())
309  r[x] = left[x];
310  else
311  r[x] = 0.0 - right[x];
312  }
313 
314  return r;
315 } /* Subt */
316 
317 
318 
319 KKB::VectorDouble SegmentorOTSU::Subt (const VectorDouble& left,
320  double right
321  )
322 {
323  kkuint32 x = 0;
324  kkuint32 len = (kkuint32)left.size ();
325 
326  VectorDouble r (len, 0.0);
327  for (x = 0; x < len; ++x)
328  {
329  double z1 = left[x];
330  double z2 = z1 - right;
331  r[x] = z2;
332  //r[x] = (left[r] - right);
333  }
334 
335  return r;
336 } /* Subt */
337 
338 
339 
340 KKB::VectorDouble SegmentorOTSU::Subt (double left,
341  const VectorDouble& right
342  )
343 {
344  kkuint32 x = 0;
345  kkuint32 len = (kkuint32)right.size ();
346 
347  VectorDouble r (len, 0.0);
348  for (x = 0; x < len; ++x)
349  {
350  r[x] = left - right[x];
351  }
352 
353  return r;
354 } /* Subt */
355 
356 
357 
358 
359 
361  double right
362  )
363 {
364  kkuint32 x = 0;
365  kkuint32 len = (kkuint32)left.size ();
366 
367  VectorDouble r (len, 0.0);
368  for (x = 0; x < len; ++x)
369  {
370  double z1 = left[x];
371  double z2 = z1 - right;
372  r[x] = z2;
373  //r[x] = (left[r] - right);
374  }
375 
376  return r;
377 } /* operator- */
378 
379 
380 
381 
382 template<typename T>
384  const vector<T>& right
385  )
386 {
387  kkuint32 x = 0;
388  kkuint32 len = right.size ();
389 
390  vector<T> r (len, (T)0);
391  for (x = 0; x < len; ++x)
392  r[x] = left - right[r];
393 
394  return r;
395 } /* operator- */
396 
397 
398 
400  double right
401  )
402 {
403  VectorDouble result (left.size (), 0.0);
404 
405  for (kkuint32 x = 0; x < left.size (); ++x)
406  result[x] = left[x] * right;
407 
408  return result;
409 }
410 
411 
412 VectorDouble operator* (double left,
413  const VectorDouble& right
414  )
415 {
416  VectorDouble result (right.size (), 0.0);
417 
418  for (kkuint32 x = 0; x < right.size (); ++x)
419  result[x] = left * right[x];
420 
421  return result;
422 }
423 
424 
425 
426 
427 VectorDouble SegmentorOTSU::Power (const VectorDouble& left,
428  double right
429  )
430 {
431  kkuint32 x = 0;
432  kkuint32 len = (kkuint32)left.size ();
433  VectorDouble r (len, 0.0);
434  for (x = 0; x < len; ++x)
435  r[x] = pow (left[x], right);
436 
437  return r;
438 } /* Power */
439 
440 
441 
442 Matrix SegmentorOTSU::Power (const Matrix& left,
443  double right
444  )
445 {
446  kkuint32 numOfRows = left.NumOfRows ();
447  kkuint32 numOfCols = left.NumOfCols ();
448 
449  Matrix result (numOfRows, numOfCols);
450  double** leftData = left.Data ();
451  double** resultData = result.Data ();
452 
453  kkuint32 r, c;
454  for (r = 0; r < numOfRows; ++r)
455  {
456  double* leftDataRow = leftData[r];
457  double* resultDataRow = resultData[r];
458  for (c = 0; c < numOfCols; ++c)
459  resultDataRow[c] = pow (leftDataRow[c], right);
460  }
461  return result;
462 } /* Power */
463 
464 
465 
466 
467 VectorDouble SegmentorOTSU::Round (const VectorDouble& v)
468 {
469  VectorDouble r (v.size (), 0.0);
470  for (kkuint32 x = 0; x < v.size (); ++x)
471  {
472  r[x] = (kkint32)(v[x] + 0.5f);
473  }
474 
475  return r;
476 } /* Round */
477 
478 
479 
480 
481 template<typename T>
482 T SegmentorOTSU::Sum (const vector<T>& v)
483 {
484  T sum = (T)0;
485  kkuint32 x = 0;
486  for (x = 0; x < v.size (); ++x)
487  sum += v[x];
488  return sum;
489 } /* Sum */
490 
491 
492 
493 void SegmentorOTSU::NdGrid (const VectorDouble& x,
494  const VectorDouble& y,
495  Matrix& xm,
496  Matrix& ym
497  )
498 {
499  kkuint32 xLen = (kkuint32)x.size ();
500  kkuint32 yLen = (kkuint32)y.size ();
501 
502  xm.ReSize (xLen, xLen);
503  ym.ReSize (yLen, yLen);
504 
505  kkuint32 row, col;
506 
507  for (row = 0; row < xLen; ++row)
508  {
509  for (col = 0; col < yLen; ++col)
510  xm[row][col] = x[row];
511  }
512 
513  for (row = 0; row < xLen; ++row)
514  {
515  for (col = 0; col < yLen; ++col)
516  ym[row][col] = y[col];
517  }
518 }
519 
520 
521 
522 void SegmentorOTSU::MakeNanWhenLesOrEqualZero (Matrix& m)
523 {
524  kkint32 r, c;
525  double** data = m.Data ();
526  for (r = 0; r < m.NumOfRows (); ++r)
527  {
528  double* dataRow = data[r];
529  for (c = 0; c < m.NumOfCols (); ++c)
530  {
531  if (dataRow[c] <= 0.0)
532  dataRow[c] = NaN;
533  }
534  }
535 } /* MakeNanWhenLesOrEqualZero */
536 
537 
538 
539 template<typename T>
540 vector<T> SegmentorOTSU::SubSet (const vector<T>& P,
541  kkint32 start,
542  kkint32 end
543  )
544 {
545  vector<T> subSet;
546  for (kkint32 i = start; i <= end; ++i)
547  subSet.push_back (P[i]);
548  return subSet;
549 }
550 
551 
552 
553 template<typename T>
554 T SegmentorOTSU::SumSubSet (const vector<T>& P,
555  kkint32 start,
556  kkint32 end
557  )
558 {
559  T sum = (T)0.0;
560  for (kkint32 i = start; i <= end; ++i)
561  sum += P[i];
562  return sum;
563 }
564 
565 
566 template<typename T>
567 void SegmentorOTSU::ZeroOutNaN (vector<T>& v)
568 {
569  kkuint32 x = 0;
570  for (x = 0; x < v.size (); ++x)
571  {
572  if (_isnan (v[x]))
573  v[x] = (T)0.0;
574  }
575 }
576 
577 #if !defined(OS_WINDOWS)
578 bool _isnan (double& d)
579 {
580  return !(isnan (d) == 0);
581 }
582 #endif
583 
584 
585 
586 void SegmentorOTSU::ZeroOutNaN (Matrix& m)
587 {
588  double** data = m.Data ();
589  kkuint32 numOfRows = m.NumOfRows ();
590  kkuint32 numOfCols = m.NumOfCols ();
591 
592  kkuint32 r, c;
593  for (r = 0; r < numOfRows; ++r)
594  {
595  double* dataRow = data[r];
596  for (c = 0; c < numOfCols; ++c)
597  {
598  if (IsNaN (dataRow[c]))
599  dataRow[c] = 0.0;
600  }
601  }
602 } /* ZeroOutNaN */
603 
604 
605 VectorDouble SegmentorOTSU::LinSpace (double start,
606  double end,
607  kkint32 numPoints
608  )
609 {
610  // The First and last point are known leaving (numPoints - 2) left to distribute between "start" and "end".
611  // which means there will be a total of (numPoints - 1) intervals.
612  double inc = (end - start) / (double)(numPoints - 1);
613  VectorDouble result;
614  while (result.size () < (kkuint32)numPoints)
615  {
616  result.push_back (start);
617  start += inc;
618  }
619 
620  return result;
621 } /* LinSpace */
622 
623 
624 
625 double SegmentorOTSU::sig_func (VectorDouble k,
626  kkint32 nbins,
627  const VectorDouble& P,
628  kkint32 numClasses
629  )
630 {
631  kkint32 x = 0, y = 0;
632 
633  // muT = sum((1:nbins).*P);
634  double muT = 0.0;
635  for (x = 0, y = 1; x < nbins; ++x, ++y)
636  muT += y * P[x];
637 
638  //sigma2T = sum(((1:nbins)-muT).^2.*P);
639  double sigma2T = 0.0;
640  for (x = 0, y = 1; x < nbins; ++x, ++y)
641  sigma2T += (pow ((y - muT), 2.0) * P[x]);
642 
643  //k = round(k*(nbins-1)+1);
644  for (x = 0; x < (kkint32)k.size (); ++x)
645  k[x] = floor (k[x] * (nbins - 1) + 1.0 + 0.5);
646 
647  //k = sort(k);
648  sort (k.begin (), k.end ());
649 
650  //if any(k<1 | k>nbins), y = 1; return, end
651  if ((k[0] < 1.0) || (k[k.size () - 1] > nbins))
652  return 1.0;
653 
654  //k = [0 k nbins]; % Puts '0' at beginning and 'nbins' at the end.
655  k.insert (k.begin (), 0.0);
656  k.push_back (nbins);
657 
658  // sigma2B = 0;
659  double sigma2B = 0.0;
660 
661  //for j = 1:numClasses
662  for (kkint32 j = 0; j < numClasses; ++j)
663  {
664  //wj = sum(P(k(j)+1:k(j+1)));
665  double wj = SumSubSet (P, (kkint32)(k[j] + 1), (kkint32)k[j + 1]);
666 
667  //if wj==0, y = 1; return, end
668  if (wj == 0.0)
669  return 1.0;
670 
671  //muj = sum((k(j)+1:k(j+1)).*P(k(j)+1:k(j+1)))/wj;
672  kkint32 idxStart = (kkint32)(k[j] + 1);
673  kkint32 idxEnd = (kkint32)(k[j + 1]);
674  double muj = 0.0;
675  for (kkint32 i = idxStart; i <= idxEnd; ++i)
676  muj += (i * P[i] / wj);
677 
678  //sigma2B = sigma2B + wj*(muj-muT)^2;
679  sigma2B = sigma2B + wj * pow ((muj - muT), 2.0);
680  }
681 
682  //y = 1-sigma2B/sigma2T; % within the range [0 1]
683  return 1.0 - sigma2B / sigma2T;
684 } /* sig_func */
685 
686 
687 
688 
689 
690 RasterPtr SegmentorOTSU::SegmentImage (RasterPtr srcImage,
691  kkint32 numClasses,
692  double& sep
693  )
694 {
695  /*
696  function [IDX,sep] = otsu (srcImage, numClasses)
697 
698  %OTSU Global image thresholding/segmentation using Otsu's method.
699  % IDX = OTSU(srcImage,N) segments the image srcImage into N classes by means of Otsu's
700  % N-thresholding method. OTSU returns an array IDX containing the cluster
701  % indexes (from 1 to N) of each point. Zero values are assigned to
702  % non-finite (NaN or Inf) pixels.
703  %
704  % IDX = OTSU(srcImage) uses two classes (N=2, default value).
705  %
706  % [IDX,sep] = OTSU(...) also returns the value (sep) of the separability
707  % criterion within the range [0 1]. Zero is obtained only with data
708  % having less than N values, whereas one (optimal value) is obtained only
709  % with N-valued arrays.
710  %
711  % Notes:
712  % -----
713  % It should be noticed that the thresholds generally become less credible
714  % as the number of classes (N) to be separated increases (see Otsu's
715  % paper for more details).
716  %
717  % If srcImage is an RGB image, a Karhunen-Loeve transform is first performed on
718  % the three R,G,B channels. The segmentation is then carried out on the
719  % image component that contains most of the energy.
720  %
721  % Example:
722  % -------
723  % load clown
724  % subplot(221)
725  % X = ind2rgb(X,map);
726  % imshow(X)
727  % title('Original','FontWeight','bold')
728  % for numClasses = 2:4
729  % IDX = otsu(X,numClasses);
730  % subplot(2,2,numClasses)
731  % imagesc(IDX), axis image off
732  % title(['numClasses = ' int2str(numClasses)],'FontWeight','bold')
733  % end
734  % colormap(gray)
735  %
736  % Reference:
737  % ---------
738  % Otsu N, <a href="matlab:web('http://dx.doi.org/doi:10.1109/TSMC.1979.4310076')">A Threshold Selection Method from Gray-Level Histograms</a>,
739  % IEEE Trans. Syst. Man Cybern. 9:62-66;1979
740  %
741  % See also GRAYTHRESH, IM2BW
742  %
743  % -- Damien Garcia -- 2007/08, revised 2010/03
744  % Visit my <a
745  % href="matlab:web('http://www.biomecardio.com/matlab/otsu.html')">website</a> for more details about OTSU
746  */
747 
748  kkint32 pixelIdx = 0;
749  kkint32 x = 0;
750  bool isColorImage = srcImage->Color ();
751 
752  // Checking numClasses (number of classes)
753 
754  if (numClasses == 1)
755  {
756  //IDX = NaN(size(srcImage));
757  //sep = 0;
758  return NULL;
759  }
760 
761  else if ((numClasses < 1) || (numClasses > 255))
762  {
763  log.Level (-1) << "SegmentorOTSU::SegmentImage ***ERROR*** 'numClasses must be a between 1 and 255 !'" << endl;
764  sep = 0;
765  return NULL;
766  }
767 
768  if (isColorImage)
769  {
770  srcImage = srcImage->CreateGrayScaleKLT ();
771  }
772  else
773  {
774  srcImage = new Raster (*srcImage);
775  }
776 
777  kkint32 totPixels = srcImage->TotPixels ();
778  kkint32 pixelsCounted = 0;
779  VectorInt unI;
780  VectorInt unICounts;
781  VectorInt32 counts (256, 0);
782  {
783  // %% Convert to 256 levels
784  // srcImage = srcImage-min(srcImage(:));
785  // srcImage = round(srcImage/max(srcImage(:))*255);
786  // Re-scale Source Image to utilize range of 0 through 255.
787 
788  //%% Probability dCistribution
789  //unI = sort(unique(srcImage));
790  //nbins = min(length(unI),256);
791 
792 
793  uchar* greenArea = srcImage->GreenArea ();
794 
795  uchar pixelMin = 255;
796  uchar pixelMax = 1;
797 
798  for (pixelIdx = 1; pixelIdx < totPixels; ++pixelIdx)
799  {
800  if (greenArea[pixelIdx] < 1)
801  continue;
802 
803  pixelsCounted++;
804 
805  if (greenArea[pixelIdx] < pixelMin)
806  pixelMin = greenArea[pixelIdx];
807 
808  if (greenArea[pixelIdx] > pixelMax)
809  pixelMax = greenArea[pixelIdx];
810  }
811 
812  VectorInt counts (256, 0);
813 
814  for (pixelIdx = 0; pixelIdx < totPixels; ++pixelIdx)
815  {
816  /*
817  double pixelFraction = (double)((double)(greenArea[pixelIdx]) - pixelMin) / (double)srcRange;
818  kkuint32 newPixelVal = (uchar)(pixelFraction * 256.0 + 0.5);
819  if (newPixelVal > 255)
820  newPixelVal = 255;
821 
822  greenArea[pixelIdx] = (uchar)newPixelVal;
823  counts[newPixelVal]++;
824  */
825  counts[greenArea[pixelIdx]]++;
826  }
827 
828  for (x = 1; x < (kkint32)counts.size (); ++x)
829  {
830  if (counts[x] > 0)
831  {
832  unI.push_back (x);
833  unICounts.push_back (counts[x]);
834  }
835  }
836  }
837 
838  kkint32 nbins = (kkint32)unI.size ();
839 
840  if (nbins <= numClasses)
841  {
842  // IDX = ones (size(srcImage));
843  //for i = 1:numClasses, IDX(srcImage==unI(i)) = i; end
844  //sep = 1;
845  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
846  for (x = 0; x < nbins; ++x)
847  {
848  LabelRaster (result, unI[x], x, srcImage);
849  }
850  sep = 1;
851  delete srcImage;
852  srcImage = NULL;
853  return result;
854  }
855 
856  //elseif (nbins < 256)
857  // [histo,pixval] = hist(srcImage(:),unI);
858  //else
859  // [histo,pixval] = hist(srcImage(:),256);
860  VectorInt histo = unICounts;
861  VectorInt pixval = unI;
862 
863  //P = histo/sum(histo);
864  VectorDouble P (histo.size (), 0.0);
865  for (x = 0; x < (kkint32)histo.size (); ++x)
866  P[x] = (double)(histo[x]) / (double)pixelsCounted;
867 
868  //clear unI
869  unI.clear ();
870 
871  //%% Zeroth- and first-order cumulative moments
872  //w = cumsum(P);
873  VectorDouble w (P.size (), 0.0);
874  w[0] = P[0];
875  for (x = 1; x < (kkint32)P.size (); ++x)
876  w[x] = w[x - 1] + P[x];
877 
878  //mu = cumsum((1:nbins).*P);
879  VectorDouble mu (P.size (), 0.0);
880  mu[0] = 1.0 * P[0];
881  for (x = 1; x < (kkint32)P.size (); ++x)
882  mu[x] = mu[x - 1] + ((x + 1) * P[x]);
883  double muEnd = mu[mu.size () - 1];
884 
885  //%% Maximal sigmaB^2 and Segmented image
886  if (numClasses == 2)
887  {
888  //sigma2B =...
889  // (mu(end) * w(2:end-1) - mu(2:end-1)) .^2 ./ w(2:end-1)./(1-w(2:end-1));
890  // ------------------- P1 ----------------- ----------- P2 -----------
891  //[maxsig,k] = max(sigma2B);
892 
893  VectorDouble wSubSet = SubSet (w, 1, (kkint32)w.size () - 2);
894  VectorDouble muSubSet = SubSet (mu, 1, (kkint32)mu.size () - 2);
895 
896  VectorDouble P1 = Power (Subt (muEnd * wSubSet, muSubSet), 2.0);
897  VectorDouble P2 = DotDiv (wSubSet, Subt (1.0, wSubSet));
898  VectorDouble sigma2B = DotDiv (P1, P2);
899  double maxSig = sigma2B[0];
900  kkint32 maxSigIdx = 0;
901  for (x = 1; x < (kkint32)sigma2B.size (); ++x)
902  {
903  if (sigma2B[x] > maxSig)
904  {
905  maxSig = sigma2B[x];
906  maxSigIdx = x;
907  }
908  }
909 
910  //[maxsig,k] = max(sigma2B);
911  kkint32 k = maxSigIdx;
912 
913  //% segmented image
914  //IDX = ones(size(srcImage));
915  //IDX(srcImage>pixval(k+1)) = 2;
916  threshold1 = pixval[k + 1];
917  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
918  uchar* resultArea = result->GreenArea ();
919  uchar* srcArea = srcImage->GreenArea ();
920 
921  while (true)
922  {
923  kkuint32 numClass2Pixs = 0;
924  for (x = 0; x < totPixels; ++x)
925  {
926  if (srcArea[x] > threshold1)
927  {
928  resultArea[x] = 2;
929  ++numClass2Pixs;
930  }
931  else
932  {
933  resultArea[x] = 1;
934  }
935  }
936 
937  if ((threshold1 < 1) || (numClass2Pixs >100))
938  break;
939  --threshold1;
940  }
941 
942  //% separability criterion
943  //sep = maxsig/sum(((1:nbins)-mu(end)).^2.*P);
944  double sum = 0.0;
945  kkint32 y = 0;
946  muEnd = mu[mu.size () - 1];
947  for (x = 0, y = 1; x < nbins; ++x, ++y)
948  sum = pow (((double)y - muEnd), 2.0) * P[x];
949 
950  sep = maxSig / sum;
951  delete srcImage;
952  srcImage = NULL;
953  return result;
954  }
955 
956  if (numClasses == 3)
957  {
958  //w0 = w;
959  //w2 = fliplr(cumsum(fliplr(P)));
960  VectorDouble w0 = w;
961  VectorDouble w2 = FlipLeftRight (CumSum (FlipLeftRight (P)));
962 
963 
964  //[w0,w2] = ndgrid(w0,w2);
965  Matrix w0M ((kkint32)w0.size (), (kkint32)w0.size ());
966  Matrix w2M ((kkint32)w2.size (), (kkint32)w2.size ());
967  NdGrid (w0, w2, w0M, w2M);
968 
969 
970  //mu0 = mu./w;
971  VectorDouble mu0 = DotDiv (mu, w);
972 
973  //mu2 = fliplr(cumsum(fliplr((1:nbins).*P)) ./ cumsum(fliplr(P)));
974  // 1 2 34 4 32 3 4 321
975  // ---------- P1 ------------- ------ P2 --------
976  VectorDouble P1 = CumSum (FlipLeftRight (DotMult (BDV (1.0, 1.0, (double)nbins), P)));
977  VectorDouble P2 = CumSum (FlipLeftRight (P));
978  VectorDouble mu2 = FlipLeftRight (DotDiv (P1, P2));
979 
980  // TODO
981  //[mu0,mu2] = ndgrid(mu0,mu2);
982  Matrix mu0M ((kkint32)mu0.size (), (kkint32)mu0.size ());
983  Matrix mu2M ((kkint32)mu2.size (), (kkint32)mu2.size ());
984  NdGrid (mu0, mu2, mu0M, mu2M);
985 
986  //w1 = 1-w0-w2;
987  Matrix w1M = 1.0 - w0M - w2M;
988 
989  //w1(w1<=0) = NaN;
990  MakeNanWhenLesOrEqualZero (w1M);
991 
992  //sigma2B =...
993  // w0.*(mu0-mu(end)).^2 + w2.*(mu2-mu(end)).^2 +...
994  // (w0.*(mu0-mu(end)) + w2.*(mu2-mu(end))).^2./w1;
995  Matrix P1M = (DotMult (w0M, Power ((mu0M - muEnd), 2.0))) + (DotMult (w2M, Power ((mu2M - muEnd), 2.0)));
996  Matrix P2M = DotDiv (Power ((DotMult (w0M, (mu0M - muEnd)) + DotMult (w2M, (mu2M - muEnd))), 2.0), w1M);
997  Matrix sigma2B = P1M + P2M;
998 
999 
1000  //sigma2B(isnan(sigma2B)) = 0; % zeroing if k1 >= k2
1001  ZeroOutNaN (sigma2B);
1002 
1003  //[maxsig,k] = max(sigma2B(:)); % Turns sigma2B into 1D Array then locates largest value and index.
1004  // [k1,k2] = ind2sub([nbins nbins],k); % Sets k1 and k2 to the indexes for k mapped into a 2D square matrix that is (nbins x nbins)
1005  kkint32 k1, k2;
1006  double maxsig = 0.0;
1007  sigma2B.FindMaxValue (maxsig, k1, k2);
1008 
1009  //% segmented image
1010  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
1011  {
1012  //IDX = ones(size(srcImage))*3;
1013  //IDX(srcImage<=pixval(k1)) = 1;
1014  //IDX(srcImage>pixval(k1) & srcImage<=pixval(k2)) = 2;
1015  uchar* srcData = srcImage->GreenArea ();
1016  uchar* data = result->GreenArea ();
1017  threshold1 = pixval[k1];
1018  threshold2 = pixval[k2];
1019  for (x = 0; x < totPixels; ++x)
1020  {
1021  if (srcData[x] <= threshold1)
1022  data[x] = 1;
1023  else if (srcData[x] <= threshold2)
1024  data[x] = 2;
1025  else
1026  data[x] = 3;
1027  }
1028  }
1029 
1030  //% separability criterion
1031  //sep = maxsig / sum (((1:nbins)-mu(end)).^2.*P);
1032 
1033  //VectorDouble xxx = BDV (1.0, 1.0, (double)nbins);
1034  //VectorDouble yyy = Subt (xxx, muEnd);
1035  //VectorDouble zzz = Power (yyy, 2.0);
1036  sep = maxsig / Sum (DotMult (Power (Subt (BDV (1.0, 1.0, (double)nbins), muEnd), 2.0), P));
1037  delete srcImage;
1038  srcImage = NULL;
1039  return result;
1040  }
1041 
1042  {
1043  /*
1044  //k0 = linspace(0,1,numClasses+1); % k0 = row vector of linear spaced points between 0 and 1 with (numClasses + 1) points
1045  VectorDouble k0 = LinSpace (0, 1, numClasses + 1);
1046 
1047  //k0 = k0(2:numClasses);
1048 
1049 
1050  [k,y] = fminsearch(@sig_func,k0,optimset('TolX',1));
1051  k = round(k*(nbins-1)+1);
1052 
1053  % segmented image
1054  IDX = ones(size(srcImage))*numClasses;
1055  IDX(srcImage<=pixval(k(1))) = 1;
1056  for i = 1:numClasses-2
1057  IDX(srcImage>pixval(k(i)) & srcImage<=pixval(k(i+1))) = i+1;
1058  end
1059 
1060  % separability criterion
1061  sep = 1-y;
1062  */
1063  }
1064 
1065  delete srcImage;
1066  srcImage = NULL;
1067 
1068  return NULL;
1069 } /* SegmentImage */
1070 
1071 
1072 
1073 
1074 /**
1075  *@brief Segments image into 'numClasses' taking into account only pixels
1076  * indicated by 'mask' image.
1077  *@param[in] srcImage Image to segment. If it is a color image will be
1078  * converted to GrayScale using 'CreateGrayScaleKLTOnMaskedArea'
1079  *@param[in] mask Indicates which pixels to consider when thresholding image. Pixels
1080  * that are not part of mask will be assigned label '0'.
1081  *@param[in] numClasses Number of classes to segment image into. Current only '2' and '3' are supported.
1082  *@param[out] sep
1083  *@return Labeled GrayScale image where pixels will be label into their respective class; between '1' and 'numClasses'.
1084  */
1085 RasterPtr SegmentorOTSU::SegmentMaskedImage (RasterPtr srcImage,
1086  RasterPtr mask,
1087  kkint32 numClasses,
1088  double& sep
1089  )
1090 {
1091  kkint32 pixelIdx = 0;
1092  kkint32 x = 0;
1093  bool isColorImage = srcImage->Color ();
1094 
1095  uchar* maskArea = NULL;
1096  uchar maskTh = 0;
1097  if (mask)
1098  {
1099  maskArea = mask->GreenArea ();
1100  maskTh = mask->BackgroundPixelTH ();
1101  }
1102 
1103  if (numClasses == 1)
1104  {
1105  return NULL;
1106  }
1107 
1108  else if ((numClasses < 1) || (numClasses > 255))
1109  {
1110  log.Level (-1) << endl << endl
1111  << "SegmentorOTSU::SegmentMaskedImage ***ERROR*** 'numClasses must be a between 1 and 255 !'" << endl
1112  << endl;
1113  sep = 0;
1114  return NULL;
1115  }
1116 
1117  if (isColorImage)
1118  {
1119  if (mask)
1120  srcImage = srcImage->CreateGrayScaleKLTOnMaskedArea (*mask);
1121  else
1122  srcImage = srcImage->CreateGrayScaleKLT ();
1123  }
1124  else
1125  {
1126  srcImage = new Raster (*srcImage);
1127  }
1128 
1129  kkint32 totPixels = srcImage->TotPixels ();
1130  kkint32 totMaskPixels = totPixels;
1131  if (mask)
1132  totMaskPixels = mask->TotalBackgroundPixels ();
1133 
1134  VectorInt unI;
1135  VectorInt unICounts;
1136 
1137  {
1138  uchar* greenArea = srcImage->GreenArea ();
1139 
1140  uchar pixelMin = greenArea[0];
1141  uchar pixelMax = greenArea[0];
1142 
1143  for (pixelIdx = 1; pixelIdx < totPixels; ++pixelIdx)
1144  {
1145  if ((!mask) || (maskArea[pixelIdx] > maskTh))
1146  {
1147  if (greenArea[pixelIdx] < pixelMin)
1148  pixelMin = greenArea[pixelIdx];
1149 
1150  if (greenArea[pixelIdx] > pixelMax)
1151  pixelMax = greenArea[pixelIdx];
1152  }
1153  }
1154 
1155  //kkint32 srcRange = pixelMax - pixelMin + 1;
1156  VectorInt counts (256, 0);
1157 
1158  for (pixelIdx = 0; pixelIdx < totPixels; ++pixelIdx)
1159  {
1160  if ((!mask) || (maskArea[pixelIdx] > maskTh))
1161  counts[greenArea[pixelIdx]]++;
1162  }
1163 
1164  for (x = 0; x < (kkint32)counts.size (); ++x)
1165  {
1166  if (counts[x] > 0)
1167  {
1168  unI.push_back (x);
1169  unICounts.push_back (counts[x]);
1170  }
1171  }
1172  }
1173 
1174  kkint32 nbins = (kkint32)unI.size ();
1175 
1176  if (nbins <= numClasses)
1177  {
1178  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
1179  for (x = 0; x < nbins; ++x)
1180  {
1181  LabelRaster (result, mask, unI[x], x, srcImage);
1182  }
1183  sep = 1;
1184  delete srcImage;
1185  srcImage = NULL;
1186  return result;
1187  }
1188 
1189  VectorInt histo = unICounts;
1190  VectorInt pixval = unI;
1191 
1192  VectorDouble P (histo.size (), 0.0);
1193  for (x = 0; x < (kkint32)histo.size (); ++x)
1194  P[x] = (double)(histo[x]) / (double)totMaskPixels;
1195 
1196  unI.clear ();
1197 
1198  //%% Zeroth- and first-order cumulative moments
1199  //w = cumsum(P);
1200  VectorDouble w (P.size (), 0.0);
1201  w[0] = P[0];
1202  for (x = 1; x < (kkint32)P.size (); ++x)
1203  w[x] = w[x - 1] + P[x];
1204 
1205  //mu = cumsum((1:nbins).*P);
1206  VectorDouble mu (P.size (), 0.0);
1207  mu[0] = 1.0 * P[0];
1208  for (x = 1; x < (kkint32)P.size (); ++x)
1209  mu[x] = mu[x - 1] + ((x + 1) * P[x]);
1210  double muEnd = mu[mu.size () - 1];
1211 
1212  //%% Maximal sigmaB^2 and Segmented image
1213  if (numClasses == 2)
1214  {
1215  //sigma2B =...
1216  // (mu(end) * w(2:end-1) - mu(2:end-1)) .^2 ./ w(2:end-1)./(1-w(2:end-1));
1217  // ------------------- P1 ----------------- ----------- P2 -----------
1218  //[maxsig,k] = max(sigma2B);
1219 
1220  VectorDouble wSubSet = SubSet (w, 1, (kkint32)w.size () - 2);
1221  VectorDouble muSubSet = SubSet (mu, 1, (kkint32)mu.size () - 2);
1222 
1223  VectorDouble P1 = Power (Subt (muEnd * wSubSet, muSubSet), 2.0);
1224  VectorDouble P2 = DotDiv (wSubSet, Subt (1.0, wSubSet));
1225  VectorDouble sigma2B = DotDiv (P1, P2);
1226  double maxSig = sigma2B[0];
1227  kkint32 maxSigIdx = 0;
1228  for (x = 1; x < (kkint32)sigma2B.size (); ++x)
1229  {
1230  if (sigma2B[x] > maxSig)
1231  {
1232  maxSig = sigma2B[x];
1233  maxSigIdx = x;
1234  }
1235  }
1236 
1237  //[maxsig,k] = max(sigma2B);
1238  kkint32 k = maxSigIdx;
1239 
1240  //% segmented image
1241  //IDX = ones(size(srcImage));
1242  //IDX(srcImage>pixval(k+1)) = 2;
1243  kkint32 threshold = pixval[k + 1];
1244  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
1245  uchar* resultArea = result->GreenArea ();
1246  uchar* srcArea = srcImage->GreenArea ();
1247  for (x = 0; x < totPixels; ++x)
1248  {
1249  if ((!maskArea) || (maskArea[x] > maskTh))
1250  {
1251  if (srcArea[x] > threshold)
1252  resultArea[x] = 2;
1253  else
1254  resultArea[x] = 1;
1255  }
1256  else
1257  {
1258  resultArea[x] = 0;
1259  }
1260  }
1261 
1262  //% separability criterion
1263  //sep = maxsig/sum(((1:nbins)-mu(end)).^2.*P);
1264  double sum = 0.0;
1265  kkint32 y = 0;
1266  muEnd = mu[mu.size () - 1];
1267  for (x = 0, y = 1; x < nbins; ++x, ++y)
1268  sum = pow (((double)y - muEnd), 2.0) * P[x];
1269 
1270  sep = maxSig / sum;
1271  delete srcImage;
1272  srcImage = NULL;
1273  return result;
1274  }
1275 
1276  if (numClasses == 3)
1277  {
1278  VectorDouble w0 = w;
1279  VectorDouble w2 = FlipLeftRight (CumSum (FlipLeftRight (P)));
1280 
1281  Matrix w0M ((kkint32)w0.size (), (kkint32)w0.size ());
1282  Matrix w2M ((kkint32)w2.size (), (kkint32)w2.size ());
1283  NdGrid (w0, w2, w0M, w2M);
1284 
1285  VectorDouble mu0 = DotDiv (mu, w);
1286 
1287  //mu2 = fliplr(cumsum(fliplr((1:nbins).*P)) ./ cumsum(fliplr(P)));
1288  // 1 2 34 4 32 3 4 321
1289  // ---------- P1 ------------- ------ P2 --------
1290  VectorDouble P1 = CumSum (FlipLeftRight (DotMult (BDV (1.0, 1.0, (double)nbins), P)));
1291  VectorDouble P2 = CumSum (FlipLeftRight (P));
1292  VectorDouble mu2 = FlipLeftRight (DotDiv (P1, P2));
1293 
1294  //[mu0,mu2] = ndgrid(mu0,mu2);
1295  Matrix mu0M ((kkint32)mu0.size (), (kkint32)mu0.size ());
1296  Matrix mu2M ((kkint32)mu2.size (), (kkint32)mu2.size ());
1297  NdGrid (mu0, mu2, mu0M, mu2M);
1298 
1299  //w1 = 1-w0-w2;
1300  Matrix w1M = 1.0 - w0M - w2M;
1301 
1302  //w1(w1<=0) = NaN;
1303  MakeNanWhenLesOrEqualZero (w1M);
1304 
1305  //sigma2B =...
1306  // w0.*(mu0-mu(end)).^2 + w2.*(mu2-mu(end)).^2 +...
1307  // (w0.*(mu0-mu(end)) + w2.*(mu2-mu(end))).^2./w1;
1308  Matrix P1M = (DotMult (w0M, Power ((mu0M - muEnd), 2.0))) + (DotMult (w2M, Power ((mu2M - muEnd), 2.0)));
1309  Matrix P2M = DotDiv (Power ((DotMult (w0M, (mu0M - muEnd)) + DotMult (w2M, (mu2M - muEnd))), 2.0), w1M);
1310  Matrix sigma2B = P1M + P2M;
1311 
1312  //sigma2B(isnan(sigma2B)) = 0; % zeroing if k1 >= k2
1313  ZeroOutNaN (sigma2B);
1314 
1315  //[maxsig,k] = max(sigma2B(:)); % Turns sigma2B into 1D Array then locates largest value and index.
1316  // [k1,k2] = ind2sub([nbins nbins],k); % Sets k1 and k2 to the indexes for k mapped into a 2D square matrix that is (nbins x nbins)
1317  kkint32 k1, k2;
1318  double maxsig = 0.0;
1319  sigma2B.FindMaxValue (maxsig, k1, k2);
1320 
1321  //% segmented image
1322  RasterPtr result = new Raster (srcImage->Height (), srcImage->Width (), false);
1323  {
1324  //IDX = ones(size(srcImage))*3;
1325  //IDX(srcImage<=pixval(k1)) = 1;
1326  //IDX(srcImage>pixval(k1) & srcImage<=pixval(k2)) = 2;
1327  uchar* srcData = srcImage->GreenArea ();
1328  uchar* data = result->GreenArea ();
1329  double th1 = pixval[k1];
1330  double th2 = pixval[k2];
1331  for (x = 0; x < totPixels; ++x)
1332  {
1333  if ((!maskArea) || (maskArea[x] > maskTh))
1334  {
1335  if (srcData[x] <= th1)
1336  data[x] = 1;
1337  else if (srcData[x] <= th2)
1338  data[x] = 2;
1339  else
1340  data[x] = 3;
1341  }
1342  else
1343  {
1344  data[x] = 0;
1345  }
1346  }
1347  }
1348 
1349  sep = maxsig / Sum (DotMult (Power (Subt (BDV (1.0, 1.0, (double)nbins), muEnd), 2.0), P));
1350  delete srcImage;
1351  srcImage = NULL;
1352  return result;
1353  }
1354 
1355  delete srcImage;
1356  srcImage = NULL;
1357 
1358  return NULL;
1359 } /* SegmentMaskedImage */
1360 
1361 
1362 
1363 PixelValue SegmentorOTSU::ClassAverageRGB (const RasterPtr origImage,
1364  const RasterPtr segmentedImage,
1365  uchar segmentedClass
1366  )
1367 {
1368  kkuint32 totalRed = 0;
1369  kkuint32 totalGreen = 0;
1370  kkuint32 totalBlue = 0;
1371 
1372  const uchar* origRed = origImage->RedArea ();
1373  const uchar* origGreen = origImage->GreenArea ();
1374  const uchar* origBlue = origImage->BlueArea ();
1375 
1376  const uchar* mask = segmentedImage->GreenArea ();
1377 
1378  bool origImageColor = origImage->Color ();
1379 
1380  kkuint32 totalPixels = (kkuint32)origImage->TotPixels ();
1381 
1382  for (kkuint32 x = 0; x < totalPixels; ++x)
1383  {
1384  if (mask[x] == segmentedClass)
1385  {
1386  totalGreen += origGreen[x];
1387  if (origImageColor)
1388  {
1389  totalRed += origRed [x];
1390  totalBlue += origBlue [x];
1391  }
1392  }
1393  }
1394 
1395  totalGreen = (uchar)(0.5 + (double)totalGreen / (double)totalPixels);
1396  if (origImageColor)
1397  {
1398  totalRed = (uchar)(0.5 + (double)totalRed / (double)totalPixels);
1399  totalBlue = (uchar)(0.5 + (double)totalBlue / (double)totalPixels);
1400  }
1401 
1402  return PixelValue ((uchar)totalRed, (uchar)totalGreen, (uchar)totalBlue);
1403 } /* AverageRegionRGB */
1404 
1405 
1406 
1408  const RasterPtr segmentedImage,
1409  const PixelValue& targetColor
1410  )
1411 {
1412  // We can safely assume there will be at least 2 classes.
1413  VectorUint32 totalReds (3, 0); // Initialized to 3 because vectors are '0' based; index '0' will not be used.
1414  VectorUint32 totalGreens (3, 0);
1415  VectorUint32 totalBlues (3, 0);
1416 
1417  uchar largestClass = 2;
1418 
1419  const uchar* origRed = origImage->RedArea ();
1420  const uchar* origGreen = origImage->GreenArea ();
1421  const uchar* origBlue = origImage->BlueArea ();
1422 
1423  const uchar* mask = segmentedImage->GreenArea ();
1424 
1425  bool origImageColor = origImage->Color ();
1426 
1427  kkuint32 totalPixels = (kkuint32)origImage->TotPixels ();
1428 
1429  uchar classValue = 0;
1430 
1431  for (kkuint32 x = 0; x < totalPixels; ++x)
1432  {
1433  classValue = mask[x];
1434  while (classValue > largestClass)
1435  {
1436  totalReds.push_back (0);
1437  totalGreens.push_back (0);
1438  totalBlues.push_back (0);
1439  ++largestClass;
1440  }
1441 
1442  totalGreens[classValue] += origGreen[x];
1443  if (origImageColor)
1444  {
1445  totalReds[classValue] += origRed [x];
1446  totalBlues[classValue] += origBlue[x];
1447  }
1448  }
1449 
1450  VectorFloat distFromTarget (largestClass + 1, 0.0f);
1451 
1452  double closestDistFound = 99999999.99;
1453  uchar closestClass = 255;
1454 
1455  for (uint x = 1; x <= largestClass; ++x)
1456  {
1457  double avgRed = (double)totalReds [x] / (double)totalPixels;
1458  double avgGreen = (double)totalGreens[x] / (double)totalPixels;
1459  double avgBlue = (double)totalBlues [x] / (double)totalPixels;
1460 
1461  double deltaRed = fabs (targetColor.r - avgRed);
1462  double deltaGreen = fabs (targetColor.g - avgGreen);
1463  double deltaBlue = fabs (targetColor.b - avgBlue);
1464  double distToTarget = sqrt (deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue);
1465 
1466  if (distToTarget < closestDistFound)
1467  {
1468  closestDistFound = distToTarget;
1469  closestClass = x;
1470  }
1471  }
1472 
1473  return closestClass;
1474 } /* GetClassClosestToTargetColor */
std::vector< kkuint32 > VectorUint32
Vector of unsigned 32 bit integers.
Definition: KKBaseTypes.h:145
bool IsNaN(const double &d)
Definition: KKBaseTypes.cpp:93
kkint32 NumOfRows() const
Definition: Matrix.h:128
__int32 kkint32
Definition: KKBaseTypes.h:88
RasterPtr CreateGrayScaleKLT() const
Creates a image using a KLT Transform with the goal of weighting in favor the color channels with gre...
Definition: Raster.cpp:9503
Matrix operator-(double right)
Definition: Matrix.cpp:443
Matrix(kkint32 _numOfRows, kkint32 _numOfCols)
Definition: Matrix.cpp:108
std::vector< int > VectorInt
Definition: KKBaseTypes.h:138
PixelValue ClassAverageRGB(const RasterPtr origImage, const RasterPtr segmentedImage, uchar segmentedClass)
Will compute the average RGB values of the region indicated by the segmented image.
A class that is used by to represent a single image in memory.
Definition: Raster.h:108
Supports two dimensional matrices.
Definition: Matrix.h:46
SegmentorOTSU(RunLog &_log)
uchar GetClassClosestToTargetColor(const RasterPtr origImage, const RasterPtr segmentedImage, const PixelValue &targetColor)
Determines which class in the segmented image is closet in RGB color space to the specified target co...
bool Color() const
Definition: Raster.h:310
unsigned int uint
Unsigned integer.
Definition: KKBaseTypes.h:78
unsigned __int32 kkuint32
Definition: KKBaseTypes.h:89
kkint32 NumOfCols() const
Definition: Matrix.h:126
kkint32 Height() const
Definition: Raster.h:319
VectorDouble operator-(const VectorDouble &left, double right)
KKTHread * KKTHreadPtr
PixelValue(uchar _r, uchar _g, uchar _b)
Constructs a &#39;PixelValue&#39; instance using the provided values for the color components.
Definition: PixelValue.cpp:60
std::vector< float > VectorFloat
Definition: KKBaseTypes.h:149
void FindMaxValue(double &maxVal, kkint32 &row, kkint32 &col)
Locates the maximum value in a matrix along with the row and column that is located.
Definition: Matrix.cpp:818
VectorDouble operator*(const VectorDouble &left, double right)
Matrix operator+(const Matrix &right)
Definition: Matrix.cpp:369
unsigned char uchar
Unsigned character.
Definition: KKBaseTypes.h:77
uchar BackgroundPixelTH() const
Definition: Raster.h:335
std::vector< kkint32 > VectorInt32
Vector of signed 32 bit integers.
Definition: KKBaseTypes.h:144
uchar * RedArea() const
Definition: Raster.h:329
vector< T > operator-(T left, const vector< T > &right)
kkint32 TotPixels() const
Definition: Raster.h:323
kkint32 TotalBackgroundPixels() const
Definition: Raster.cpp:1086
RasterPtr SegmentImage(RasterPtr srcImage, kkint32 numClasses, double &sep)
Segments image into &#39;numClasses&#39;.
Raster(kkint32 _height, kkint32 _width, bool _color)
Constructs a blank image with given dimensions.
Definition: Raster.cpp:356
uchar * GreenArea() const
Definition: Raster.h:330
Matrix operator-(const Matrix &right)
Definition: Matrix.cpp:419
uchar * BlueArea() const
Definition: Raster.h:331
kkint32 Width() const
Definition: Raster.h:324
VectorDouble operator*(double left, const VectorDouble &right)
#define OS_WINDOWS
Definition: FirstIncludes.h:11
Used for logging messages.
Definition: RunLog.h:49
void ReSize(kkint32 _numOfRows, kkint32 _numOfCols)
Definition: Matrix.cpp:224
Raster(const Raster &_raster)
Copy Constructor.
Definition: Raster.cpp:460
Matrix operator-(double left, const Matrix &right)
Definition: Matrix.cpp:456
RasterPtr CreateGrayScaleKLTOnMaskedArea(const Raster &mask) const
Same as &#39;CreateKLT&#39; except it will only take into account pixels specified by the &#39;mask&#39; image...
Definition: Raster.cpp:9643
double **const Data() const
Definition: Matrix.h:106
Used by the Raster Class to represent the contents of one pixel.
Definition: PixelValue.h:22
std::vector< double > VectorDouble
Vector of doubles.
Definition: KKBaseTypes.h:148
RasterPtr SegmentMaskedImage(RasterPtr srcImage, RasterPtr mask, kkint32 numClasses, double &sep)
Segments image into &#39;numClasses&#39; taking into account only pixels indicated by &#39;mask&#39; image...