blob: 4ea18ba403c7f334f99826f44c49bfeee6342490 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% SSSSS EEEEE GGGG M M EEEEE N N TTTTT %
7% SS E G MM MM E NN N T %
8% SSS EEE G GGG M M M EEE N N N T %
9% SS E G G M M E N NN T %
10% SSSSS EEEEE GGGG M M EEEEE N N T %
11% %
12% %
13% MagickCore Methods to Segment an Image with Thresholding Fuzzy c-Means %
14% %
15% Software Design %
16% John Cristy %
17% April 1993 %
18% %
19% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36% Segment segments an image by analyzing the histograms of the color
37% components and identifying units that are homogeneous with the fuzzy
38% c-means technique. The scale-space filter analyzes the histograms of
39% the three color components of the image and identifies a set of
40% classes. The extents of each class is used to coarsely segment the
41% image with thresholding. The color associated with each class is
42% determined by the mean color of all pixels within the extents of a
43% particular class. Finally, any unclassified pixels are assigned to
44% the closest class with the fuzzy c-means technique.
45%
46% The fuzzy c-Means algorithm can be summarized as follows:
47%
48% o Build a histogram, one for each color component of the image.
49%
50% o For each histogram, successively apply the scale-space filter and
51% build an interval tree of zero crossings in the second derivative
52% at each scale. Analyze this scale-space ``fingerprint'' to
53% determine which peaks and valleys in the histogram are most
54% predominant.
55%
56% o The fingerprint defines intervals on the axis of the histogram.
57% Each interval contains either a minima or a maxima in the original
58% signal. If each color component lies within the maxima interval,
59% that pixel is considered ``classified'' and is assigned an unique
60% class number.
61%
62% o Any pixel that fails to be classified in the above thresholding
63% pass is classified using the fuzzy c-Means technique. It is
64% assigned to one of the classes discovered in the histogram analysis
65% phase.
66%
67% The fuzzy c-Means technique attempts to cluster a pixel by finding
68% the local minima of the generalized within group sum of squared error
69% objective function. A pixel is assigned to the closest class of
70% which the fuzzy membership has a maximum value.
71%
72% Segment is strongly based on software written by Andy Gallo,
73% University of Delaware.
74%
75% The following reference was used in creating this program:
76%
77% Young Won Lim, Sang Uk Lee, "On The Color Image Segmentation
78% Algorithm Based on the Thresholding and the Fuzzy c-Means
79% Techniques", Pattern Recognition, Volume 23, Number 9, pages
80% 935-952, 1990.
81%
82%
83*/
84
85#include "magick/studio.h"
86#include "magick/cache.h"
87#include "magick/color.h"
88#include "magick/colorspace.h"
89#include "magick/exception.h"
90#include "magick/exception-private.h"
91#include "magick/image.h"
92#include "magick/image-private.h"
93#include "magick/memory_.h"
94#include "magick/monitor.h"
95#include "magick/monitor-private.h"
96#include "magick/quantize.h"
97#include "magick/quantum.h"
98#include "magick/quantum-private.h"
99#include "magick/segment.h"
100#include "magick/string_.h"
101
102/*
103 Define declarations.
104*/
105#define MaxDimension 3
106#define DeltaTau 0.5f
107#if defined(FastClassify)
108#define WeightingExponent 2.0
109#define SegmentPower(ratio) (ratio)
110#else
111#define WeightingExponent 2.5
112#define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
113#endif
114#define Tau 5.2f
115
116/*
117 Typedef declarations.
118*/
119typedef struct _ExtentPacket
120{
121 MagickRealType
122 center;
123
124 long
125 index,
126 left,
127 right;
128} ExtentPacket;
129
130typedef struct _Cluster
131{
132 struct _Cluster
133 *next;
134
135 ExtentPacket
136 red,
137 green,
138 blue;
139
140 long
141 count,
142 id;
143} Cluster;
144
145typedef struct _IntervalTree
146{
147 MagickRealType
148 tau;
149
150 long
151 left,
152 right;
153
154 MagickRealType
155 mean_stability,
156 stability;
157
158 struct _IntervalTree
159 *sibling,
160 *child;
161} IntervalTree;
162
163typedef struct _ZeroCrossing
164{
165 MagickRealType
166 tau,
167 histogram[256];
168
169 short
170 crossings[256];
171} ZeroCrossing;
172
173/*
174 Constant declarations.
175*/
176static const int
177 Blue = 2,
178 Green = 1,
179 Red = 0,
180 SafeMargin = 3,
181 TreeLength = 600;
182
183/*
184 Method prototypes.
185*/
186static MagickRealType
187 OptimalTau(const long *,const double,const double,const double,
188 const double,short *);
189
190static long
191 DefineRegion(const short *,ExtentPacket *);
192
193static void
194 InitializeHistogram(const Image *,long **,ExceptionInfo *),
195 ScaleSpace(const long *,const MagickRealType,MagickRealType *),
196 ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *);
197
198/*
199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200% %
201% %
202% %
203+ C l a s s i f y %
204% %
205% %
206% %
207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208%
209% Classify() defines one or more classes. Each pixel is thresholded to
210% determine which class it belongs to. If the class is not identified it is
211% assigned to the closest class based on the fuzzy c-Means technique.
212%
213% The format of the Classify method is:
214%
215% MagickBooleanType Classify(Image *image,short **extrema,
216% const MagickRealType cluster_threshold,
217% const MagickRealType weighting_exponent,
218% const MagickBooleanType verbose)
219%
220% A description of each parameter follows.
221%
222% o image: the image.
223%
224% o extrema: Specifies a pointer to an array of integers. They
225% represent the peaks and valleys of the histogram for each color
226% component.
227%
228% o cluster_threshold: This MagickRealType represents the minimum number of
229% pixels contained in a hexahedra before it can be considered valid
230% (expressed as a percentage).
231%
232% o weighting_exponent: Specifies the membership weighting exponent.
233%
234% o verbose: A value greater than zero prints detailed information about
235% the identified classes.
236%
237*/
238static MagickBooleanType Classify(Image *image,short **extrema,
239 const MagickRealType cluster_threshold,
240 const MagickRealType weighting_exponent,const MagickBooleanType verbose)
241{
242#define SegmentImageTag "Segment/Image"
243
244 Cluster
245 *cluster,
246 *head,
247 *last_cluster,
248 *next_cluster;
249
250 ExceptionInfo
251 *exception;
252
253 ExtentPacket
254 blue,
255 green,
256 red;
257
258 long
259 count,
260 progress,
261 y;
262
263 MagickRealType
264 *free_squares;
265
266 MagickStatusType
267 status;
268
269 register long
270 i;
271
272 register MagickRealType
273 *squares;
274
275 unsigned long
276 number_clusters;
277
278 CacheView
279 *image_view;
280
281 /*
282 Form clusters.
283 */
284 cluster=(Cluster *) NULL;
285 head=(Cluster *) NULL;
286 (void) ResetMagickMemory(&red,0,sizeof(red));
287 (void) ResetMagickMemory(&green,0,sizeof(green));
288 (void) ResetMagickMemory(&blue,0,sizeof(blue));
289 while (DefineRegion(extrema[Red],&red) != 0)
290 {
291 green.index=0;
292 while (DefineRegion(extrema[Green],&green) != 0)
293 {
294 blue.index=0;
295 while (DefineRegion(extrema[Blue],&blue) != 0)
296 {
297 /*
298 Allocate a new class.
299 */
300 if (head != (Cluster *) NULL)
301 {
302 cluster->next=(Cluster *) AcquireMagickMemory(
303 sizeof(*cluster->next));
304 cluster=cluster->next;
305 }
306 else
307 {
308 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
309 head=cluster;
310 }
311 if (cluster == (Cluster *) NULL)
312 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
313 image->filename);
314 /*
315 Initialize a new class.
316 */
317 cluster->count=0;
318 cluster->red=red;
319 cluster->green=green;
320 cluster->blue=blue;
321 cluster->next=(Cluster *) NULL;
322 }
323 }
324 }
325 if (head == (Cluster *) NULL)
326 {
327 /*
328 No classes were identified-- create one.
329 */
330 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
331 if (cluster == (Cluster *) NULL)
332 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
333 image->filename);
334 /*
335 Initialize a new class.
336 */
337 cluster->count=0;
338 cluster->red=red;
339 cluster->green=green;
340 cluster->blue=blue;
341 cluster->next=(Cluster *) NULL;
342 head=cluster;
343 }
344 /*
345 Count the pixels for each cluster.
346 */
347 status=MagickTrue;
348 count=0;
349 progress=0;
350 exception=(&image->exception);
351 image_view=AcquireCacheView(image);
352 for (y=0; y < (long) image->rows; y++)
353 {
354 register const PixelPacket
355 *p;
356
357 register long
358 x;
359
360 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
361 if (p == (const PixelPacket *) NULL)
362 break;
363 for (x=0; x < (long) image->columns; x++)
364 {
365 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
366 if (((long) ScaleQuantumToChar(p->red) >=
367 (cluster->red.left-SafeMargin)) &&
368 ((long) ScaleQuantumToChar(p->red) <=
369 (cluster->red.right+SafeMargin)) &&
370 ((long) ScaleQuantumToChar(p->green) >=
371 (cluster->green.left-SafeMargin)) &&
372 ((long) ScaleQuantumToChar(p->green) <=
373 (cluster->green.right+SafeMargin)) &&
374 ((long) ScaleQuantumToChar(p->blue) >=
375 (cluster->blue.left-SafeMargin)) &&
376 ((long) ScaleQuantumToChar(p->blue) <=
377 (cluster->blue.right+SafeMargin)))
378 {
379 /*
380 Count this pixel.
381 */
382 count++;
383 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(p->red);
384 cluster->green.center+=(MagickRealType)
385 ScaleQuantumToChar(p->green);
386 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(p->blue);
387 cluster->count++;
388 break;
389 }
390 p++;
391 }
392 if (image->progress_monitor != (MagickProgressMonitor) NULL)
393 {
394 MagickBooleanType
395 proceed;
396
397#if defined(MAGICKCORE_OPENMP_SUPPORT)
398 #pragma omp critical (MagickCore_Classify)
399#endif
400 proceed=SetImageProgress(image,SegmentImageTag,progress++,
401 2*image->rows);
402 if (proceed == MagickFalse)
403 status=MagickFalse;
404 }
405 }
406 image_view=DestroyCacheView(image_view);
407 /*
408 Remove clusters that do not meet minimum cluster threshold.
409 */
410 count=0;
411 last_cluster=head;
412 next_cluster=head;
413 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
414 {
415 next_cluster=cluster->next;
416 if ((cluster->count > 0) &&
417 (cluster->count >= (count*cluster_threshold/100.0)))
418 {
419 /*
420 Initialize cluster.
421 */
422 cluster->id=count;
423 cluster->red.center/=cluster->count;
424 cluster->green.center/=cluster->count;
425 cluster->blue.center/=cluster->count;
426 count++;
427 last_cluster=cluster;
428 continue;
429 }
430 /*
431 Delete cluster.
432 */
433 if (cluster == head)
434 head=next_cluster;
435 else
436 last_cluster->next=next_cluster;
437 cluster=(Cluster *) RelinquishMagickMemory(cluster);
438 }
439 number_clusters=(unsigned long) count;
440 if (verbose != MagickFalse)
441 {
442 /*
443 Print cluster statistics.
444 */
445 (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
446 (void) fprintf(stdout,"===================\n\n");
447 (void) fprintf(stdout,"\tCluster Threshold = %g\n",cluster_threshold);
448 (void) fprintf(stdout,"\tWeighting Exponent = %g\n",weighting_exponent);
449 (void) fprintf(stdout,"\tTotal Number of Clusters = %lu\n\n",
450 number_clusters);
451 /*
452 Print the total number of points per cluster.
453 */
454 (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
455 (void) fprintf(stdout,"=============================\n\n");
456 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
457 (void) fprintf(stdout,"Cluster #%ld = %ld\n",cluster->id,
458 cluster->count);
459 /*
460 Print the cluster extents.
461 */
462 (void) fprintf(stdout,
463 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
464 (void) fprintf(stdout,"================");
465 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
466 {
467 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
468 (void) fprintf(stdout,"%ld-%ld %ld-%ld %ld-%ld\n",cluster->red.left,
469 cluster->red.right,cluster->green.left,cluster->green.right,
470 cluster->blue.left,cluster->blue.right);
471 }
472 /*
473 Print the cluster center values.
474 */
475 (void) fprintf(stdout,
476 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
477 (void) fprintf(stdout,"=====================");
478 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
479 {
480 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
481 (void) fprintf(stdout,"%g %g %g\n",(double) cluster->red.center,
482 (double) cluster->green.center,(double) cluster->blue.center);
483 }
484 (void) fprintf(stdout,"\n");
485 }
486 if (number_clusters > 256)
487 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
488 /*
489 Speed up distance calculations.
490 */
491 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
492 if (squares == (MagickRealType *) NULL)
493 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
494 image->filename);
495 squares+=255;
496 for (i=(-255); i <= 255; i++)
497 squares[i]=(MagickRealType) i*(MagickRealType) i;
498 /*
499 Allocate image colormap.
500 */
501 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
502 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
503 image->filename);
504 i=0;
505 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
506 {
507 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
508 (cluster->red.center+0.5));
509 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
510 (cluster->green.center+0.5));
511 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
512 (cluster->blue.center+0.5));
513 i++;
514 }
515 /*
516 Do course grain classes.
517 */
518 exception=(&image->exception);
519 image_view=AcquireCacheView(image);
520#if defined(MAGICKCORE_OPENMP_SUPPORT)
521 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
522#endif
523 for (y=0; y < (long) image->rows; y++)
524 {
525 Cluster
526 *cluster;
527
528 register const PixelPacket
529 *__restrict p;
530
531 register IndexPacket
532 *__restrict indexes;
533
534 register long
535 x;
536
537 register PixelPacket
538 *__restrict q;
539
540 if (status == MagickFalse)
541 continue;
542 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
543 if (q == (PixelPacket *) NULL)
544 {
545 status=MagickFalse;
546 continue;
547 }
548 indexes=GetCacheViewAuthenticIndexQueue(image_view);
549 for (x=0; x < (long) image->columns; x++)
550 {
551 indexes[x]=(IndexPacket) 0;
552 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
553 {
554 if (((long) ScaleQuantumToChar(q->red) >=
555 (cluster->red.left-SafeMargin)) &&
556 ((long) ScaleQuantumToChar(q->red) <=
557 (cluster->red.right+SafeMargin)) &&
558 ((long) ScaleQuantumToChar(q->green) >=
559 (cluster->green.left-SafeMargin)) &&
560 ((long) ScaleQuantumToChar(q->green) <=
561 (cluster->green.right+SafeMargin)) &&
562 ((long) ScaleQuantumToChar(q->blue) >=
563 (cluster->blue.left-SafeMargin)) &&
564 ((long) ScaleQuantumToChar(q->blue) <=
565 (cluster->blue.right+SafeMargin)))
566 {
567 /*
568 Classify this pixel.
569 */
570 indexes[x]=(IndexPacket) cluster->id;
571 break;
572 }
573 }
574 if (cluster == (Cluster *) NULL)
575 {
576 MagickRealType
577 distance_squared,
578 local_minima,
579 numerator,
580 ratio,
581 sum;
582
583 register long
584 j,
585 k;
586
587 /*
588 Compute fuzzy membership.
589 */
590 local_minima=0.0;
591 for (j=0; j < (long) image->colors; j++)
592 {
593 sum=0.0;
594 p=image->colormap+j;
595 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
596 (long) ScaleQuantumToChar(p->red)]+
597 squares[(long) ScaleQuantumToChar(q->green)-
598 (long) ScaleQuantumToChar(p->green)]+
599 squares[(long) ScaleQuantumToChar(q->blue)-
600 (long) ScaleQuantumToChar(p->blue)];
601 numerator=distance_squared;
602 for (k=0; k < (long) image->colors; k++)
603 {
604 p=image->colormap+k;
605 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
606 (long) ScaleQuantumToChar(p->red)]+
607 squares[(long) ScaleQuantumToChar(q->green)-
608 (long) ScaleQuantumToChar(p->green)]+
609 squares[(long) ScaleQuantumToChar(q->blue)-
610 (long) ScaleQuantumToChar(p->blue)];
611 ratio=numerator/distance_squared;
612 sum+=SegmentPower(ratio);
613 }
614 if ((sum != 0.0) && ((1.0/sum) > local_minima))
615 {
616 /*
617 Classify this pixel.
618 */
619 local_minima=1.0/sum;
620 indexes[x]=(IndexPacket) j;
621 }
622 }
623 }
624 q++;
625 }
626 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
627 status=MagickFalse;
628 if (image->progress_monitor != (MagickProgressMonitor) NULL)
629 {
630 MagickBooleanType
631 proceed;
632
633#if defined(MAGICKCORE_OPENMP_SUPPORT)
634 #pragma omp critical (MagickCore_Classify)
635#endif
636 proceed=SetImageProgress(image,SegmentImageTag,progress++,
637 2*image->rows);
638 if (proceed == MagickFalse)
639 status=MagickFalse;
640 }
641 }
642 image_view=DestroyCacheView(image_view);
643 status&=SyncImage(image);
644 /*
645 Relinquish resources.
646 */
647 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
648 {
649 next_cluster=cluster->next;
650 cluster=(Cluster *) RelinquishMagickMemory(cluster);
651 }
652 squares-=255;
653 free_squares=squares;
654 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
655 return(MagickTrue);
656}
657
658/*
659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
660% %
661% %
662% %
663+ C o n s o l i d a t e C r o s s i n g s %
664% %
665% %
666% %
667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668%
669% ConsolidateCrossings() guarantees that an even number of zero crossings
670% always lie between two crossings.
671%
672% The format of the ConsolidateCrossings method is:
673%
674% ConsolidateCrossings(ZeroCrossing *zero_crossing,
675% const unsigned long number_crossings)
676%
677% A description of each parameter follows.
678%
679% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
680%
681% o number_crossings: This unsigned long specifies the number of elements
682% in the zero_crossing array.
683%
684*/
685
686static inline long MagickAbsoluteValue(const long x)
687{
688 if (x < 0)
689 return(-x);
690 return(x);
691}
692
693static inline long MagickMax(const long x,const long y)
694{
695 if (x > y)
696 return(x);
697 return(y);
698}
699
700static inline long MagickMin(const long x,const long y)
701{
702 if (x < y)
703 return(x);
704 return(y);
705}
706
707static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
708 const unsigned long number_crossings)
709{
710 long
711 center,
712 correct,
713 count,
714 left,
715 right;
716
717 register long
718 i,
719 j,
720 k,
721 l;
722
723 /*
724 Consolidate zero crossings.
725 */
726 for (i=(long) number_crossings-1; i >= 0; i--)
727 for (j=0; j <= 255; j++)
728 {
729 if (zero_crossing[i].crossings[j] == 0)
730 continue;
731 /*
732 Find the entry that is closest to j and still preserves the
733 property that there are an even number of crossings between
734 intervals.
735 */
736 for (k=j-1; k > 0; k--)
737 if (zero_crossing[i+1].crossings[k] != 0)
738 break;
739 left=MagickMax(k,0);
740 center=j;
741 for (k=j+1; k < 255; k++)
742 if (zero_crossing[i+1].crossings[k] != 0)
743 break;
744 right=MagickMin(k,255);
745 /*
746 K is the zero crossing just left of j.
747 */
748 for (k=j-1; k > 0; k--)
749 if (zero_crossing[i].crossings[k] != 0)
750 break;
751 if (k < 0)
752 k=0;
753 /*
754 Check center for an even number of crossings between k and j.
755 */
756 correct=(-1);
757 if (zero_crossing[i+1].crossings[j] != 0)
758 {
759 count=0;
760 for (l=k+1; l < center; l++)
761 if (zero_crossing[i+1].crossings[l] != 0)
762 count++;
763 if (((count % 2) == 0) && (center != k))
764 correct=center;
765 }
766 /*
767 Check left for an even number of crossings between k and j.
768 */
769 if (correct == -1)
770 {
771 count=0;
772 for (l=k+1; l < left; l++)
773 if (zero_crossing[i+1].crossings[l] != 0)
774 count++;
775 if (((count % 2) == 0) && (left != k))
776 correct=left;
777 }
778 /*
779 Check right for an even number of crossings between k and j.
780 */
781 if (correct == -1)
782 {
783 count=0;
784 for (l=k+1; l < right; l++)
785 if (zero_crossing[i+1].crossings[l] != 0)
786 count++;
787 if (((count % 2) == 0) && (right != k))
788 correct=right;
789 }
790 l=zero_crossing[i].crossings[j];
791 zero_crossing[i].crossings[j]=0;
792 if (correct != -1)
793 zero_crossing[i].crossings[correct]=(short) l;
794 }
795}
796
797/*
798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799% %
800% %
801% %
802+ D e f i n e R e g i o n %
803% %
804% %
805% %
806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807%
808% DefineRegion() defines the left and right boundaries of a peak region.
809%
810% The format of the DefineRegion method is:
811%
812% long DefineRegion(const short *extrema,ExtentPacket *extents)
813%
814% A description of each parameter follows.
815%
816% o extrema: Specifies a pointer to an array of integers. They
817% represent the peaks and valleys of the histogram for each color
818% component.
819%
820% o extents: This pointer to an ExtentPacket represent the extends
821% of a particular peak or valley of a color component.
822%
823*/
824static long DefineRegion(const short *extrema,ExtentPacket *extents)
825{
826 /*
827 Initialize to default values.
828 */
829 extents->left=0;
830 extents->center=0.0;
831 extents->right=255;
832 /*
833 Find the left side (maxima).
834 */
835 for ( ; extents->index <= 255; extents->index++)
836 if (extrema[extents->index] > 0)
837 break;
838 if (extents->index > 255)
839 return(MagickFalse); /* no left side - no region exists */
840 extents->left=extents->index;
841 /*
842 Find the right side (minima).
843 */
844 for ( ; extents->index <= 255; extents->index++)
845 if (extrema[extents->index] < 0)
846 break;
847 extents->right=extents->index-1;
848 return(MagickTrue);
849}
850
851/*
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853% %
854% %
855% %
856+ D e r i v a t i v e H i s t o g r a m %
857% %
858% %
859% %
860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861%
862% DerivativeHistogram() determines the derivative of the histogram using
863% central differencing.
864%
865% The format of the DerivativeHistogram method is:
866%
867% DerivativeHistogram(const MagickRealType *histogram,
868% MagickRealType *derivative)
869%
870% A description of each parameter follows.
871%
872% o histogram: Specifies an array of MagickRealTypes representing the number
873% of pixels for each intensity of a particular color component.
874%
875% o derivative: This array of MagickRealTypes is initialized by
876% DerivativeHistogram to the derivative of the histogram using central
877% differencing.
878%
879*/
880static void DerivativeHistogram(const MagickRealType *histogram,
881 MagickRealType *derivative)
882{
883 register long
884 i,
885 n;
886
887 /*
888 Compute endpoints using second order polynomial interpolation.
889 */
890 n=255;
891 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
892 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
893 /*
894 Compute derivative using central differencing.
895 */
896 for (i=1; i < n; i++)
897 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
898 return;
899}
900
901/*
902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903% %
904% %
905% %
906+ G e t I m a g e D y n a m i c T h r e s h o l d %
907% %
908% %
909% %
910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911%
912% GetImageDynamicThreshold() returns the dynamic threshold for an image.
913%
914% The format of the GetImageDynamicThreshold method is:
915%
916% MagickBooleanType GetImageDynamicThreshold(const Image *image,
917% const double cluster_threshold,const double smooth_threshold,
918% MagickPixelPacket *pixel,ExceptionInfo *exception)
919%
920% A description of each parameter follows.
921%
922% o image: the image.
923%
924% o cluster_threshold: This MagickRealType represents the minimum number of
925% pixels contained in a hexahedra before it can be considered valid
926% (expressed as a percentage).
927%
928% o smooth_threshold: the smoothing threshold eliminates noise in the second
929% derivative of the histogram. As the value is increased, you can expect a
930% smoother second derivative.
931%
932% o pixel: return the dynamic threshold here.
933%
934% o exception: return any errors or warnings in this structure.
935%
936*/
937MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
938 const double cluster_threshold,const double smooth_threshold,
939 MagickPixelPacket *pixel,ExceptionInfo *exception)
940{
941 Cluster
942 *background,
943 *cluster,
944 *object,
945 *head,
946 *last_cluster,
947 *next_cluster;
948
949 ExtentPacket
950 blue,
951 green,
952 red;
953
954 long
955 count,
956 *histogram[MaxDimension],
957 y;
958
959 MagickBooleanType
960 proceed;
961
962 MagickRealType
963 threshold;
964
965 register const PixelPacket
966 *p;
967
968 register long
969 i,
970 x;
971
972 short
973 *extrema[MaxDimension];
974
975 /*
976 Allocate histogram and extrema.
977 */
978 assert(image != (Image *) NULL);
979 assert(image->signature == MagickSignature);
980 if (image->debug != MagickFalse)
981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
982 GetMagickPixelPacket(image,pixel);
983 for (i=0; i < MaxDimension; i++)
984 {
985 histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
986 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
987 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
988 {
989 for (i-- ; i >= 0; i--)
990 {
991 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
992 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
993 }
994 (void) ThrowMagickException(exception,GetMagickModule(),
995 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
996 return(MagickFalse);
997 }
998 }
999 /*
1000 Initialize histogram.
1001 */
1002 InitializeHistogram(image,histogram,exception);
1003 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
1004 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
1005 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
1006 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
1007 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
1008 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
1009 /*
1010 Form clusters.
1011 */
1012 cluster=(Cluster *) NULL;
1013 head=(Cluster *) NULL;
1014 (void) ResetMagickMemory(&red,0,sizeof(red));
1015 (void) ResetMagickMemory(&green,0,sizeof(green));
1016 (void) ResetMagickMemory(&blue,0,sizeof(blue));
1017 while (DefineRegion(extrema[Red],&red) != 0)
1018 {
1019 green.index=0;
1020 while (DefineRegion(extrema[Green],&green) != 0)
1021 {
1022 blue.index=0;
1023 while (DefineRegion(extrema[Blue],&blue) != 0)
1024 {
1025 /*
1026 Allocate a new class.
1027 */
1028 if (head != (Cluster *) NULL)
1029 {
1030 cluster->next=(Cluster *) AcquireMagickMemory(
1031 sizeof(*cluster->next));
1032 cluster=cluster->next;
1033 }
1034 else
1035 {
1036 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
1037 head=cluster;
1038 }
1039 if (cluster == (Cluster *) NULL)
1040 {
1041 (void) ThrowMagickException(exception,GetMagickModule(),
1042 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1043 image->filename);
1044 return(MagickFalse);
1045 }
1046 /*
1047 Initialize a new class.
1048 */
1049 cluster->count=0;
1050 cluster->red=red;
1051 cluster->green=green;
1052 cluster->blue=blue;
1053 cluster->next=(Cluster *) NULL;
1054 }
1055 }
1056 }
1057 if (head == (Cluster *) NULL)
1058 {
1059 /*
1060 No classes were identified-- create one.
1061 */
1062 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
1063 if (cluster == (Cluster *) NULL)
1064 {
1065 (void) ThrowMagickException(exception,GetMagickModule(),
1066 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1067 return(MagickFalse);
1068 }
1069 /*
1070 Initialize a new class.
1071 */
1072 cluster->count=0;
1073 cluster->red=red;
1074 cluster->green=green;
1075 cluster->blue=blue;
1076 cluster->next=(Cluster *) NULL;
1077 head=cluster;
1078 }
1079 /*
1080 Count the pixels for each cluster.
1081 */
1082 count=0;
1083 for (y=0; y < (long) image->rows; y++)
1084 {
1085 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1086 if (p == (const PixelPacket *) NULL)
1087 break;
1088 for (x=0; x < (long) image->columns; x++)
1089 {
1090 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
1091 if (((long) ScaleQuantumToChar(p->red) >=
1092 (cluster->red.left-SafeMargin)) &&
1093 ((long) ScaleQuantumToChar(p->red) <=
1094 (cluster->red.right+SafeMargin)) &&
1095 ((long) ScaleQuantumToChar(p->green) >=
1096 (cluster->green.left-SafeMargin)) &&
1097 ((long) ScaleQuantumToChar(p->green) <=
1098 (cluster->green.right+SafeMargin)) &&
1099 ((long) ScaleQuantumToChar(p->blue) >=
1100 (cluster->blue.left-SafeMargin)) &&
1101 ((long) ScaleQuantumToChar(p->blue) <=
1102 (cluster->blue.right+SafeMargin)))
1103 {
1104 /*
1105 Count this pixel.
1106 */
1107 count++;
1108 cluster->red.center+=(MagickRealType)
1109 ScaleQuantumToChar(p->red);
1110 cluster->green.center+=(MagickRealType)
1111 ScaleQuantumToChar(p->green);
1112 cluster->blue.center+=(MagickRealType)
1113 ScaleQuantumToChar(p->blue);
1114 cluster->count++;
1115 break;
1116 }
1117 p++;
1118 }
1119 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
1120 if (proceed == MagickFalse)
1121 break;
1122 }
1123 /*
1124 Remove clusters that do not meet minimum cluster threshold.
1125 */
1126 count=0;
1127 last_cluster=head;
1128 next_cluster=head;
1129 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1130 {
1131 next_cluster=cluster->next;
1132 if ((cluster->count > 0) &&
1133 (cluster->count >= (count*cluster_threshold/100.0)))
1134 {
1135 /*
1136 Initialize cluster.
1137 */
1138 cluster->id=count;
1139 cluster->red.center/=cluster->count;
1140 cluster->green.center/=cluster->count;
1141 cluster->blue.center/=cluster->count;
1142 count++;
1143 last_cluster=cluster;
1144 continue;
1145 }
1146 /*
1147 Delete cluster.
1148 */
1149 if (cluster == head)
1150 head=next_cluster;
1151 else
1152 last_cluster->next=next_cluster;
1153 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1154 }
1155 object=head;
1156 background=head;
1157 if (count > 1)
1158 {
1159 object=head->next;
1160 for (cluster=object; cluster->next != (Cluster *) NULL; )
1161 {
1162 if (cluster->count < object->count)
1163 object=cluster;
1164 cluster=cluster->next;
1165 }
1166 background=head->next;
1167 for (cluster=background; cluster->next != (Cluster *) NULL; )
1168 {
1169 if (cluster->count > background->count)
1170 background=cluster;
1171 cluster=cluster->next;
1172 }
1173 }
1174 threshold=(background->red.center+object->red.center)/2.0;
1175 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
1176 (threshold+0.5));
1177 threshold=(background->green.center+object->green.center)/2.0;
1178 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
1179 (threshold+0.5));
1180 threshold=(background->blue.center+object->blue.center)/2.0;
1181 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
1182 (threshold+0.5));
1183 /*
1184 Relinquish resources.
1185 */
1186 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1187 {
1188 next_cluster=cluster->next;
1189 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1190 }
1191 for (i=0; i < MaxDimension; i++)
1192 {
1193 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1194 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1195 }
1196 return(MagickTrue);
1197}
1198
1199/*
1200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201% %
1202% %
1203% %
1204+ I n i t i a l i z e H i s t o g r a m %
1205% %
1206% %
1207% %
1208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209%
1210% InitializeHistogram() computes the histogram for an image.
1211%
1212% The format of the InitializeHistogram method is:
1213%
1214% InitializeHistogram(const Image *image,long **histogram)
1215%
1216% A description of each parameter follows.
1217%
1218% o image: Specifies a pointer to an Image structure; returned from
1219% ReadImage.
1220%
1221% o histogram: Specifies an array of integers representing the number
1222% of pixels for each intensity of a particular color component.
1223%
1224*/
1225static void InitializeHistogram(const Image *image,long **histogram,
1226 ExceptionInfo *exception)
1227{
1228 long
1229 y;
1230
1231 register const PixelPacket
1232 *p;
1233
1234 register long
1235 i,
1236 x;
1237
1238 /*
1239 Initialize histogram.
1240 */
1241 for (i=0; i <= 255; i++)
1242 {
1243 histogram[Red][i]=0;
1244 histogram[Green][i]=0;
1245 histogram[Blue][i]=0;
1246 }
1247 for (y=0; y < (long) image->rows; y++)
1248 {
1249 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1250 if (p == (const PixelPacket *) NULL)
1251 break;
1252 for (x=0; x < (long) image->columns; x++)
1253 {
1254 histogram[Red][(long) ScaleQuantumToChar(p->red)]++;
1255 histogram[Green][(long) ScaleQuantumToChar(p->green)]++;
1256 histogram[Blue][(long) ScaleQuantumToChar(p->blue)]++;
1257 p++;
1258 }
1259 }
1260}
1261
1262/*
1263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264% %
1265% %
1266% %
1267+ I n i t i a l i z e I n t e r v a l T r e e %
1268% %
1269% %
1270% %
1271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272%
1273% InitializeIntervalTree() initializes an interval tree from the lists of
1274% zero crossings.
1275%
1276% The format of the InitializeIntervalTree method is:
1277%
1278% InitializeIntervalTree(IntervalTree **list,long *number_nodes,
1279% IntervalTree *node)
1280%
1281% A description of each parameter follows.
1282%
1283% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
1284%
1285% o number_crossings: This unsigned long specifies the number of elements
1286% in the zero_crossing array.
1287%
1288*/
1289
1290static void InitializeList(IntervalTree **list,long *number_nodes,
1291 IntervalTree *node)
1292{
1293 if (node == (IntervalTree *) NULL)
1294 return;
1295 if (node->child == (IntervalTree *) NULL)
1296 list[(*number_nodes)++]=node;
1297 InitializeList(list,number_nodes,node->sibling);
1298 InitializeList(list,number_nodes,node->child);
1299}
1300
1301static void MeanStability(IntervalTree *node)
1302{
1303 register IntervalTree
1304 *child;
1305
1306 if (node == (IntervalTree *) NULL)
1307 return;
1308 node->mean_stability=0.0;
1309 child=node->child;
1310 if (child != (IntervalTree *) NULL)
1311 {
1312 register long
1313 count;
1314
1315 register MagickRealType
1316 sum;
1317
1318 sum=0.0;
1319 count=0;
1320 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
1321 {
1322 sum+=child->stability;
1323 count++;
1324 }
1325 node->mean_stability=sum/(MagickRealType) count;
1326 }
1327 MeanStability(node->sibling);
1328 MeanStability(node->child);
1329}
1330
1331static void Stability(IntervalTree *node)
1332{
1333 if (node == (IntervalTree *) NULL)
1334 return;
1335 if (node->child == (IntervalTree *) NULL)
1336 node->stability=0.0;
1337 else
1338 node->stability=node->tau-(node->child)->tau;
1339 Stability(node->sibling);
1340 Stability(node->child);
1341}
1342
1343static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
1344 const unsigned long number_crossings)
1345{
1346 IntervalTree
1347 *head,
1348 **list,
1349 *node,
1350 *root;
1351
1352 long
1353 j,
1354 k,
1355 left,
1356 number_nodes;
1357
1358 register long
1359 i;
1360
1361 /*
1362 Allocate interval tree.
1363 */
1364 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1365 sizeof(*list));
1366 if (list == (IntervalTree **) NULL)
1367 return((IntervalTree *) NULL);
1368 /*
1369 The root is the entire histogram.
1370 */
1371 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
1372 root->child=(IntervalTree *) NULL;
1373 root->sibling=(IntervalTree *) NULL;
1374 root->tau=0.0;
1375 root->left=0;
1376 root->right=255;
1377 for (i=(-1); i < (long) number_crossings; i++)
1378 {
1379 /*
1380 Initialize list with all nodes with no children.
1381 */
1382 number_nodes=0;
1383 InitializeList(list,&number_nodes,root);
1384 /*
1385 Split list.
1386 */
1387 for (j=0; j < number_nodes; j++)
1388 {
1389 head=list[j];
1390 left=head->left;
1391 node=head;
1392 for (k=head->left+1; k < head->right; k++)
1393 {
1394 if (zero_crossing[i+1].crossings[k] != 0)
1395 {
1396 if (node == head)
1397 {
1398 node->child=(IntervalTree *) AcquireMagickMemory(
1399 sizeof(*node->child));
1400 node=node->child;
1401 }
1402 else
1403 {
1404 node->sibling=(IntervalTree *) AcquireMagickMemory(
1405 sizeof(*node->sibling));
1406 node=node->sibling;
1407 }
1408 node->tau=zero_crossing[i+1].tau;
1409 node->child=(IntervalTree *) NULL;
1410 node->sibling=(IntervalTree *) NULL;
1411 node->left=left;
1412 node->right=k;
1413 left=k;
1414 }
1415 }
1416 if (left != head->left)
1417 {
1418 node->sibling=(IntervalTree *) AcquireMagickMemory(
1419 sizeof(*node->sibling));
1420 node=node->sibling;
1421 node->tau=zero_crossing[i+1].tau;
1422 node->child=(IntervalTree *) NULL;
1423 node->sibling=(IntervalTree *) NULL;
1424 node->left=left;
1425 node->right=head->right;
1426 }
1427 }
1428 }
1429 /*
1430 Determine the stability: difference between a nodes tau and its child.
1431 */
1432 Stability(root->child);
1433 MeanStability(root->child);
1434 list=(IntervalTree **) RelinquishMagickMemory(list);
1435 return(root);
1436}
1437
1438/*
1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440% %
1441% %
1442% %
1443+ O p t i m a l T a u %
1444% %
1445% %
1446% %
1447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448%
1449% OptimalTau() finds the optimal tau for each band of the histogram.
1450%
1451% The format of the OptimalTau method is:
1452%
1453% MagickRealType OptimalTau(const long *histogram,const double max_tau,
1454% const double min_tau,const double delta_tau,
1455% const double smooth_threshold,short *extrema)
1456%
1457% A description of each parameter follows.
1458%
1459% o histogram: Specifies an array of integers representing the number
1460% of pixels for each intensity of a particular color component.
1461%
1462% o extrema: Specifies a pointer to an array of integers. They
1463% represent the peaks and valleys of the histogram for each color
1464% component.
1465%
1466*/
1467
1468static void ActiveNodes(IntervalTree **list,long *number_nodes,
1469 IntervalTree *node)
1470{
1471 if (node == (IntervalTree *) NULL)
1472 return;
1473 if (node->stability >= node->mean_stability)
1474 {
1475 list[(*number_nodes)++]=node;
1476 ActiveNodes(list,number_nodes,node->sibling);
1477 }
1478 else
1479 {
1480 ActiveNodes(list,number_nodes,node->sibling);
1481 ActiveNodes(list,number_nodes,node->child);
1482 }
1483}
1484
1485static void FreeNodes(IntervalTree *node)
1486{
1487 if (node == (IntervalTree *) NULL)
1488 return;
1489 FreeNodes(node->sibling);
1490 FreeNodes(node->child);
1491 node=(IntervalTree *) RelinquishMagickMemory(node);
1492}
1493
1494static MagickRealType OptimalTau(const long *histogram,const double max_tau,
1495 const double min_tau,const double delta_tau,const double smooth_threshold,
1496 short *extrema)
1497{
1498 IntervalTree
1499 **list,
1500 *node,
1501 *root;
1502
1503 long
1504 index,
1505 j,
1506 k,
1507 number_nodes;
1508
1509 MagickRealType
1510 average_tau,
1511 *derivative,
1512 *second_derivative,
1513 tau,
1514 value;
1515
1516 register long
1517 i,
1518 x;
1519
1520 MagickBooleanType
1521 peak;
1522
1523 unsigned long
1524 count,
1525 number_crossings;
1526
1527 ZeroCrossing
1528 *zero_crossing;
1529
1530 /*
1531 Allocate interval tree.
1532 */
1533 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1534 sizeof(*list));
1535 if (list == (IntervalTree **) NULL)
1536 return(0.0);
1537 /*
1538 Allocate zero crossing list.
1539 */
1540 count=(unsigned long) ((max_tau-min_tau)/delta_tau)+2;
1541 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
1542 sizeof(*zero_crossing));
1543 if (zero_crossing == (ZeroCrossing *) NULL)
1544 return(0.0);
1545 for (i=0; i < (long) count; i++)
1546 zero_crossing[i].tau=(-1.0);
1547 /*
1548 Initialize zero crossing list.
1549 */
1550 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
1551 second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
1552 sizeof(*second_derivative));
1553 if ((derivative == (MagickRealType *) NULL) ||
1554 (second_derivative == (MagickRealType *) NULL))
1555 ThrowFatalException(ResourceLimitFatalError,
1556 "UnableToAllocateDerivatives");
1557 i=0;
1558 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
1559 {
1560 zero_crossing[i].tau=tau;
1561 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
1562 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1563 DerivativeHistogram(derivative,second_derivative);
1564 ZeroCrossHistogram(second_derivative,smooth_threshold,
1565 zero_crossing[i].crossings);
1566 i++;
1567 }
1568 /*
1569 Add an entry for the original histogram.
1570 */
1571 zero_crossing[i].tau=0.0;
1572 for (j=0; j <= 255; j++)
1573 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
1574 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1575 DerivativeHistogram(derivative,second_derivative);
1576 ZeroCrossHistogram(second_derivative,smooth_threshold,
1577 zero_crossing[i].crossings);
1578 number_crossings=(unsigned long) i;
1579 derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
1580 second_derivative=(MagickRealType *)
1581 RelinquishMagickMemory(second_derivative);
1582 /*
1583 Ensure the scale-space fingerprints form lines in scale-space, not loops.
1584 */
1585 ConsolidateCrossings(zero_crossing,number_crossings);
1586 /*
1587 Force endpoints to be included in the interval.
1588 */
1589 for (i=0; i <= (long) number_crossings; i++)
1590 {
1591 for (j=0; j < 255; j++)
1592 if (zero_crossing[i].crossings[j] != 0)
1593 break;
1594 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
1595 for (j=255; j > 0; j--)
1596 if (zero_crossing[i].crossings[j] != 0)
1597 break;
1598 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
1599 }
1600 /*
1601 Initialize interval tree.
1602 */
1603 root=InitializeIntervalTree(zero_crossing,number_crossings);
1604 if (root == (IntervalTree *) NULL)
1605 return(0.0);
1606 /*
1607 Find active nodes: stability is greater (or equal) to the mean stability of
1608 its children.
1609 */
1610 number_nodes=0;
1611 ActiveNodes(list,&number_nodes,root->child);
1612 /*
1613 Initialize extrema.
1614 */
1615 for (i=0; i <= 255; i++)
1616 extrema[i]=0;
1617 for (i=0; i < number_nodes; i++)
1618 {
1619 /*
1620 Find this tau in zero crossings list.
1621 */
1622 k=0;
1623 node=list[i];
1624 for (j=0; j <= (long) number_crossings; j++)
1625 if (zero_crossing[j].tau == node->tau)
1626 k=j;
1627 /*
1628 Find the value of the peak.
1629 */
1630 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
1631 MagickFalse;
1632 index=node->left;
1633 value=zero_crossing[k].histogram[index];
1634 for (x=node->left; x <= node->right; x++)
1635 {
1636 if (peak != MagickFalse)
1637 {
1638 if (zero_crossing[k].histogram[x] > value)
1639 {
1640 value=zero_crossing[k].histogram[x];
1641 index=x;
1642 }
1643 }
1644 else
1645 if (zero_crossing[k].histogram[x] < value)
1646 {
1647 value=zero_crossing[k].histogram[x];
1648 index=x;
1649 }
1650 }
1651 for (x=node->left; x <= node->right; x++)
1652 {
1653 if (index == 0)
1654 index=256;
1655 if (peak != MagickFalse)
1656 extrema[x]=(short) index;
1657 else
1658 extrema[x]=(short) (-index);
1659 }
1660 }
1661 /*
1662 Determine the average tau.
1663 */
1664 average_tau=0.0;
1665 for (i=0; i < number_nodes; i++)
1666 average_tau+=list[i]->tau;
1667 average_tau/=(MagickRealType) number_nodes;
1668 /*
1669 Relinquish resources.
1670 */
1671 FreeNodes(root);
1672 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
1673 list=(IntervalTree **) RelinquishMagickMemory(list);
1674 return(average_tau);
1675}
1676
1677/*
1678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1679% %
1680% %
1681% %
1682+ S c a l e S p a c e %
1683% %
1684% %
1685% %
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687%
1688% ScaleSpace() performs a scale-space filter on the 1D histogram.
1689%
1690% The format of the ScaleSpace method is:
1691%
1692% ScaleSpace(const long *histogram,const MagickRealType tau,
1693% MagickRealType *scale_histogram)
1694%
1695% A description of each parameter follows.
1696%
1697% o histogram: Specifies an array of MagickRealTypes representing the number
1698% of pixels for each intensity of a particular color component.
1699%
1700*/
1701
1702static void ScaleSpace(const long *histogram,const MagickRealType tau,
1703 MagickRealType *scale_histogram)
1704{
1705 MagickRealType
1706 alpha,
1707 beta,
1708 *gamma,
1709 sum;
1710
1711 register long
1712 u,
1713 x;
1714
1715 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
1716 if (gamma == (MagickRealType *) NULL)
1717 ThrowFatalException(ResourceLimitFatalError,
1718 "UnableToAllocateGammaMap");
1719 alpha=1.0/(tau*sqrt(2.0*MagickPI));
1720 beta=(-1.0/(2.0*tau*tau));
1721 for (x=0; x <= 255; x++)
1722 gamma[x]=0.0;
1723 for (x=0; x <= 255; x++)
1724 {
1725 gamma[x]=exp((double) beta*x*x);
1726 if (gamma[x] < MagickEpsilon)
1727 break;
1728 }
1729 for (x=0; x <= 255; x++)
1730 {
1731 sum=0.0;
1732 for (u=0; u <= 255; u++)
1733 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
1734 scale_histogram[x]=alpha*sum;
1735 }
1736 gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
1737}
1738
1739/*
1740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741% %
1742% %
1743% %
1744% S e g m e n t I m a g e %
1745% %
1746% %
1747% %
1748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1749%
1750% SegmentImage() segment an image by analyzing the histograms of the color
1751% components and identifying units that are homogeneous with the fuzzy
1752% C-means technique.
1753%
1754% The format of the SegmentImage method is:
1755%
1756% MagickBooleanType SegmentImage(Image *image,
1757% const ColorspaceType colorspace,const MagickBooleanType verbose,
1758% const double cluster_threshold,const double smooth_threshold)
1759%
1760% A description of each parameter follows.
1761%
1762% o image: the image.
1763%
1764% o colorspace: Indicate the colorspace.
1765%
1766% o verbose: Set to MagickTrue to print detailed information about the
1767% identified classes.
1768%
1769% o cluster_threshold: This represents the minimum number of pixels
1770% contained in a hexahedra before it can be considered valid (expressed
1771% as a percentage).
1772%
1773% o smooth_threshold: the smoothing threshold eliminates noise in the second
1774% derivative of the histogram. As the value is increased, you can expect a
1775% smoother second derivative.
1776%
1777*/
1778MagickExport MagickBooleanType SegmentImage(Image *image,
1779 const ColorspaceType colorspace,const MagickBooleanType verbose,
1780 const double cluster_threshold,const double smooth_threshold)
1781{
1782 long
1783 *histogram[MaxDimension];
1784
1785 MagickBooleanType
1786 status;
1787
1788 register long
1789 i;
1790
1791 short
1792 *extrema[MaxDimension];
1793
1794 /*
1795 Allocate histogram and extrema.
1796 */
1797 assert(image != (Image *) NULL);
1798 assert(image->signature == MagickSignature);
1799 if (image->debug != MagickFalse)
1800 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1801 for (i=0; i < MaxDimension; i++)
1802 {
1803 histogram[i]=(long *) AcquireQuantumMemory(256,sizeof(**histogram));
1804 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
1805 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
1806 {
1807 for (i-- ; i >= 0; i--)
1808 {
1809 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1810 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1811 }
1812 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1813 image->filename)
1814 }
1815 }
1816 if (colorspace != RGBColorspace)
1817 (void) TransformImageColorspace(image,colorspace);
1818 /*
1819 Initialize histogram.
1820 */
1821 InitializeHistogram(image,histogram,&image->exception);
1822 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
1823 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
1824 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
1825 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
1826 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
1827 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
1828 /*
1829 Classify using the fuzzy c-Means technique.
1830 */
1831 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
1832 if (colorspace != RGBColorspace)
1833 (void) TransformImageColorspace(image,colorspace);
1834 /*
1835 Relinquish resources.
1836 */
1837 for (i=0; i < MaxDimension; i++)
1838 {
1839 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1840 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1841 }
1842 return(status);
1843}
1844
1845/*
1846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1847% %
1848% %
1849% %
1850+ Z e r o C r o s s H i s t o g r a m %
1851% %
1852% %
1853% %
1854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1855%
1856% ZeroCrossHistogram() find the zero crossings in a histogram and marks
1857% directions as: 1 is negative to positive; 0 is zero crossing; and -1
1858% is positive to negative.
1859%
1860% The format of the ZeroCrossHistogram method is:
1861%
1862% ZeroCrossHistogram(MagickRealType *second_derivative,
1863% const MagickRealType smooth_threshold,short *crossings)
1864%
1865% A description of each parameter follows.
1866%
1867% o second_derivative: Specifies an array of MagickRealTypes representing the
1868% second derivative of the histogram of a particular color component.
1869%
1870% o crossings: This array of integers is initialized with
1871% -1, 0, or 1 representing the slope of the first derivative of the
1872% of a particular color component.
1873%
1874*/
1875static void ZeroCrossHistogram(MagickRealType *second_derivative,
1876 const MagickRealType smooth_threshold,short *crossings)
1877{
1878 long
1879 parity;
1880
1881 register long
1882 i;
1883
1884 /*
1885 Merge low numbers to zero to help prevent noise.
1886 */
1887 for (i=0; i <= 255; i++)
1888 if ((second_derivative[i] < smooth_threshold) &&
1889 (second_derivative[i] >= -smooth_threshold))
1890 second_derivative[i]=0.0;
1891 /*
1892 Mark zero crossings.
1893 */
1894 parity=0;
1895 for (i=0; i <= 255; i++)
1896 {
1897 crossings[i]=0;
1898 if (second_derivative[i] < 0.0)
1899 {
1900 if (parity > 0)
1901 crossings[i]=(-1);
1902 parity=1;
1903 }
1904 else
1905 if (second_derivative[i] > 0.0)
1906 {
1907 if (parity < 0)
1908 crossings[i]=1;
1909 parity=(-1);
1910 }
1911 }
1912}