blob: 0f7d99f76d097fd11015827f19a76bce5dc22c9a [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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"
cristye7e40552010-04-24 21:34:22 +000088#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000089#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"
102
103/*
104 Define declarations.
105*/
106#define MaxDimension 3
107#define DeltaTau 0.5f
108#if defined(FastClassify)
109#define WeightingExponent 2.0
110#define SegmentPower(ratio) (ratio)
111#else
112#define WeightingExponent 2.5
113#define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
114#endif
115#define Tau 5.2f
116
117/*
118 Typedef declarations.
119*/
120typedef struct _ExtentPacket
121{
122 MagickRealType
123 center;
124
cristybb503372010-05-27 20:51:26 +0000125 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000126 index,
127 left,
128 right;
129} ExtentPacket;
130
131typedef struct _Cluster
132{
133 struct _Cluster
134 *next;
135
136 ExtentPacket
137 red,
138 green,
139 blue;
140
cristybb503372010-05-27 20:51:26 +0000141 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000142 count,
143 id;
144} Cluster;
145
146typedef struct _IntervalTree
147{
148 MagickRealType
149 tau;
150
cristybb503372010-05-27 20:51:26 +0000151 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000152 left,
153 right;
154
155 MagickRealType
156 mean_stability,
157 stability;
158
159 struct _IntervalTree
160 *sibling,
161 *child;
162} IntervalTree;
163
164typedef struct _ZeroCrossing
165{
166 MagickRealType
167 tau,
168 histogram[256];
169
170 short
171 crossings[256];
172} ZeroCrossing;
173
174/*
175 Constant declarations.
176*/
177static const int
178 Blue = 2,
179 Green = 1,
180 Red = 0,
181 SafeMargin = 3,
182 TreeLength = 600;
183
184/*
185 Method prototypes.
186*/
187static MagickRealType
cristybb503372010-05-27 20:51:26 +0000188 OptimalTau(const ssize_t *,const double,const double,const double,
cristy3ed852e2009-09-05 21:47:34 +0000189 const double,short *);
190
cristybb503372010-05-27 20:51:26 +0000191static ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000192 DefineRegion(const short *,ExtentPacket *);
193
194static void
cristybb503372010-05-27 20:51:26 +0000195 InitializeHistogram(const Image *,ssize_t **,ExceptionInfo *),
196 ScaleSpace(const ssize_t *,const MagickRealType,MagickRealType *),
cristy3ed852e2009-09-05 21:47:34 +0000197 ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *);
198
199/*
200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201% %
202% %
203% %
204+ C l a s s i f y %
205% %
206% %
207% %
208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209%
210% Classify() defines one or more classes. Each pixel is thresholded to
cristy33c53022010-06-25 12:17:27 +0000211% determine which class it belongs to. If the class is not identified it is
cristy3ed852e2009-09-05 21:47:34 +0000212% assigned to the closest class based on the fuzzy c-Means technique.
213%
214% The format of the Classify method is:
215%
216% MagickBooleanType Classify(Image *image,short **extrema,
217% const MagickRealType cluster_threshold,
218% const MagickRealType weighting_exponent,
219% const MagickBooleanType verbose)
220%
221% A description of each parameter follows.
222%
223% o image: the image.
224%
225% o extrema: Specifies a pointer to an array of integers. They
226% represent the peaks and valleys of the histogram for each color
227% component.
228%
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).
232%
233% o weighting_exponent: Specifies the membership weighting exponent.
234%
235% o verbose: A value greater than zero prints detailed information about
236% the identified classes.
237%
238*/
239static MagickBooleanType Classify(Image *image,short **extrema,
240 const MagickRealType cluster_threshold,
241 const MagickRealType weighting_exponent,const MagickBooleanType verbose)
242{
243#define SegmentImageTag "Segment/Image"
244
cristyc4c8d132010-01-07 01:58:38 +0000245 CacheView
246 *image_view;
247
cristy3ed852e2009-09-05 21:47:34 +0000248 Cluster
249 *cluster,
250 *head,
251 *last_cluster,
252 *next_cluster;
253
254 ExceptionInfo
255 *exception;
256
257 ExtentPacket
258 blue,
259 green,
260 red;
261
cristy5f959472010-05-27 22:19:46 +0000262 MagickOffsetType
263 progress;
cristy3ed852e2009-09-05 21:47:34 +0000264
265 MagickRealType
266 *free_squares;
267
268 MagickStatusType
269 status;
270
cristybb503372010-05-27 20:51:26 +0000271 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000272 i;
273
274 register MagickRealType
275 *squares;
276
cristybb503372010-05-27 20:51:26 +0000277 size_t
cristy3ed852e2009-09-05 21:47:34 +0000278 number_clusters;
279
cristy5f959472010-05-27 22:19:46 +0000280 ssize_t
281 count,
282 y;
283
cristy3ed852e2009-09-05 21:47:34 +0000284 /*
285 Form clusters.
286 */
287 cluster=(Cluster *) NULL;
288 head=(Cluster *) NULL;
289 (void) ResetMagickMemory(&red,0,sizeof(red));
290 (void) ResetMagickMemory(&green,0,sizeof(green));
291 (void) ResetMagickMemory(&blue,0,sizeof(blue));
292 while (DefineRegion(extrema[Red],&red) != 0)
293 {
294 green.index=0;
295 while (DefineRegion(extrema[Green],&green) != 0)
296 {
297 blue.index=0;
298 while (DefineRegion(extrema[Blue],&blue) != 0)
299 {
300 /*
301 Allocate a new class.
302 */
303 if (head != (Cluster *) NULL)
304 {
305 cluster->next=(Cluster *) AcquireMagickMemory(
306 sizeof(*cluster->next));
307 cluster=cluster->next;
308 }
309 else
310 {
cristy73bd4a52010-10-05 11:24:23 +0000311 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +0000312 head=cluster;
313 }
314 if (cluster == (Cluster *) NULL)
315 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
316 image->filename);
317 /*
318 Initialize a new class.
319 */
320 cluster->count=0;
321 cluster->red=red;
322 cluster->green=green;
323 cluster->blue=blue;
324 cluster->next=(Cluster *) NULL;
325 }
326 }
327 }
328 if (head == (Cluster *) NULL)
329 {
330 /*
331 No classes were identified-- create one.
332 */
cristy73bd4a52010-10-05 11:24:23 +0000333 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +0000334 if (cluster == (Cluster *) NULL)
335 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
336 image->filename);
337 /*
338 Initialize a new class.
339 */
340 cluster->count=0;
341 cluster->red=red;
342 cluster->green=green;
343 cluster->blue=blue;
344 cluster->next=(Cluster *) NULL;
345 head=cluster;
346 }
347 /*
348 Count the pixels for each cluster.
349 */
350 status=MagickTrue;
351 count=0;
352 progress=0;
353 exception=(&image->exception);
354 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000355 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000356 {
357 register const PixelPacket
358 *p;
359
cristybb503372010-05-27 20:51:26 +0000360 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000361 x;
362
363 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
364 if (p == (const PixelPacket *) NULL)
365 break;
cristybb503372010-05-27 20:51:26 +0000366 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000367 {
368 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristybb503372010-05-27 20:51:26 +0000369 if (((ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000370 (cluster->red.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000371 ((ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000372 (cluster->red.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000373 ((ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000374 (cluster->green.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000375 ((ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000376 (cluster->green.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000377 ((ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000378 (cluster->blue.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000379 ((ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000380 (cluster->blue.right+SafeMargin)))
381 {
382 /*
383 Count this pixel.
384 */
385 count++;
cristyce70c172010-01-07 17:15:30 +0000386 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(GetRedPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +0000387 cluster->green.center+=(MagickRealType)
cristyce70c172010-01-07 17:15:30 +0000388 ScaleQuantumToChar(GetGreenPixelComponent(p));
389 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +0000390 cluster->count++;
391 break;
392 }
393 p++;
394 }
395 if (image->progress_monitor != (MagickProgressMonitor) NULL)
396 {
397 MagickBooleanType
398 proceed;
399
cristyb5d5f722009-11-04 03:03:49 +0000400#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000401 #pragma omp critical (MagickCore_Classify)
402#endif
403 proceed=SetImageProgress(image,SegmentImageTag,progress++,
404 2*image->rows);
405 if (proceed == MagickFalse)
406 status=MagickFalse;
407 }
408 }
409 image_view=DestroyCacheView(image_view);
410 /*
411 Remove clusters that do not meet minimum cluster threshold.
412 */
413 count=0;
414 last_cluster=head;
415 next_cluster=head;
416 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
417 {
418 next_cluster=cluster->next;
419 if ((cluster->count > 0) &&
420 (cluster->count >= (count*cluster_threshold/100.0)))
421 {
422 /*
423 Initialize cluster.
424 */
425 cluster->id=count;
426 cluster->red.center/=cluster->count;
427 cluster->green.center/=cluster->count;
428 cluster->blue.center/=cluster->count;
429 count++;
430 last_cluster=cluster;
431 continue;
432 }
433 /*
434 Delete cluster.
435 */
436 if (cluster == head)
437 head=next_cluster;
438 else
439 last_cluster->next=next_cluster;
440 cluster=(Cluster *) RelinquishMagickMemory(cluster);
441 }
cristybb503372010-05-27 20:51:26 +0000442 number_clusters=(size_t) count;
cristy3ed852e2009-09-05 21:47:34 +0000443 if (verbose != MagickFalse)
444 {
445 /*
446 Print cluster statistics.
447 */
448 (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
449 (void) fprintf(stdout,"===================\n\n");
cristye7f51092010-01-17 00:39:37 +0000450 (void) fprintf(stdout,"\tCluster Threshold = %g\n",(double)
cristy4f3c0be2009-09-12 16:04:05 +0000451 cluster_threshold);
cristye7f51092010-01-17 00:39:37 +0000452 (void) fprintf(stdout,"\tWeighting Exponent = %g\n",(double)
cristy4f3c0be2009-09-12 16:04:05 +0000453 weighting_exponent);
cristye8c25f92010-06-03 00:53:06 +0000454 (void) fprintf(stdout,"\tTotal Number of Clusters = %.20g\n\n",(double)
455 number_clusters);
cristy3ed852e2009-09-05 21:47:34 +0000456 /*
457 Print the total number of points per cluster.
458 */
459 (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
460 (void) fprintf(stdout,"=============================\n\n");
461 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristye8c25f92010-06-03 00:53:06 +0000462 (void) fprintf(stdout,"Cluster #%.20g = %.20g\n",(double) cluster->id,
463 (double) cluster->count);
cristy3ed852e2009-09-05 21:47:34 +0000464 /*
465 Print the cluster extents.
466 */
467 (void) fprintf(stdout,
468 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
469 (void) fprintf(stdout,"================");
470 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
471 {
cristye8c25f92010-06-03 00:53:06 +0000472 (void) fprintf(stdout,"\n\nCluster #%.20g\n\n",(double) cluster->id);
473 (void) fprintf(stdout,"%.20g-%.20g %.20g-%.20g %.20g-%.20g\n",(double)
474 cluster->red.left,(double) cluster->red.right,(double)
475 cluster->green.left,(double) cluster->green.right,(double)
476 cluster->blue.left,(double) cluster->blue.right);
cristy3ed852e2009-09-05 21:47:34 +0000477 }
478 /*
479 Print the cluster center values.
480 */
481 (void) fprintf(stdout,
482 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
483 (void) fprintf(stdout,"=====================");
484 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
485 {
cristye8c25f92010-06-03 00:53:06 +0000486 (void) fprintf(stdout,"\n\nCluster #%.20g\n\n",(double) cluster->id);
cristye7f51092010-01-17 00:39:37 +0000487 (void) fprintf(stdout,"%g %g %g\n",(double)
cristy8cd5b312010-01-07 01:10:24 +0000488 cluster->red.center,(double) cluster->green.center,(double)
489 cluster->blue.center);
cristy3ed852e2009-09-05 21:47:34 +0000490 }
491 (void) fprintf(stdout,"\n");
492 }
493 if (number_clusters > 256)
494 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
495 /*
496 Speed up distance calculations.
497 */
498 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
499 if (squares == (MagickRealType *) NULL)
500 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
501 image->filename);
502 squares+=255;
503 for (i=(-255); i <= 255; i++)
504 squares[i]=(MagickRealType) i*(MagickRealType) i;
505 /*
506 Allocate image colormap.
507 */
508 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
509 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
510 image->filename);
511 i=0;
512 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
513 {
514 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
515 (cluster->red.center+0.5));
516 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
517 (cluster->green.center+0.5));
518 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
519 (cluster->blue.center+0.5));
520 i++;
521 }
522 /*
523 Do course grain classes.
524 */
525 exception=(&image->exception);
526 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000527#if defined(MAGICKCORE_OPENMP_SUPPORT)
528 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000529#endif
cristybb503372010-05-27 20:51:26 +0000530 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000531 {
532 Cluster
533 *cluster;
534
535 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000536 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000537
538 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000539 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000540
cristybb503372010-05-27 20:51:26 +0000541 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000542 x;
543
544 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000545 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000546
547 if (status == MagickFalse)
548 continue;
549 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
550 if (q == (PixelPacket *) NULL)
551 {
552 status=MagickFalse;
553 continue;
554 }
555 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000556 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
cristyfba5a8b2011-05-03 17:12:12 +0000558 SetIndexPixelComponent(indexes+x,0);
cristy3ed852e2009-09-05 21:47:34 +0000559 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
560 {
cristybb503372010-05-27 20:51:26 +0000561 if (((ssize_t) ScaleQuantumToChar(q->red) >=
cristy3ed852e2009-09-05 21:47:34 +0000562 (cluster->red.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000563 ((ssize_t) ScaleQuantumToChar(q->red) <=
cristy3ed852e2009-09-05 21:47:34 +0000564 (cluster->red.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000565 ((ssize_t) ScaleQuantumToChar(q->green) >=
cristy3ed852e2009-09-05 21:47:34 +0000566 (cluster->green.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000567 ((ssize_t) ScaleQuantumToChar(q->green) <=
cristy3ed852e2009-09-05 21:47:34 +0000568 (cluster->green.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000569 ((ssize_t) ScaleQuantumToChar(q->blue) >=
cristy3ed852e2009-09-05 21:47:34 +0000570 (cluster->blue.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +0000571 ((ssize_t) ScaleQuantumToChar(q->blue) <=
cristy3ed852e2009-09-05 21:47:34 +0000572 (cluster->blue.right+SafeMargin)))
573 {
574 /*
575 Classify this pixel.
576 */
cristyfba5a8b2011-05-03 17:12:12 +0000577 SetIndexPixelComponent(indexes+x,cluster->id);
cristy3ed852e2009-09-05 21:47:34 +0000578 break;
579 }
580 }
581 if (cluster == (Cluster *) NULL)
582 {
583 MagickRealType
584 distance_squared,
585 local_minima,
586 numerator,
587 ratio,
588 sum;
589
cristybb503372010-05-27 20:51:26 +0000590 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000591 j,
592 k;
593
594 /*
595 Compute fuzzy membership.
596 */
597 local_minima=0.0;
cristybb503372010-05-27 20:51:26 +0000598 for (j=0; j < (ssize_t) image->colors; j++)
cristy3ed852e2009-09-05 21:47:34 +0000599 {
600 sum=0.0;
601 p=image->colormap+j;
cristybb503372010-05-27 20:51:26 +0000602 distance_squared=squares[(ssize_t) ScaleQuantumToChar(q->red)-
603 (ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p))]+
604 squares[(ssize_t) ScaleQuantumToChar(q->green)-
605 (ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
606 squares[(ssize_t) ScaleQuantumToChar(q->blue)-
607 (ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p))];
cristy3ed852e2009-09-05 21:47:34 +0000608 numerator=distance_squared;
cristybb503372010-05-27 20:51:26 +0000609 for (k=0; k < (ssize_t) image->colors; k++)
cristy3ed852e2009-09-05 21:47:34 +0000610 {
611 p=image->colormap+k;
cristybb503372010-05-27 20:51:26 +0000612 distance_squared=squares[(ssize_t) ScaleQuantumToChar(q->red)-
613 (ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p))]+
614 squares[(ssize_t) ScaleQuantumToChar(q->green)-
615 (ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
616 squares[(ssize_t) ScaleQuantumToChar(q->blue)-
617 (ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p))];
cristy3ed852e2009-09-05 21:47:34 +0000618 ratio=numerator/distance_squared;
619 sum+=SegmentPower(ratio);
620 }
621 if ((sum != 0.0) && ((1.0/sum) > local_minima))
622 {
623 /*
624 Classify this pixel.
625 */
626 local_minima=1.0/sum;
cristyfba5a8b2011-05-03 17:12:12 +0000627 SetIndexPixelComponent(indexes+x,j);
cristy3ed852e2009-09-05 21:47:34 +0000628 }
629 }
630 }
631 q++;
632 }
633 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
634 status=MagickFalse;
635 if (image->progress_monitor != (MagickProgressMonitor) NULL)
636 {
637 MagickBooleanType
638 proceed;
639
cristyb5d5f722009-11-04 03:03:49 +0000640#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000641 #pragma omp critical (MagickCore_Classify)
642#endif
643 proceed=SetImageProgress(image,SegmentImageTag,progress++,
644 2*image->rows);
645 if (proceed == MagickFalse)
646 status=MagickFalse;
647 }
648 }
649 image_view=DestroyCacheView(image_view);
650 status&=SyncImage(image);
651 /*
652 Relinquish resources.
653 */
654 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
655 {
656 next_cluster=cluster->next;
657 cluster=(Cluster *) RelinquishMagickMemory(cluster);
658 }
659 squares-=255;
660 free_squares=squares;
661 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
662 return(MagickTrue);
663}
664
665/*
666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667% %
668% %
669% %
670+ C o n s o l i d a t e C r o s s i n g s %
671% %
672% %
673% %
674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675%
676% ConsolidateCrossings() guarantees that an even number of zero crossings
677% always lie between two crossings.
678%
679% The format of the ConsolidateCrossings method is:
680%
681% ConsolidateCrossings(ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +0000682% const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +0000683%
684% A description of each parameter follows.
685%
686% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
687%
cristybb503372010-05-27 20:51:26 +0000688% o number_crossings: This size_t specifies the number of elements
cristy3ed852e2009-09-05 21:47:34 +0000689% in the zero_crossing array.
690%
691*/
692
cristybb503372010-05-27 20:51:26 +0000693static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +0000694{
695 if (x < 0)
696 return(-x);
697 return(x);
698}
699
cristybb503372010-05-27 20:51:26 +0000700static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000701{
702 if (x > y)
703 return(x);
704 return(y);
705}
706
cristybb503372010-05-27 20:51:26 +0000707static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000708{
709 if (x < y)
710 return(x);
711 return(y);
712}
713
714static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +0000715 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +0000716{
cristy9d314ff2011-03-09 01:30:28 +0000717 register ssize_t
718 i,
719 j,
720 k,
721 l;
722
cristybb503372010-05-27 20:51:26 +0000723 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000724 center,
725 correct,
726 count,
727 left,
728 right;
729
cristy3ed852e2009-09-05 21:47:34 +0000730 /*
731 Consolidate zero crossings.
732 */
cristybb503372010-05-27 20:51:26 +0000733 for (i=(ssize_t) number_crossings-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000734 for (j=0; j <= 255; j++)
735 {
736 if (zero_crossing[i].crossings[j] == 0)
737 continue;
738 /*
739 Find the entry that is closest to j and still preserves the
740 property that there are an even number of crossings between
741 intervals.
742 */
743 for (k=j-1; k > 0; k--)
744 if (zero_crossing[i+1].crossings[k] != 0)
745 break;
746 left=MagickMax(k,0);
747 center=j;
748 for (k=j+1; k < 255; k++)
749 if (zero_crossing[i+1].crossings[k] != 0)
750 break;
751 right=MagickMin(k,255);
752 /*
753 K is the zero crossing just left of j.
754 */
755 for (k=j-1; k > 0; k--)
756 if (zero_crossing[i].crossings[k] != 0)
757 break;
758 if (k < 0)
759 k=0;
760 /*
761 Check center for an even number of crossings between k and j.
762 */
763 correct=(-1);
764 if (zero_crossing[i+1].crossings[j] != 0)
765 {
766 count=0;
767 for (l=k+1; l < center; l++)
768 if (zero_crossing[i+1].crossings[l] != 0)
769 count++;
770 if (((count % 2) == 0) && (center != k))
771 correct=center;
772 }
773 /*
774 Check left for an even number of crossings between k and j.
775 */
776 if (correct == -1)
777 {
778 count=0;
779 for (l=k+1; l < left; l++)
780 if (zero_crossing[i+1].crossings[l] != 0)
781 count++;
782 if (((count % 2) == 0) && (left != k))
783 correct=left;
784 }
785 /*
786 Check right for an even number of crossings between k and j.
787 */
788 if (correct == -1)
789 {
790 count=0;
791 for (l=k+1; l < right; l++)
792 if (zero_crossing[i+1].crossings[l] != 0)
793 count++;
794 if (((count % 2) == 0) && (right != k))
795 correct=right;
796 }
cristycee97112010-05-28 00:44:52 +0000797 l=(ssize_t) zero_crossing[i].crossings[j];
cristy3ed852e2009-09-05 21:47:34 +0000798 zero_crossing[i].crossings[j]=0;
799 if (correct != -1)
800 zero_crossing[i].crossings[correct]=(short) l;
801 }
802}
803
804/*
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806% %
807% %
808% %
809+ D e f i n e R e g i o n %
810% %
811% %
812% %
813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814%
815% DefineRegion() defines the left and right boundaries of a peak region.
816%
817% The format of the DefineRegion method is:
818%
cristybb503372010-05-27 20:51:26 +0000819% ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000820%
821% A description of each parameter follows.
822%
823% o extrema: Specifies a pointer to an array of integers. They
824% represent the peaks and valleys of the histogram for each color
825% component.
826%
827% o extents: This pointer to an ExtentPacket represent the extends
828% of a particular peak or valley of a color component.
829%
830*/
cristybb503372010-05-27 20:51:26 +0000831static ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000832{
833 /*
834 Initialize to default values.
835 */
836 extents->left=0;
837 extents->center=0.0;
838 extents->right=255;
839 /*
840 Find the left side (maxima).
841 */
842 for ( ; extents->index <= 255; extents->index++)
843 if (extrema[extents->index] > 0)
844 break;
845 if (extents->index > 255)
846 return(MagickFalse); /* no left side - no region exists */
847 extents->left=extents->index;
848 /*
849 Find the right side (minima).
850 */
851 for ( ; extents->index <= 255; extents->index++)
852 if (extrema[extents->index] < 0)
853 break;
854 extents->right=extents->index-1;
855 return(MagickTrue);
856}
857
858/*
859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860% %
861% %
862% %
863+ D e r i v a t i v e H i s t o g r a m %
864% %
865% %
866% %
867%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868%
869% DerivativeHistogram() determines the derivative of the histogram using
870% central differencing.
871%
872% The format of the DerivativeHistogram method is:
873%
874% DerivativeHistogram(const MagickRealType *histogram,
875% MagickRealType *derivative)
876%
877% A description of each parameter follows.
878%
879% o histogram: Specifies an array of MagickRealTypes representing the number
880% of pixels for each intensity of a particular color component.
881%
882% o derivative: This array of MagickRealTypes is initialized by
883% DerivativeHistogram to the derivative of the histogram using central
884% differencing.
885%
886*/
887static void DerivativeHistogram(const MagickRealType *histogram,
888 MagickRealType *derivative)
889{
cristybb503372010-05-27 20:51:26 +0000890 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000891 i,
892 n;
893
894 /*
895 Compute endpoints using second order polynomial interpolation.
896 */
897 n=255;
898 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
899 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
900 /*
901 Compute derivative using central differencing.
902 */
903 for (i=1; i < n; i++)
904 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
905 return;
906}
907
908/*
909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910% %
911% %
912% %
913+ 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% %
915% %
916% %
917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
918%
919% GetImageDynamicThreshold() returns the dynamic threshold for an image.
920%
921% The format of the GetImageDynamicThreshold method is:
922%
923% MagickBooleanType GetImageDynamicThreshold(const Image *image,
924% const double cluster_threshold,const double smooth_threshold,
925% MagickPixelPacket *pixel,ExceptionInfo *exception)
926%
927% A description of each parameter follows.
928%
929% o image: the image.
930%
931% o cluster_threshold: This MagickRealType represents the minimum number of
932% pixels contained in a hexahedra before it can be considered valid
933% (expressed as a percentage).
934%
935% o smooth_threshold: the smoothing threshold eliminates noise in the second
936% derivative of the histogram. As the value is increased, you can expect a
937% smoother second derivative.
938%
939% o pixel: return the dynamic threshold here.
940%
941% o exception: return any errors or warnings in this structure.
942%
943*/
944MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
945 const double cluster_threshold,const double smooth_threshold,
946 MagickPixelPacket *pixel,ExceptionInfo *exception)
947{
948 Cluster
949 *background,
950 *cluster,
951 *object,
952 *head,
953 *last_cluster,
954 *next_cluster;
955
956 ExtentPacket
957 blue,
958 green,
959 red;
960
cristy3ed852e2009-09-05 21:47:34 +0000961 MagickBooleanType
962 proceed;
963
964 MagickRealType
965 threshold;
966
967 register const PixelPacket
968 *p;
969
cristybb503372010-05-27 20:51:26 +0000970 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000971 i,
972 x;
973
974 short
975 *extrema[MaxDimension];
976
cristy9d314ff2011-03-09 01:30:28 +0000977 ssize_t
978 count,
979 *histogram[MaxDimension],
980 y;
981
cristy3ed852e2009-09-05 21:47:34 +0000982 /*
983 Allocate histogram and extrema.
984 */
985 assert(image != (Image *) NULL);
986 assert(image->signature == MagickSignature);
987 if (image->debug != MagickFalse)
988 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
989 GetMagickPixelPacket(image,pixel);
990 for (i=0; i < MaxDimension; i++)
991 {
cristybb503372010-05-27 20:51:26 +0000992 histogram[i]=(ssize_t *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +0000993 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristybb503372010-05-27 20:51:26 +0000994 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000995 {
996 for (i-- ; i >= 0; i--)
997 {
998 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +0000999 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001000 }
1001 (void) ThrowMagickException(exception,GetMagickModule(),
1002 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1003 return(MagickFalse);
1004 }
1005 }
1006 /*
1007 Initialize histogram.
1008 */
1009 InitializeHistogram(image,histogram,exception);
1010 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
1011 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
1012 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
1013 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
1014 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
1015 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
1016 /*
1017 Form clusters.
1018 */
1019 cluster=(Cluster *) NULL;
1020 head=(Cluster *) NULL;
1021 (void) ResetMagickMemory(&red,0,sizeof(red));
1022 (void) ResetMagickMemory(&green,0,sizeof(green));
1023 (void) ResetMagickMemory(&blue,0,sizeof(blue));
1024 while (DefineRegion(extrema[Red],&red) != 0)
1025 {
1026 green.index=0;
1027 while (DefineRegion(extrema[Green],&green) != 0)
1028 {
1029 blue.index=0;
1030 while (DefineRegion(extrema[Blue],&blue) != 0)
1031 {
1032 /*
1033 Allocate a new class.
1034 */
1035 if (head != (Cluster *) NULL)
1036 {
1037 cluster->next=(Cluster *) AcquireMagickMemory(
1038 sizeof(*cluster->next));
1039 cluster=cluster->next;
1040 }
1041 else
1042 {
cristy73bd4a52010-10-05 11:24:23 +00001043 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001044 head=cluster;
1045 }
1046 if (cluster == (Cluster *) NULL)
1047 {
1048 (void) ThrowMagickException(exception,GetMagickModule(),
1049 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1050 image->filename);
1051 return(MagickFalse);
1052 }
1053 /*
1054 Initialize a new class.
1055 */
1056 cluster->count=0;
1057 cluster->red=red;
1058 cluster->green=green;
1059 cluster->blue=blue;
1060 cluster->next=(Cluster *) NULL;
1061 }
1062 }
1063 }
1064 if (head == (Cluster *) NULL)
1065 {
1066 /*
1067 No classes were identified-- create one.
1068 */
cristy73bd4a52010-10-05 11:24:23 +00001069 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001070 if (cluster == (Cluster *) NULL)
1071 {
1072 (void) ThrowMagickException(exception,GetMagickModule(),
1073 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1074 return(MagickFalse);
1075 }
1076 /*
1077 Initialize a new class.
1078 */
1079 cluster->count=0;
1080 cluster->red=red;
1081 cluster->green=green;
1082 cluster->blue=blue;
1083 cluster->next=(Cluster *) NULL;
1084 head=cluster;
1085 }
1086 /*
1087 Count the pixels for each cluster.
1088 */
1089 count=0;
cristybb503372010-05-27 20:51:26 +00001090 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001091 {
1092 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1093 if (p == (const PixelPacket *) NULL)
1094 break;
cristybb503372010-05-27 20:51:26 +00001095 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001096 {
1097 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristybb503372010-05-27 20:51:26 +00001098 if (((ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001099 (cluster->red.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +00001100 ((ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001101 (cluster->red.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +00001102 ((ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001103 (cluster->green.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +00001104 ((ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001105 (cluster->green.right+SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +00001106 ((ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001107 (cluster->blue.left-SafeMargin)) &&
cristybb503372010-05-27 20:51:26 +00001108 ((ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001109 (cluster->blue.right+SafeMargin)))
1110 {
1111 /*
1112 Count this pixel.
1113 */
1114 count++;
1115 cluster->red.center+=(MagickRealType)
cristyce70c172010-01-07 17:15:30 +00001116 ScaleQuantumToChar(GetRedPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001117 cluster->green.center+=(MagickRealType)
cristyce70c172010-01-07 17:15:30 +00001118 ScaleQuantumToChar(GetGreenPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001119 cluster->blue.center+=(MagickRealType)
cristyce70c172010-01-07 17:15:30 +00001120 ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001121 cluster->count++;
1122 break;
1123 }
1124 p++;
1125 }
cristycee97112010-05-28 00:44:52 +00001126 proceed=SetImageProgress(image,SegmentImageTag,(MagickOffsetType) y,
1127 2*image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001128 if (proceed == MagickFalse)
1129 break;
1130 }
1131 /*
1132 Remove clusters that do not meet minimum cluster threshold.
1133 */
1134 count=0;
1135 last_cluster=head;
1136 next_cluster=head;
1137 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1138 {
1139 next_cluster=cluster->next;
1140 if ((cluster->count > 0) &&
1141 (cluster->count >= (count*cluster_threshold/100.0)))
1142 {
1143 /*
1144 Initialize cluster.
1145 */
1146 cluster->id=count;
1147 cluster->red.center/=cluster->count;
1148 cluster->green.center/=cluster->count;
1149 cluster->blue.center/=cluster->count;
1150 count++;
1151 last_cluster=cluster;
1152 continue;
1153 }
1154 /*
1155 Delete cluster.
1156 */
1157 if (cluster == head)
1158 head=next_cluster;
1159 else
1160 last_cluster->next=next_cluster;
1161 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1162 }
1163 object=head;
1164 background=head;
1165 if (count > 1)
1166 {
1167 object=head->next;
1168 for (cluster=object; cluster->next != (Cluster *) NULL; )
1169 {
1170 if (cluster->count < object->count)
1171 object=cluster;
1172 cluster=cluster->next;
1173 }
1174 background=head->next;
1175 for (cluster=background; cluster->next != (Cluster *) NULL; )
1176 {
1177 if (cluster->count > background->count)
1178 background=cluster;
1179 cluster=cluster->next;
1180 }
1181 }
1182 threshold=(background->red.center+object->red.center)/2.0;
1183 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
1184 (threshold+0.5));
1185 threshold=(background->green.center+object->green.center)/2.0;
1186 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
1187 (threshold+0.5));
1188 threshold=(background->blue.center+object->blue.center)/2.0;
1189 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
1190 (threshold+0.5));
1191 /*
1192 Relinquish resources.
1193 */
1194 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1195 {
1196 next_cluster=cluster->next;
1197 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1198 }
1199 for (i=0; i < MaxDimension; i++)
1200 {
1201 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001202 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001203 }
1204 return(MagickTrue);
1205}
1206
1207/*
1208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209% %
1210% %
1211% %
1212+ I n i t i a l i z e H i s t o g r a m %
1213% %
1214% %
1215% %
1216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217%
1218% InitializeHistogram() computes the histogram for an image.
1219%
1220% The format of the InitializeHistogram method is:
1221%
cristybb503372010-05-27 20:51:26 +00001222% InitializeHistogram(const Image *image,ssize_t **histogram)
cristy3ed852e2009-09-05 21:47:34 +00001223%
1224% A description of each parameter follows.
1225%
1226% o image: Specifies a pointer to an Image structure; returned from
1227% ReadImage.
1228%
1229% o histogram: Specifies an array of integers representing the number
1230% of pixels for each intensity of a particular color component.
1231%
1232*/
cristybb503372010-05-27 20:51:26 +00001233static void InitializeHistogram(const Image *image,ssize_t **histogram,
cristy3ed852e2009-09-05 21:47:34 +00001234 ExceptionInfo *exception)
1235{
cristy3ed852e2009-09-05 21:47:34 +00001236 register const PixelPacket
1237 *p;
1238
cristybb503372010-05-27 20:51:26 +00001239 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001240 i,
1241 x;
1242
cristy9d314ff2011-03-09 01:30:28 +00001243 ssize_t
1244 y;
1245
cristy3ed852e2009-09-05 21:47:34 +00001246 /*
1247 Initialize histogram.
1248 */
1249 for (i=0; i <= 255; i++)
1250 {
1251 histogram[Red][i]=0;
1252 histogram[Green][i]=0;
1253 histogram[Blue][i]=0;
1254 }
cristybb503372010-05-27 20:51:26 +00001255 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001256 {
1257 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1258 if (p == (const PixelPacket *) NULL)
1259 break;
cristybb503372010-05-27 20:51:26 +00001260 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001261 {
cristybb503372010-05-27 20:51:26 +00001262 histogram[Red][(ssize_t) ScaleQuantumToChar(GetRedPixelComponent(p))]++;
1263 histogram[Green][(ssize_t) ScaleQuantumToChar(GetGreenPixelComponent(p))]++;
1264 histogram[Blue][(ssize_t) ScaleQuantumToChar(GetBluePixelComponent(p))]++;
cristy3ed852e2009-09-05 21:47:34 +00001265 p++;
1266 }
1267 }
1268}
1269
1270/*
1271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272% %
1273% %
1274% %
1275+ I n i t i a l i z e I n t e r v a l T r e e %
1276% %
1277% %
1278% %
1279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280%
1281% InitializeIntervalTree() initializes an interval tree from the lists of
1282% zero crossings.
1283%
1284% The format of the InitializeIntervalTree method is:
1285%
cristybb503372010-05-27 20:51:26 +00001286% InitializeIntervalTree(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001287% IntervalTree *node)
1288%
1289% A description of each parameter follows.
1290%
1291% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
1292%
cristybb503372010-05-27 20:51:26 +00001293% o number_crossings: This size_t specifies the number of elements
cristy3ed852e2009-09-05 21:47:34 +00001294% in the zero_crossing array.
1295%
1296*/
1297
cristybb503372010-05-27 20:51:26 +00001298static void InitializeList(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001299 IntervalTree *node)
1300{
1301 if (node == (IntervalTree *) NULL)
1302 return;
1303 if (node->child == (IntervalTree *) NULL)
1304 list[(*number_nodes)++]=node;
1305 InitializeList(list,number_nodes,node->sibling);
1306 InitializeList(list,number_nodes,node->child);
1307}
1308
1309static void MeanStability(IntervalTree *node)
1310{
1311 register IntervalTree
1312 *child;
1313
1314 if (node == (IntervalTree *) NULL)
1315 return;
1316 node->mean_stability=0.0;
1317 child=node->child;
1318 if (child != (IntervalTree *) NULL)
1319 {
cristybb503372010-05-27 20:51:26 +00001320 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001321 count;
1322
1323 register MagickRealType
1324 sum;
1325
1326 sum=0.0;
1327 count=0;
1328 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
1329 {
1330 sum+=child->stability;
1331 count++;
1332 }
1333 node->mean_stability=sum/(MagickRealType) count;
1334 }
1335 MeanStability(node->sibling);
1336 MeanStability(node->child);
1337}
1338
1339static void Stability(IntervalTree *node)
1340{
1341 if (node == (IntervalTree *) NULL)
1342 return;
1343 if (node->child == (IntervalTree *) NULL)
1344 node->stability=0.0;
1345 else
1346 node->stability=node->tau-(node->child)->tau;
1347 Stability(node->sibling);
1348 Stability(node->child);
1349}
1350
1351static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +00001352 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +00001353{
1354 IntervalTree
1355 *head,
1356 **list,
1357 *node,
1358 *root;
1359
cristy9d314ff2011-03-09 01:30:28 +00001360 register ssize_t
1361 i;
1362
cristybb503372010-05-27 20:51:26 +00001363 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001364 j,
1365 k,
1366 left,
1367 number_nodes;
1368
cristy3ed852e2009-09-05 21:47:34 +00001369 /*
1370 Allocate interval tree.
1371 */
1372 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1373 sizeof(*list));
1374 if (list == (IntervalTree **) NULL)
1375 return((IntervalTree *) NULL);
1376 /*
1377 The root is the entire histogram.
1378 */
cristy73bd4a52010-10-05 11:24:23 +00001379 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
cristy3ed852e2009-09-05 21:47:34 +00001380 root->child=(IntervalTree *) NULL;
1381 root->sibling=(IntervalTree *) NULL;
1382 root->tau=0.0;
1383 root->left=0;
1384 root->right=255;
cristybb503372010-05-27 20:51:26 +00001385 for (i=(-1); i < (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001386 {
1387 /*
1388 Initialize list with all nodes with no children.
1389 */
1390 number_nodes=0;
1391 InitializeList(list,&number_nodes,root);
1392 /*
1393 Split list.
1394 */
1395 for (j=0; j < number_nodes; j++)
1396 {
1397 head=list[j];
1398 left=head->left;
1399 node=head;
1400 for (k=head->left+1; k < head->right; k++)
1401 {
1402 if (zero_crossing[i+1].crossings[k] != 0)
1403 {
1404 if (node == head)
1405 {
1406 node->child=(IntervalTree *) AcquireMagickMemory(
1407 sizeof(*node->child));
1408 node=node->child;
1409 }
1410 else
1411 {
1412 node->sibling=(IntervalTree *) AcquireMagickMemory(
1413 sizeof(*node->sibling));
1414 node=node->sibling;
1415 }
1416 node->tau=zero_crossing[i+1].tau;
1417 node->child=(IntervalTree *) NULL;
1418 node->sibling=(IntervalTree *) NULL;
1419 node->left=left;
1420 node->right=k;
1421 left=k;
1422 }
1423 }
1424 if (left != head->left)
1425 {
1426 node->sibling=(IntervalTree *) AcquireMagickMemory(
1427 sizeof(*node->sibling));
1428 node=node->sibling;
1429 node->tau=zero_crossing[i+1].tau;
1430 node->child=(IntervalTree *) NULL;
1431 node->sibling=(IntervalTree *) NULL;
1432 node->left=left;
1433 node->right=head->right;
1434 }
1435 }
1436 }
1437 /*
1438 Determine the stability: difference between a nodes tau and its child.
1439 */
1440 Stability(root->child);
1441 MeanStability(root->child);
1442 list=(IntervalTree **) RelinquishMagickMemory(list);
1443 return(root);
1444}
1445
1446/*
1447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448% %
1449% %
1450% %
1451+ O p t i m a l T a u %
1452% %
1453% %
1454% %
1455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456%
1457% OptimalTau() finds the optimal tau for each band of the histogram.
1458%
1459% The format of the OptimalTau method is:
1460%
cristybb503372010-05-27 20:51:26 +00001461% MagickRealType OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001462% const double min_tau,const double delta_tau,
1463% const double smooth_threshold,short *extrema)
1464%
1465% A description of each parameter follows.
1466%
1467% o histogram: Specifies an array of integers representing the number
1468% of pixels for each intensity of a particular color component.
1469%
1470% o extrema: Specifies a pointer to an array of integers. They
1471% represent the peaks and valleys of the histogram for each color
1472% component.
1473%
1474*/
1475
cristybb503372010-05-27 20:51:26 +00001476static void ActiveNodes(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001477 IntervalTree *node)
1478{
1479 if (node == (IntervalTree *) NULL)
1480 return;
1481 if (node->stability >= node->mean_stability)
1482 {
1483 list[(*number_nodes)++]=node;
1484 ActiveNodes(list,number_nodes,node->sibling);
1485 }
1486 else
1487 {
1488 ActiveNodes(list,number_nodes,node->sibling);
1489 ActiveNodes(list,number_nodes,node->child);
1490 }
1491}
1492
1493static void FreeNodes(IntervalTree *node)
1494{
1495 if (node == (IntervalTree *) NULL)
1496 return;
1497 FreeNodes(node->sibling);
1498 FreeNodes(node->child);
1499 node=(IntervalTree *) RelinquishMagickMemory(node);
1500}
1501
cristybb503372010-05-27 20:51:26 +00001502static MagickRealType OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001503 const double min_tau,const double delta_tau,const double smooth_threshold,
1504 short *extrema)
1505{
1506 IntervalTree
1507 **list,
1508 *node,
1509 *root;
1510
cristy9d314ff2011-03-09 01:30:28 +00001511 MagickBooleanType
1512 peak;
cristy3ed852e2009-09-05 21:47:34 +00001513
1514 MagickRealType
1515 average_tau,
1516 *derivative,
1517 *second_derivative,
1518 tau,
1519 value;
1520
cristybb503372010-05-27 20:51:26 +00001521 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001522 i,
1523 x;
1524
cristybb503372010-05-27 20:51:26 +00001525 size_t
cristy3ed852e2009-09-05 21:47:34 +00001526 count,
1527 number_crossings;
1528
cristy9d314ff2011-03-09 01:30:28 +00001529 ssize_t
1530 index,
1531 j,
1532 k,
1533 number_nodes;
1534
cristy3ed852e2009-09-05 21:47:34 +00001535 ZeroCrossing
1536 *zero_crossing;
1537
1538 /*
1539 Allocate interval tree.
1540 */
1541 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1542 sizeof(*list));
1543 if (list == (IntervalTree **) NULL)
1544 return(0.0);
1545 /*
1546 Allocate zero crossing list.
1547 */
cristybb503372010-05-27 20:51:26 +00001548 count=(size_t) ((max_tau-min_tau)/delta_tau)+2;
cristy3ed852e2009-09-05 21:47:34 +00001549 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
1550 sizeof(*zero_crossing));
1551 if (zero_crossing == (ZeroCrossing *) NULL)
1552 return(0.0);
cristybb503372010-05-27 20:51:26 +00001553 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00001554 zero_crossing[i].tau=(-1.0);
1555 /*
1556 Initialize zero crossing list.
1557 */
1558 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
1559 second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
1560 sizeof(*second_derivative));
1561 if ((derivative == (MagickRealType *) NULL) ||
1562 (second_derivative == (MagickRealType *) NULL))
1563 ThrowFatalException(ResourceLimitFatalError,
1564 "UnableToAllocateDerivatives");
1565 i=0;
1566 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
1567 {
1568 zero_crossing[i].tau=tau;
1569 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
1570 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1571 DerivativeHistogram(derivative,second_derivative);
1572 ZeroCrossHistogram(second_derivative,smooth_threshold,
1573 zero_crossing[i].crossings);
1574 i++;
1575 }
1576 /*
1577 Add an entry for the original histogram.
1578 */
1579 zero_crossing[i].tau=0.0;
1580 for (j=0; j <= 255; j++)
1581 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
1582 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1583 DerivativeHistogram(derivative,second_derivative);
1584 ZeroCrossHistogram(second_derivative,smooth_threshold,
1585 zero_crossing[i].crossings);
cristybb503372010-05-27 20:51:26 +00001586 number_crossings=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001587 derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
1588 second_derivative=(MagickRealType *)
1589 RelinquishMagickMemory(second_derivative);
1590 /*
1591 Ensure the scale-space fingerprints form lines in scale-space, not loops.
1592 */
1593 ConsolidateCrossings(zero_crossing,number_crossings);
1594 /*
1595 Force endpoints to be included in the interval.
1596 */
cristybb503372010-05-27 20:51:26 +00001597 for (i=0; i <= (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001598 {
1599 for (j=0; j < 255; j++)
1600 if (zero_crossing[i].crossings[j] != 0)
1601 break;
1602 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
1603 for (j=255; j > 0; j--)
1604 if (zero_crossing[i].crossings[j] != 0)
1605 break;
1606 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
1607 }
1608 /*
1609 Initialize interval tree.
1610 */
1611 root=InitializeIntervalTree(zero_crossing,number_crossings);
1612 if (root == (IntervalTree *) NULL)
1613 return(0.0);
1614 /*
1615 Find active nodes: stability is greater (or equal) to the mean stability of
1616 its children.
1617 */
1618 number_nodes=0;
1619 ActiveNodes(list,&number_nodes,root->child);
1620 /*
1621 Initialize extrema.
1622 */
1623 for (i=0; i <= 255; i++)
1624 extrema[i]=0;
1625 for (i=0; i < number_nodes; i++)
1626 {
1627 /*
1628 Find this tau in zero crossings list.
1629 */
1630 k=0;
1631 node=list[i];
cristybb503372010-05-27 20:51:26 +00001632 for (j=0; j <= (ssize_t) number_crossings; j++)
cristy3ed852e2009-09-05 21:47:34 +00001633 if (zero_crossing[j].tau == node->tau)
1634 k=j;
1635 /*
1636 Find the value of the peak.
1637 */
1638 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
1639 MagickFalse;
1640 index=node->left;
1641 value=zero_crossing[k].histogram[index];
1642 for (x=node->left; x <= node->right; x++)
1643 {
1644 if (peak != MagickFalse)
1645 {
1646 if (zero_crossing[k].histogram[x] > value)
1647 {
1648 value=zero_crossing[k].histogram[x];
1649 index=x;
1650 }
1651 }
1652 else
1653 if (zero_crossing[k].histogram[x] < value)
1654 {
1655 value=zero_crossing[k].histogram[x];
1656 index=x;
1657 }
1658 }
1659 for (x=node->left; x <= node->right; x++)
1660 {
1661 if (index == 0)
1662 index=256;
1663 if (peak != MagickFalse)
1664 extrema[x]=(short) index;
1665 else
1666 extrema[x]=(short) (-index);
1667 }
1668 }
1669 /*
1670 Determine the average tau.
1671 */
1672 average_tau=0.0;
1673 for (i=0; i < number_nodes; i++)
1674 average_tau+=list[i]->tau;
1675 average_tau/=(MagickRealType) number_nodes;
1676 /*
1677 Relinquish resources.
1678 */
1679 FreeNodes(root);
1680 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
1681 list=(IntervalTree **) RelinquishMagickMemory(list);
1682 return(average_tau);
1683}
1684
1685/*
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687% %
1688% %
1689% %
1690+ S c a l e S p a c e %
1691% %
1692% %
1693% %
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695%
1696% ScaleSpace() performs a scale-space filter on the 1D histogram.
1697%
1698% The format of the ScaleSpace method is:
1699%
cristybb503372010-05-27 20:51:26 +00001700% ScaleSpace(const ssize_t *histogram,const MagickRealType tau,
cristy3ed852e2009-09-05 21:47:34 +00001701% MagickRealType *scale_histogram)
1702%
1703% A description of each parameter follows.
1704%
1705% o histogram: Specifies an array of MagickRealTypes representing the number
1706% of pixels for each intensity of a particular color component.
1707%
1708*/
1709
cristybb503372010-05-27 20:51:26 +00001710static void ScaleSpace(const ssize_t *histogram,const MagickRealType tau,
cristy3ed852e2009-09-05 21:47:34 +00001711 MagickRealType *scale_histogram)
1712{
1713 MagickRealType
1714 alpha,
1715 beta,
1716 *gamma,
1717 sum;
1718
cristybb503372010-05-27 20:51:26 +00001719 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001720 u,
1721 x;
1722
1723 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
1724 if (gamma == (MagickRealType *) NULL)
1725 ThrowFatalException(ResourceLimitFatalError,
1726 "UnableToAllocateGammaMap");
1727 alpha=1.0/(tau*sqrt(2.0*MagickPI));
1728 beta=(-1.0/(2.0*tau*tau));
1729 for (x=0; x <= 255; x++)
1730 gamma[x]=0.0;
1731 for (x=0; x <= 255; x++)
1732 {
1733 gamma[x]=exp((double) beta*x*x);
1734 if (gamma[x] < MagickEpsilon)
1735 break;
1736 }
1737 for (x=0; x <= 255; x++)
1738 {
1739 sum=0.0;
1740 for (u=0; u <= 255; u++)
1741 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
1742 scale_histogram[x]=alpha*sum;
1743 }
1744 gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
1745}
1746
1747/*
1748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1749% %
1750% %
1751% %
1752% S e g m e n t I m a g e %
1753% %
1754% %
1755% %
1756%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1757%
1758% SegmentImage() segment an image by analyzing the histograms of the color
1759% components and identifying units that are homogeneous with the fuzzy
1760% C-means technique.
1761%
1762% The format of the SegmentImage method is:
1763%
1764% MagickBooleanType SegmentImage(Image *image,
1765% const ColorspaceType colorspace,const MagickBooleanType verbose,
1766% const double cluster_threshold,const double smooth_threshold)
1767%
1768% A description of each parameter follows.
1769%
1770% o image: the image.
1771%
1772% o colorspace: Indicate the colorspace.
1773%
1774% o verbose: Set to MagickTrue to print detailed information about the
1775% identified classes.
1776%
1777% o cluster_threshold: This represents the minimum number of pixels
1778% contained in a hexahedra before it can be considered valid (expressed
1779% as a percentage).
1780%
1781% o smooth_threshold: the smoothing threshold eliminates noise in the second
1782% derivative of the histogram. As the value is increased, you can expect a
1783% smoother second derivative.
1784%
1785*/
1786MagickExport MagickBooleanType SegmentImage(Image *image,
1787 const ColorspaceType colorspace,const MagickBooleanType verbose,
1788 const double cluster_threshold,const double smooth_threshold)
1789{
cristy3ed852e2009-09-05 21:47:34 +00001790 MagickBooleanType
1791 status;
1792
cristybb503372010-05-27 20:51:26 +00001793 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001794 i;
1795
1796 short
1797 *extrema[MaxDimension];
1798
cristy9d314ff2011-03-09 01:30:28 +00001799 ssize_t
1800 *histogram[MaxDimension];
1801
cristy3ed852e2009-09-05 21:47:34 +00001802 /*
1803 Allocate histogram and extrema.
1804 */
1805 assert(image != (Image *) NULL);
1806 assert(image->signature == MagickSignature);
1807 if (image->debug != MagickFalse)
1808 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1809 for (i=0; i < MaxDimension; i++)
1810 {
cristybb503372010-05-27 20:51:26 +00001811 histogram[i]=(ssize_t *) AcquireQuantumMemory(256,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +00001812 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
cristybb503372010-05-27 20:51:26 +00001813 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001814 {
1815 for (i-- ; i >= 0; i--)
1816 {
1817 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001818 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001819 }
1820 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1821 image->filename)
1822 }
1823 }
1824 if (colorspace != RGBColorspace)
1825 (void) TransformImageColorspace(image,colorspace);
1826 /*
1827 Initialize histogram.
1828 */
1829 InitializeHistogram(image,histogram,&image->exception);
1830 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
1831 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
1832 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
1833 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
1834 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
1835 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
1836 /*
1837 Classify using the fuzzy c-Means technique.
1838 */
1839 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
1840 if (colorspace != RGBColorspace)
1841 (void) TransformImageColorspace(image,colorspace);
1842 /*
1843 Relinquish resources.
1844 */
1845 for (i=0; i < MaxDimension; i++)
1846 {
1847 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001848 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001849 }
1850 return(status);
1851}
1852
1853/*
1854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1855% %
1856% %
1857% %
1858+ Z e r o C r o s s H i s t o g r a m %
1859% %
1860% %
1861% %
1862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1863%
1864% ZeroCrossHistogram() find the zero crossings in a histogram and marks
1865% directions as: 1 is negative to positive; 0 is zero crossing; and -1
1866% is positive to negative.
1867%
1868% The format of the ZeroCrossHistogram method is:
1869%
1870% ZeroCrossHistogram(MagickRealType *second_derivative,
1871% const MagickRealType smooth_threshold,short *crossings)
1872%
1873% A description of each parameter follows.
1874%
1875% o second_derivative: Specifies an array of MagickRealTypes representing the
1876% second derivative of the histogram of a particular color component.
1877%
1878% o crossings: This array of integers is initialized with
1879% -1, 0, or 1 representing the slope of the first derivative of the
1880% of a particular color component.
1881%
1882*/
1883static void ZeroCrossHistogram(MagickRealType *second_derivative,
1884 const MagickRealType smooth_threshold,short *crossings)
1885{
cristybb503372010-05-27 20:51:26 +00001886 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001887 i;
1888
cristy9d314ff2011-03-09 01:30:28 +00001889 ssize_t
1890 parity;
1891
cristy3ed852e2009-09-05 21:47:34 +00001892 /*
1893 Merge low numbers to zero to help prevent noise.
1894 */
1895 for (i=0; i <= 255; i++)
1896 if ((second_derivative[i] < smooth_threshold) &&
1897 (second_derivative[i] >= -smooth_threshold))
1898 second_derivative[i]=0.0;
1899 /*
1900 Mark zero crossings.
1901 */
1902 parity=0;
1903 for (i=0; i <= 255; i++)
1904 {
1905 crossings[i]=0;
1906 if (second_derivative[i] < 0.0)
1907 {
1908 if (parity > 0)
1909 crossings[i]=(-1);
1910 parity=1;
1911 }
1912 else
1913 if (second_derivative[i] > 0.0)
1914 {
1915 if (parity < 0)
1916 crossings[i]=1;
1917 parity=(-1);
1918 }
1919 }
1920}