2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Methods to Segment an Image with Thresholding Fuzzy c-Means %
20 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
46 % The fuzzy c-Means algorithm can be summarized as follows:
48 % o Build a histogram, one for each color component of the image.
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
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
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
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.
72 % Segment is strongly based on software written by Andy Gallo,
73 % University of Delaware.
75 % The following reference was used in creating this program:
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
85 #include "magick/studio.h"
86 #include "magick/cache.h"
87 #include "magick/color.h"
88 #include "magick/colormap.h"
89 #include "magick/colorspace.h"
90 #include "magick/exception.h"
91 #include "magick/exception-private.h"
92 #include "magick/image.h"
93 #include "magick/image-private.h"
94 #include "magick/memory_.h"
95 #include "magick/monitor.h"
96 #include "magick/monitor-private.h"
97 #include "magick/quantize.h"
98 #include "magick/quantum.h"
99 #include "magick/quantum-private.h"
100 #include "magick/segment.h"
101 #include "magick/string_.h"
106 #define MaxDimension 3
107 #define DeltaTau 0.5f
108 #if defined(FastClassify)
109 #define WeightingExponent 2.0
110 #define SegmentPower(ratio) (ratio)
112 #define WeightingExponent 2.5
113 #define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
118 Typedef declarations.
120 typedef struct _ExtentPacket
131 typedef struct _Cluster
146 typedef struct _IntervalTree
164 typedef struct _ZeroCrossing
175 Constant declarations.
187 static MagickRealType
188 OptimalTau(const long *,const double,const double,const double,
189 const double,short *);
192 DefineRegion(const short *,ExtentPacket *);
195 InitializeHistogram(const Image *,long **,ExceptionInfo *),
196 ScaleSpace(const long *,const MagickRealType,MagickRealType *),
197 ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *);
200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210 % Classify() defines one or more classes. Each pixel is thresholded to
211 % determine which class it belongs to. If the class is not identified it is
212 % assigned to the closest class based on the fuzzy c-Means technique.
214 % The format of the Classify method is:
216 % MagickBooleanType Classify(Image *image,short **extrema,
217 % const MagickRealType cluster_threshold,
218 % const MagickRealType weighting_exponent,
219 % const MagickBooleanType verbose)
221 % A description of each parameter follows.
223 % o image: the image.
225 % o extrema: Specifies a pointer to an array of integers. They
226 % represent the peaks and valleys of the histogram for each color
229 % o cluster_threshold: This MagickRealType represents the minimum number of
230 % pixels contained in a hexahedra before it can be considered valid
231 % (expressed as a percentage).
233 % o weighting_exponent: Specifies the membership weighting exponent.
235 % o verbose: A value greater than zero prints detailed information about
236 % the identified classes.
239 static MagickBooleanType Classify(Image *image,short **extrema,
240 const MagickRealType cluster_threshold,
241 const MagickRealType weighting_exponent,const MagickBooleanType verbose)
243 #define SegmentImageTag "Segment/Image"
276 register MagickRealType
285 cluster=(Cluster *) NULL;
286 head=(Cluster *) NULL;
287 (void) ResetMagickMemory(&red,0,sizeof(red));
288 (void) ResetMagickMemory(&green,0,sizeof(green));
289 (void) ResetMagickMemory(&blue,0,sizeof(blue));
290 while (DefineRegion(extrema[Red],&red) != 0)
293 while (DefineRegion(extrema[Green],&green) != 0)
296 while (DefineRegion(extrema[Blue],&blue) != 0)
299 Allocate a new class.
301 if (head != (Cluster *) NULL)
303 cluster->next=(Cluster *) AcquireMagickMemory(
304 sizeof(*cluster->next));
305 cluster=cluster->next;
309 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
312 if (cluster == (Cluster *) NULL)
313 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
316 Initialize a new class.
320 cluster->green=green;
322 cluster->next=(Cluster *) NULL;
326 if (head == (Cluster *) NULL)
329 No classes were identified-- create one.
331 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
332 if (cluster == (Cluster *) NULL)
333 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
336 Initialize a new class.
340 cluster->green=green;
342 cluster->next=(Cluster *) NULL;
346 Count the pixels for each cluster.
351 exception=(&image->exception);
352 image_view=AcquireCacheView(image);
353 for (y=0; y < (long) image->rows; y++)
355 register const PixelPacket
361 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
362 if (p == (const PixelPacket *) NULL)
364 for (x=0; x < (long) image->columns; x++)
366 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
367 if (((long) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
368 (cluster->red.left-SafeMargin)) &&
369 ((long) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
370 (cluster->red.right+SafeMargin)) &&
371 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
372 (cluster->green.left-SafeMargin)) &&
373 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
374 (cluster->green.right+SafeMargin)) &&
375 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
376 (cluster->blue.left-SafeMargin)) &&
377 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
378 (cluster->blue.right+SafeMargin)))
384 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(GetRedPixelComponent(p));
385 cluster->green.center+=(MagickRealType)
386 ScaleQuantumToChar(GetGreenPixelComponent(p));
387 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(GetBluePixelComponent(p));
393 if (image->progress_monitor != (MagickProgressMonitor) NULL)
398 #if defined(MAGICKCORE_OPENMP_SUPPORT)
399 #pragma omp critical (MagickCore_Classify)
401 proceed=SetImageProgress(image,SegmentImageTag,progress++,
403 if (proceed == MagickFalse)
407 image_view=DestroyCacheView(image_view);
409 Remove clusters that do not meet minimum cluster threshold.
414 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
416 next_cluster=cluster->next;
417 if ((cluster->count > 0) &&
418 (cluster->count >= (count*cluster_threshold/100.0)))
424 cluster->red.center/=cluster->count;
425 cluster->green.center/=cluster->count;
426 cluster->blue.center/=cluster->count;
428 last_cluster=cluster;
437 last_cluster->next=next_cluster;
438 cluster=(Cluster *) RelinquishMagickMemory(cluster);
440 number_clusters=(unsigned long) count;
441 if (verbose != MagickFalse)
444 Print cluster statistics.
446 (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
447 (void) fprintf(stdout,"===================\n\n");
448 (void) fprintf(stdout,"\tCluster Threshold = %g\n",(double)
450 (void) fprintf(stdout,"\tWeighting Exponent = %g\n",(double)
452 (void) fprintf(stdout,"\tTotal Number of Clusters = %lu\n\n",
455 Print the total number of points per cluster.
457 (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
458 (void) fprintf(stdout,"=============================\n\n");
459 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
460 (void) fprintf(stdout,"Cluster #%ld = %ld\n",cluster->id,
463 Print the cluster extents.
465 (void) fprintf(stdout,
466 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
467 (void) fprintf(stdout,"================");
468 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
470 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
471 (void) fprintf(stdout,"%ld-%ld %ld-%ld %ld-%ld\n",cluster->red.left,
472 cluster->red.right,cluster->green.left,cluster->green.right,
473 cluster->blue.left,cluster->blue.right);
476 Print the cluster center values.
478 (void) fprintf(stdout,
479 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
480 (void) fprintf(stdout,"=====================");
481 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
483 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
484 (void) fprintf(stdout,"%g %g %g\n",(double)
485 cluster->red.center,(double) cluster->green.center,(double)
486 cluster->blue.center);
488 (void) fprintf(stdout,"\n");
490 if (number_clusters > 256)
491 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
493 Speed up distance calculations.
495 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
496 if (squares == (MagickRealType *) NULL)
497 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
500 for (i=(-255); i <= 255; i++)
501 squares[i]=(MagickRealType) i*(MagickRealType) i;
503 Allocate image colormap.
505 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
506 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
509 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
511 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
512 (cluster->red.center+0.5));
513 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
514 (cluster->green.center+0.5));
515 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
516 (cluster->blue.center+0.5));
520 Do course grain classes.
522 exception=(&image->exception);
523 image_view=AcquireCacheView(image);
524 #if defined(MAGICKCORE_OPENMP_SUPPORT)
525 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
527 for (y=0; y < (long) image->rows; y++)
532 register const PixelPacket
544 if (status == MagickFalse)
546 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
547 if (q == (PixelPacket *) NULL)
552 indexes=GetCacheViewAuthenticIndexQueue(image_view);
553 for (x=0; x < (long) image->columns; x++)
555 indexes[x]=(IndexPacket) 0;
556 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
558 if (((long) ScaleQuantumToChar(q->red) >=
559 (cluster->red.left-SafeMargin)) &&
560 ((long) ScaleQuantumToChar(q->red) <=
561 (cluster->red.right+SafeMargin)) &&
562 ((long) ScaleQuantumToChar(q->green) >=
563 (cluster->green.left-SafeMargin)) &&
564 ((long) ScaleQuantumToChar(q->green) <=
565 (cluster->green.right+SafeMargin)) &&
566 ((long) ScaleQuantumToChar(q->blue) >=
567 (cluster->blue.left-SafeMargin)) &&
568 ((long) ScaleQuantumToChar(q->blue) <=
569 (cluster->blue.right+SafeMargin)))
574 indexes[x]=(IndexPacket) cluster->id;
578 if (cluster == (Cluster *) NULL)
592 Compute fuzzy membership.
595 for (j=0; j < (long) image->colors; j++)
599 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
600 (long) ScaleQuantumToChar(GetRedPixelComponent(p))]+
601 squares[(long) ScaleQuantumToChar(q->green)-
602 (long) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
603 squares[(long) ScaleQuantumToChar(q->blue)-
604 (long) ScaleQuantumToChar(GetBluePixelComponent(p))];
605 numerator=distance_squared;
606 for (k=0; k < (long) image->colors; k++)
609 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
610 (long) ScaleQuantumToChar(GetRedPixelComponent(p))]+
611 squares[(long) ScaleQuantumToChar(q->green)-
612 (long) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
613 squares[(long) ScaleQuantumToChar(q->blue)-
614 (long) ScaleQuantumToChar(GetBluePixelComponent(p))];
615 ratio=numerator/distance_squared;
616 sum+=SegmentPower(ratio);
618 if ((sum != 0.0) && ((1.0/sum) > local_minima))
623 local_minima=1.0/sum;
624 indexes[x]=(IndexPacket) j;
630 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
632 if (image->progress_monitor != (MagickProgressMonitor) NULL)
637 #if defined(MAGICKCORE_OPENMP_SUPPORT)
638 #pragma omp critical (MagickCore_Classify)
640 proceed=SetImageProgress(image,SegmentImageTag,progress++,
642 if (proceed == MagickFalse)
646 image_view=DestroyCacheView(image_view);
647 status&=SyncImage(image);
649 Relinquish resources.
651 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
653 next_cluster=cluster->next;
654 cluster=(Cluster *) RelinquishMagickMemory(cluster);
657 free_squares=squares;
658 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 + C o n s o l i d a t e C r o s s i n g s %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673 % ConsolidateCrossings() guarantees that an even number of zero crossings
674 % always lie between two crossings.
676 % The format of the ConsolidateCrossings method is:
678 % ConsolidateCrossings(ZeroCrossing *zero_crossing,
679 % const unsigned long number_crossings)
681 % A description of each parameter follows.
683 % o zero_crossing: Specifies an array of structures of type ZeroCrossing.
685 % o number_crossings: This unsigned long specifies the number of elements
686 % in the zero_crossing array.
690 static inline long MagickAbsoluteValue(const long x)
697 static inline long MagickMax(const long x,const long y)
704 static inline long MagickMin(const long x,const long y)
711 static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
712 const unsigned long number_crossings)
728 Consolidate zero crossings.
730 for (i=(long) number_crossings-1; i >= 0; i--)
731 for (j=0; j <= 255; j++)
733 if (zero_crossing[i].crossings[j] == 0)
736 Find the entry that is closest to j and still preserves the
737 property that there are an even number of crossings between
740 for (k=j-1; k > 0; k--)
741 if (zero_crossing[i+1].crossings[k] != 0)
745 for (k=j+1; k < 255; k++)
746 if (zero_crossing[i+1].crossings[k] != 0)
748 right=MagickMin(k,255);
750 K is the zero crossing just left of j.
752 for (k=j-1; k > 0; k--)
753 if (zero_crossing[i].crossings[k] != 0)
758 Check center for an even number of crossings between k and j.
761 if (zero_crossing[i+1].crossings[j] != 0)
764 for (l=k+1; l < center; l++)
765 if (zero_crossing[i+1].crossings[l] != 0)
767 if (((count % 2) == 0) && (center != k))
771 Check left for an even number of crossings between k and j.
776 for (l=k+1; l < left; l++)
777 if (zero_crossing[i+1].crossings[l] != 0)
779 if (((count % 2) == 0) && (left != k))
783 Check right for an even number of crossings between k and j.
788 for (l=k+1; l < right; l++)
789 if (zero_crossing[i+1].crossings[l] != 0)
791 if (((count % 2) == 0) && (right != k))
794 l=zero_crossing[i].crossings[j];
795 zero_crossing[i].crossings[j]=0;
797 zero_crossing[i].crossings[correct]=(short) l;
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 + D e f i n e R e g i o n %
810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812 % DefineRegion() defines the left and right boundaries of a peak region.
814 % The format of the DefineRegion method is:
816 % long DefineRegion(const short *extrema,ExtentPacket *extents)
818 % A description of each parameter follows.
820 % o extrema: Specifies a pointer to an array of integers. They
821 % represent the peaks and valleys of the histogram for each color
824 % o extents: This pointer to an ExtentPacket represent the extends
825 % of a particular peak or valley of a color component.
828 static long DefineRegion(const short *extrema,ExtentPacket *extents)
831 Initialize to default values.
837 Find the left side (maxima).
839 for ( ; extents->index <= 255; extents->index++)
840 if (extrema[extents->index] > 0)
842 if (extents->index > 255)
843 return(MagickFalse); /* no left side - no region exists */
844 extents->left=extents->index;
846 Find the right side (minima).
848 for ( ; extents->index <= 255; extents->index++)
849 if (extrema[extents->index] < 0)
851 extents->right=extents->index-1;
856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860 + D e r i v a t i v e H i s t o g r a m %
864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866 % DerivativeHistogram() determines the derivative of the histogram using
867 % central differencing.
869 % The format of the DerivativeHistogram method is:
871 % DerivativeHistogram(const MagickRealType *histogram,
872 % MagickRealType *derivative)
874 % A description of each parameter follows.
876 % o histogram: Specifies an array of MagickRealTypes representing the number
877 % of pixels for each intensity of a particular color component.
879 % o derivative: This array of MagickRealTypes is initialized by
880 % DerivativeHistogram to the derivative of the histogram using central
884 static void DerivativeHistogram(const MagickRealType *histogram,
885 MagickRealType *derivative)
892 Compute endpoints using second order polynomial interpolation.
895 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
896 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
898 Compute derivative using central differencing.
900 for (i=1; i < n; i++)
901 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
906 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 + G e t I m a g e D y n a m i c T h r e s h o l d %
914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
916 % GetImageDynamicThreshold() returns the dynamic threshold for an image.
918 % The format of the GetImageDynamicThreshold method is:
920 % MagickBooleanType GetImageDynamicThreshold(const Image *image,
921 % const double cluster_threshold,const double smooth_threshold,
922 % MagickPixelPacket *pixel,ExceptionInfo *exception)
924 % A description of each parameter follows.
926 % o image: the image.
928 % o cluster_threshold: This MagickRealType represents the minimum number of
929 % pixels contained in a hexahedra before it can be considered valid
930 % (expressed as a percentage).
932 % o smooth_threshold: the smoothing threshold eliminates noise in the second
933 % derivative of the histogram. As the value is increased, you can expect a
934 % smoother second derivative.
936 % o pixel: return the dynamic threshold here.
938 % o exception: return any errors or warnings in this structure.
941 MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
942 const double cluster_threshold,const double smooth_threshold,
943 MagickPixelPacket *pixel,ExceptionInfo *exception)
960 *histogram[MaxDimension],
969 register const PixelPacket
977 *extrema[MaxDimension];
980 Allocate histogram and extrema.
982 assert(image != (Image *) NULL);
983 assert(image->signature == MagickSignature);
984 if (image->debug != MagickFalse)
985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
986 GetMagickPixelPacket(image,pixel);
987 for (i=0; i < MaxDimension; i++)
989 histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
990 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
991 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
993 for (i-- ; i >= 0; i--)
995 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
996 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
998 (void) ThrowMagickException(exception,GetMagickModule(),
999 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1000 return(MagickFalse);
1004 Initialize histogram.
1006 InitializeHistogram(image,histogram,exception);
1007 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
1008 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
1009 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
1010 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
1011 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
1012 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
1016 cluster=(Cluster *) NULL;
1017 head=(Cluster *) NULL;
1018 (void) ResetMagickMemory(&red,0,sizeof(red));
1019 (void) ResetMagickMemory(&green,0,sizeof(green));
1020 (void) ResetMagickMemory(&blue,0,sizeof(blue));
1021 while (DefineRegion(extrema[Red],&red) != 0)
1024 while (DefineRegion(extrema[Green],&green) != 0)
1027 while (DefineRegion(extrema[Blue],&blue) != 0)
1030 Allocate a new class.
1032 if (head != (Cluster *) NULL)
1034 cluster->next=(Cluster *) AcquireMagickMemory(
1035 sizeof(*cluster->next));
1036 cluster=cluster->next;
1040 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
1043 if (cluster == (Cluster *) NULL)
1045 (void) ThrowMagickException(exception,GetMagickModule(),
1046 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1048 return(MagickFalse);
1051 Initialize a new class.
1055 cluster->green=green;
1057 cluster->next=(Cluster *) NULL;
1061 if (head == (Cluster *) NULL)
1064 No classes were identified-- create one.
1066 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
1067 if (cluster == (Cluster *) NULL)
1069 (void) ThrowMagickException(exception,GetMagickModule(),
1070 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1071 return(MagickFalse);
1074 Initialize a new class.
1078 cluster->green=green;
1080 cluster->next=(Cluster *) NULL;
1084 Count the pixels for each cluster.
1087 for (y=0; y < (long) image->rows; y++)
1089 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1090 if (p == (const PixelPacket *) NULL)
1092 for (x=0; x < (long) image->columns; x++)
1094 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
1095 if (((long) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
1096 (cluster->red.left-SafeMargin)) &&
1097 ((long) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
1098 (cluster->red.right+SafeMargin)) &&
1099 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
1100 (cluster->green.left-SafeMargin)) &&
1101 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
1102 (cluster->green.right+SafeMargin)) &&
1103 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
1104 (cluster->blue.left-SafeMargin)) &&
1105 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
1106 (cluster->blue.right+SafeMargin)))
1112 cluster->red.center+=(MagickRealType)
1113 ScaleQuantumToChar(GetRedPixelComponent(p));
1114 cluster->green.center+=(MagickRealType)
1115 ScaleQuantumToChar(GetGreenPixelComponent(p));
1116 cluster->blue.center+=(MagickRealType)
1117 ScaleQuantumToChar(GetBluePixelComponent(p));
1123 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
1124 if (proceed == MagickFalse)
1128 Remove clusters that do not meet minimum cluster threshold.
1133 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1135 next_cluster=cluster->next;
1136 if ((cluster->count > 0) &&
1137 (cluster->count >= (count*cluster_threshold/100.0)))
1143 cluster->red.center/=cluster->count;
1144 cluster->green.center/=cluster->count;
1145 cluster->blue.center/=cluster->count;
1147 last_cluster=cluster;
1153 if (cluster == head)
1156 last_cluster->next=next_cluster;
1157 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1164 for (cluster=object; cluster->next != (Cluster *) NULL; )
1166 if (cluster->count < object->count)
1168 cluster=cluster->next;
1170 background=head->next;
1171 for (cluster=background; cluster->next != (Cluster *) NULL; )
1173 if (cluster->count > background->count)
1175 cluster=cluster->next;
1178 threshold=(background->red.center+object->red.center)/2.0;
1179 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
1181 threshold=(background->green.center+object->green.center)/2.0;
1182 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
1184 threshold=(background->blue.center+object->blue.center)/2.0;
1185 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
1188 Relinquish resources.
1190 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1192 next_cluster=cluster->next;
1193 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1195 for (i=0; i < MaxDimension; i++)
1197 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1198 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208 + I n i t i a l i z e H i s t o g r a m %
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 % InitializeHistogram() computes the histogram for an image.
1216 % The format of the InitializeHistogram method is:
1218 % InitializeHistogram(const Image *image,long **histogram)
1220 % A description of each parameter follows.
1222 % o image: Specifies a pointer to an Image structure; returned from
1225 % o histogram: Specifies an array of integers representing the number
1226 % of pixels for each intensity of a particular color component.
1229 static void InitializeHistogram(const Image *image,long **histogram,
1230 ExceptionInfo *exception)
1235 register const PixelPacket
1243 Initialize histogram.
1245 for (i=0; i <= 255; i++)
1247 histogram[Red][i]=0;
1248 histogram[Green][i]=0;
1249 histogram[Blue][i]=0;
1251 for (y=0; y < (long) image->rows; y++)
1253 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1254 if (p == (const PixelPacket *) NULL)
1256 for (x=0; x < (long) image->columns; x++)
1258 histogram[Red][(long) ScaleQuantumToChar(GetRedPixelComponent(p))]++;
1259 histogram[Green][(long) ScaleQuantumToChar(GetGreenPixelComponent(p))]++;
1260 histogram[Blue][(long) ScaleQuantumToChar(GetBluePixelComponent(p))]++;
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271 + I n i t i a l i z e I n t e r v a l T r e e %
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % InitializeIntervalTree() initializes an interval tree from the lists of
1280 % The format of the InitializeIntervalTree method is:
1282 % InitializeIntervalTree(IntervalTree **list,long *number_nodes,
1283 % IntervalTree *node)
1285 % A description of each parameter follows.
1287 % o zero_crossing: Specifies an array of structures of type ZeroCrossing.
1289 % o number_crossings: This unsigned long specifies the number of elements
1290 % in the zero_crossing array.
1294 static void InitializeList(IntervalTree **list,long *number_nodes,
1297 if (node == (IntervalTree *) NULL)
1299 if (node->child == (IntervalTree *) NULL)
1300 list[(*number_nodes)++]=node;
1301 InitializeList(list,number_nodes,node->sibling);
1302 InitializeList(list,number_nodes,node->child);
1305 static void MeanStability(IntervalTree *node)
1307 register IntervalTree
1310 if (node == (IntervalTree *) NULL)
1312 node->mean_stability=0.0;
1314 if (child != (IntervalTree *) NULL)
1319 register MagickRealType
1324 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
1326 sum+=child->stability;
1329 node->mean_stability=sum/(MagickRealType) count;
1331 MeanStability(node->sibling);
1332 MeanStability(node->child);
1335 static void Stability(IntervalTree *node)
1337 if (node == (IntervalTree *) NULL)
1339 if (node->child == (IntervalTree *) NULL)
1340 node->stability=0.0;
1342 node->stability=node->tau-(node->child)->tau;
1343 Stability(node->sibling);
1344 Stability(node->child);
1347 static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
1348 const unsigned long number_crossings)
1366 Allocate interval tree.
1368 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1370 if (list == (IntervalTree **) NULL)
1371 return((IntervalTree *) NULL);
1373 The root is the entire histogram.
1375 root=(IntervalTree *) AcquireAlignedMemory(1,sizeof(*root));
1376 root->child=(IntervalTree *) NULL;
1377 root->sibling=(IntervalTree *) NULL;
1381 for (i=(-1); i < (long) number_crossings; i++)
1384 Initialize list with all nodes with no children.
1387 InitializeList(list,&number_nodes,root);
1391 for (j=0; j < number_nodes; j++)
1396 for (k=head->left+1; k < head->right; k++)
1398 if (zero_crossing[i+1].crossings[k] != 0)
1402 node->child=(IntervalTree *) AcquireMagickMemory(
1403 sizeof(*node->child));
1408 node->sibling=(IntervalTree *) AcquireMagickMemory(
1409 sizeof(*node->sibling));
1412 node->tau=zero_crossing[i+1].tau;
1413 node->child=(IntervalTree *) NULL;
1414 node->sibling=(IntervalTree *) NULL;
1420 if (left != head->left)
1422 node->sibling=(IntervalTree *) AcquireMagickMemory(
1423 sizeof(*node->sibling));
1425 node->tau=zero_crossing[i+1].tau;
1426 node->child=(IntervalTree *) NULL;
1427 node->sibling=(IntervalTree *) NULL;
1429 node->right=head->right;
1434 Determine the stability: difference between a nodes tau and its child.
1436 Stability(root->child);
1437 MeanStability(root->child);
1438 list=(IntervalTree **) RelinquishMagickMemory(list);
1443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447 + O p t i m a l T a u %
1451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453 % OptimalTau() finds the optimal tau for each band of the histogram.
1455 % The format of the OptimalTau method is:
1457 % MagickRealType OptimalTau(const long *histogram,const double max_tau,
1458 % const double min_tau,const double delta_tau,
1459 % const double smooth_threshold,short *extrema)
1461 % A description of each parameter follows.
1463 % o histogram: Specifies an array of integers representing the number
1464 % of pixels for each intensity of a particular color component.
1466 % o extrema: Specifies a pointer to an array of integers. They
1467 % represent the peaks and valleys of the histogram for each color
1472 static void ActiveNodes(IntervalTree **list,long *number_nodes,
1475 if (node == (IntervalTree *) NULL)
1477 if (node->stability >= node->mean_stability)
1479 list[(*number_nodes)++]=node;
1480 ActiveNodes(list,number_nodes,node->sibling);
1484 ActiveNodes(list,number_nodes,node->sibling);
1485 ActiveNodes(list,number_nodes,node->child);
1489 static void FreeNodes(IntervalTree *node)
1491 if (node == (IntervalTree *) NULL)
1493 FreeNodes(node->sibling);
1494 FreeNodes(node->child);
1495 node=(IntervalTree *) RelinquishMagickMemory(node);
1498 static MagickRealType OptimalTau(const long *histogram,const double max_tau,
1499 const double min_tau,const double delta_tau,const double smooth_threshold,
1535 Allocate interval tree.
1537 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1539 if (list == (IntervalTree **) NULL)
1542 Allocate zero crossing list.
1544 count=(unsigned long) ((max_tau-min_tau)/delta_tau)+2;
1545 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
1546 sizeof(*zero_crossing));
1547 if (zero_crossing == (ZeroCrossing *) NULL)
1549 for (i=0; i < (long) count; i++)
1550 zero_crossing[i].tau=(-1.0);
1552 Initialize zero crossing list.
1554 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
1555 second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
1556 sizeof(*second_derivative));
1557 if ((derivative == (MagickRealType *) NULL) ||
1558 (second_derivative == (MagickRealType *) NULL))
1559 ThrowFatalException(ResourceLimitFatalError,
1560 "UnableToAllocateDerivatives");
1562 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
1564 zero_crossing[i].tau=tau;
1565 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
1566 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1567 DerivativeHistogram(derivative,second_derivative);
1568 ZeroCrossHistogram(second_derivative,smooth_threshold,
1569 zero_crossing[i].crossings);
1573 Add an entry for the original histogram.
1575 zero_crossing[i].tau=0.0;
1576 for (j=0; j <= 255; j++)
1577 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
1578 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1579 DerivativeHistogram(derivative,second_derivative);
1580 ZeroCrossHistogram(second_derivative,smooth_threshold,
1581 zero_crossing[i].crossings);
1582 number_crossings=(unsigned long) i;
1583 derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
1584 second_derivative=(MagickRealType *)
1585 RelinquishMagickMemory(second_derivative);
1587 Ensure the scale-space fingerprints form lines in scale-space, not loops.
1589 ConsolidateCrossings(zero_crossing,number_crossings);
1591 Force endpoints to be included in the interval.
1593 for (i=0; i <= (long) number_crossings; i++)
1595 for (j=0; j < 255; j++)
1596 if (zero_crossing[i].crossings[j] != 0)
1598 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
1599 for (j=255; j > 0; j--)
1600 if (zero_crossing[i].crossings[j] != 0)
1602 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
1605 Initialize interval tree.
1607 root=InitializeIntervalTree(zero_crossing,number_crossings);
1608 if (root == (IntervalTree *) NULL)
1611 Find active nodes: stability is greater (or equal) to the mean stability of
1615 ActiveNodes(list,&number_nodes,root->child);
1619 for (i=0; i <= 255; i++)
1621 for (i=0; i < number_nodes; i++)
1624 Find this tau in zero crossings list.
1628 for (j=0; j <= (long) number_crossings; j++)
1629 if (zero_crossing[j].tau == node->tau)
1632 Find the value of the peak.
1634 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
1637 value=zero_crossing[k].histogram[index];
1638 for (x=node->left; x <= node->right; x++)
1640 if (peak != MagickFalse)
1642 if (zero_crossing[k].histogram[x] > value)
1644 value=zero_crossing[k].histogram[x];
1649 if (zero_crossing[k].histogram[x] < value)
1651 value=zero_crossing[k].histogram[x];
1655 for (x=node->left; x <= node->right; x++)
1659 if (peak != MagickFalse)
1660 extrema[x]=(short) index;
1662 extrema[x]=(short) (-index);
1666 Determine the average tau.
1669 for (i=0; i < number_nodes; i++)
1670 average_tau+=list[i]->tau;
1671 average_tau/=(MagickRealType) number_nodes;
1673 Relinquish resources.
1676 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
1677 list=(IntervalTree **) RelinquishMagickMemory(list);
1678 return(average_tau);
1682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1686 + S c a l e S p a c e %
1690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1692 % ScaleSpace() performs a scale-space filter on the 1D histogram.
1694 % The format of the ScaleSpace method is:
1696 % ScaleSpace(const long *histogram,const MagickRealType tau,
1697 % MagickRealType *scale_histogram)
1699 % A description of each parameter follows.
1701 % o histogram: Specifies an array of MagickRealTypes representing the number
1702 % of pixels for each intensity of a particular color component.
1706 static void ScaleSpace(const long *histogram,const MagickRealType tau,
1707 MagickRealType *scale_histogram)
1719 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
1720 if (gamma == (MagickRealType *) NULL)
1721 ThrowFatalException(ResourceLimitFatalError,
1722 "UnableToAllocateGammaMap");
1723 alpha=1.0/(tau*sqrt(2.0*MagickPI));
1724 beta=(-1.0/(2.0*tau*tau));
1725 for (x=0; x <= 255; x++)
1727 for (x=0; x <= 255; x++)
1729 gamma[x]=exp((double) beta*x*x);
1730 if (gamma[x] < MagickEpsilon)
1733 for (x=0; x <= 255; x++)
1736 for (u=0; u <= 255; u++)
1737 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
1738 scale_histogram[x]=alpha*sum;
1740 gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
1744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1748 % S e g m e n t I m a g e %
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1754 % SegmentImage() segment an image by analyzing the histograms of the color
1755 % components and identifying units that are homogeneous with the fuzzy
1756 % C-means technique.
1758 % The format of the SegmentImage method is:
1760 % MagickBooleanType SegmentImage(Image *image,
1761 % const ColorspaceType colorspace,const MagickBooleanType verbose,
1762 % const double cluster_threshold,const double smooth_threshold)
1764 % A description of each parameter follows.
1766 % o image: the image.
1768 % o colorspace: Indicate the colorspace.
1770 % o verbose: Set to MagickTrue to print detailed information about the
1771 % identified classes.
1773 % o cluster_threshold: This represents the minimum number of pixels
1774 % contained in a hexahedra before it can be considered valid (expressed
1777 % o smooth_threshold: the smoothing threshold eliminates noise in the second
1778 % derivative of the histogram. As the value is increased, you can expect a
1779 % smoother second derivative.
1782 MagickExport MagickBooleanType SegmentImage(Image *image,
1783 const ColorspaceType colorspace,const MagickBooleanType verbose,
1784 const double cluster_threshold,const double smooth_threshold)
1787 *histogram[MaxDimension];
1796 *extrema[MaxDimension];
1799 Allocate histogram and extrema.
1801 assert(image != (Image *) NULL);
1802 assert(image->signature == MagickSignature);
1803 if (image->debug != MagickFalse)
1804 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1805 for (i=0; i < MaxDimension; i++)
1807 histogram[i]=(long *) AcquireQuantumMemory(256,sizeof(**histogram));
1808 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
1809 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
1811 for (i-- ; i >= 0; i--)
1813 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1814 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1816 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1820 if (colorspace != RGBColorspace)
1821 (void) TransformImageColorspace(image,colorspace);
1823 Initialize histogram.
1825 InitializeHistogram(image,histogram,&image->exception);
1826 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
1827 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
1828 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
1829 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
1830 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
1831 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
1833 Classify using the fuzzy c-Means technique.
1835 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
1836 if (colorspace != RGBColorspace)
1837 (void) TransformImageColorspace(image,colorspace);
1839 Relinquish resources.
1841 for (i=0; i < MaxDimension; i++)
1843 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
1844 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
1850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1854 + Z e r o C r o s s H i s t o g r a m %
1858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1860 % ZeroCrossHistogram() find the zero crossings in a histogram and marks
1861 % directions as: 1 is negative to positive; 0 is zero crossing; and -1
1862 % is positive to negative.
1864 % The format of the ZeroCrossHistogram method is:
1866 % ZeroCrossHistogram(MagickRealType *second_derivative,
1867 % const MagickRealType smooth_threshold,short *crossings)
1869 % A description of each parameter follows.
1871 % o second_derivative: Specifies an array of MagickRealTypes representing the
1872 % second derivative of the histogram of a particular color component.
1874 % o crossings: This array of integers is initialized with
1875 % -1, 0, or 1 representing the slope of the first derivative of the
1876 % of a particular color component.
1879 static void ZeroCrossHistogram(MagickRealType *second_derivative,
1880 const MagickRealType smooth_threshold,short *crossings)
1889 Merge low numbers to zero to help prevent noise.
1891 for (i=0; i <= 255; i++)
1892 if ((second_derivative[i] < smooth_threshold) &&
1893 (second_derivative[i] >= -smooth_threshold))
1894 second_derivative[i]=0.0;
1896 Mark zero crossings.
1899 for (i=0; i <= 255; i++)
1902 if (second_derivative[i] < 0.0)
1909 if (second_derivative[i] > 0.0)