blob: 3489ee36039c685b319fa051bcf0b10aa7112127 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% April 1993 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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
anthonye5b39652012-04-21 05:37:29 +000052% at each scale. Analyze this scale-space ''fingerprint'' to
cristy3ed852e2009-09-05 21:47:34 +000053% 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,
anthonye5b39652012-04-21 05:37:29 +000059% that pixel is considered ''classified'' and is assigned an unique
cristy3ed852e2009-09-05 21:47:34 +000060% 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
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/studio.h"
86#include "MagickCore/cache.h"
87#include "MagickCore/color.h"
88#include "MagickCore/colormap.h"
89#include "MagickCore/colorspace.h"
cristy6a566122011-07-07 00:12:37 +000090#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000091#include "MagickCore/exception.h"
92#include "MagickCore/exception-private.h"
93#include "MagickCore/image.h"
94#include "MagickCore/image-private.h"
95#include "MagickCore/memory_.h"
96#include "MagickCore/monitor.h"
97#include "MagickCore/monitor-private.h"
98#include "MagickCore/pixel-accessor.h"
cristy35f15302012-06-07 14:59:02 +000099#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +0000100#include "MagickCore/quantize.h"
101#include "MagickCore/quantum.h"
102#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +0000103#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +0000104#include "MagickCore/segment.h"
105#include "MagickCore/string_.h"
cristy16881e62012-05-06 14:41:29 +0000106#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +0000107
108/*
109 Define declarations.
110*/
111#define MaxDimension 3
112#define DeltaTau 0.5f
113#if defined(FastClassify)
114#define WeightingExponent 2.0
115#define SegmentPower(ratio) (ratio)
116#else
117#define WeightingExponent 2.5
118#define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
119#endif
120#define Tau 5.2f
121
122/*
123 Typedef declarations.
124*/
125typedef struct _ExtentPacket
126{
cristya19f1d72012-08-07 18:24:38 +0000127 double
cristy3ed852e2009-09-05 21:47:34 +0000128 center;
129
cristybb503372010-05-27 20:51:26 +0000130 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000131 index,
132 left,
133 right;
134} ExtentPacket;
135
136typedef struct _Cluster
137{
138 struct _Cluster
139 *next;
140
141 ExtentPacket
142 red,
143 green,
144 blue;
145
cristybb503372010-05-27 20:51:26 +0000146 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000147 count,
148 id;
149} Cluster;
150
151typedef struct _IntervalTree
152{
cristya19f1d72012-08-07 18:24:38 +0000153 double
cristy3ed852e2009-09-05 21:47:34 +0000154 tau;
155
cristybb503372010-05-27 20:51:26 +0000156 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000157 left,
158 right;
159
cristya19f1d72012-08-07 18:24:38 +0000160 double
cristy3ed852e2009-09-05 21:47:34 +0000161 mean_stability,
162 stability;
163
164 struct _IntervalTree
165 *sibling,
166 *child;
167} IntervalTree;
168
169typedef struct _ZeroCrossing
170{
cristya19f1d72012-08-07 18:24:38 +0000171 double
cristy3ed852e2009-09-05 21:47:34 +0000172 tau,
173 histogram[256];
174
175 short
176 crossings[256];
177} ZeroCrossing;
178
179/*
180 Constant declarations.
181*/
182static const int
183 Blue = 2,
184 Green = 1,
185 Red = 0,
186 SafeMargin = 3,
187 TreeLength = 600;
188
189/*
190 Method prototypes.
191*/
cristya19f1d72012-08-07 18:24:38 +0000192static double
cristybb503372010-05-27 20:51:26 +0000193 OptimalTau(const ssize_t *,const double,const double,const double,
cristy3ed852e2009-09-05 21:47:34 +0000194 const double,short *);
195
cristybb503372010-05-27 20:51:26 +0000196static ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000197 DefineRegion(const short *,ExtentPacket *);
198
199static void
cristybb503372010-05-27 20:51:26 +0000200 InitializeHistogram(const Image *,ssize_t **,ExceptionInfo *),
cristya19f1d72012-08-07 18:24:38 +0000201 ScaleSpace(const ssize_t *,const double,double *),
202 ZeroCrossHistogram(double *,const double,short *);
cristy3ed852e2009-09-05 21:47:34 +0000203
204/*
205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206% %
207% %
208% %
209+ C l a s s i f y %
210% %
211% %
212% %
213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214%
215% Classify() defines one or more classes. Each pixel is thresholded to
cristy33c53022010-06-25 12:17:27 +0000216% determine which class it belongs to. If the class is not identified it is
cristy3ed852e2009-09-05 21:47:34 +0000217% assigned to the closest class based on the fuzzy c-Means technique.
218%
219% The format of the Classify method is:
220%
221% MagickBooleanType Classify(Image *image,short **extrema,
cristya19f1d72012-08-07 18:24:38 +0000222% const double cluster_threshold,
223% const double weighting_exponent,
cristy018f07f2011-09-04 21:15:19 +0000224% const MagickBooleanType verbose,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000225%
226% A description of each parameter follows.
227%
228% o image: the image.
229%
230% o extrema: Specifies a pointer to an array of integers. They
231% represent the peaks and valleys of the histogram for each color
232% component.
233%
cristya19f1d72012-08-07 18:24:38 +0000234% o cluster_threshold: This double represents the minimum number of
cristy3ed852e2009-09-05 21:47:34 +0000235% pixels contained in a hexahedra before it can be considered valid
236% (expressed as a percentage).
237%
238% o weighting_exponent: Specifies the membership weighting exponent.
239%
240% o verbose: A value greater than zero prints detailed information about
241% the identified classes.
242%
cristy018f07f2011-09-04 21:15:19 +0000243% o exception: return any errors or warnings in this structure.
244%
cristy3ed852e2009-09-05 21:47:34 +0000245*/
246static MagickBooleanType Classify(Image *image,short **extrema,
cristya19f1d72012-08-07 18:24:38 +0000247 const double cluster_threshold,
248 const double weighting_exponent,const MagickBooleanType verbose,
cristy018f07f2011-09-04 21:15:19 +0000249 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000250{
251#define SegmentImageTag "Segment/Image"
252
cristyc4c8d132010-01-07 01:58:38 +0000253 CacheView
254 *image_view;
255
cristy3ed852e2009-09-05 21:47:34 +0000256 Cluster
257 *cluster,
258 *head,
259 *last_cluster,
260 *next_cluster;
261
cristy3ed852e2009-09-05 21:47:34 +0000262 ExtentPacket
263 blue,
264 green,
265 red;
266
cristy5f959472010-05-27 22:19:46 +0000267 MagickOffsetType
268 progress;
cristy3ed852e2009-09-05 21:47:34 +0000269
cristya19f1d72012-08-07 18:24:38 +0000270 double
cristy3ed852e2009-09-05 21:47:34 +0000271 *free_squares;
272
273 MagickStatusType
274 status;
275
cristybb503372010-05-27 20:51:26 +0000276 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000277 i;
278
cristya19f1d72012-08-07 18:24:38 +0000279 register double
cristy3ed852e2009-09-05 21:47:34 +0000280 *squares;
281
cristybb503372010-05-27 20:51:26 +0000282 size_t
cristy3ed852e2009-09-05 21:47:34 +0000283 number_clusters;
284
cristy5f959472010-05-27 22:19:46 +0000285 ssize_t
286 count,
287 y;
288
cristy3ed852e2009-09-05 21:47:34 +0000289 /*
290 Form clusters.
291 */
292 cluster=(Cluster *) NULL;
293 head=(Cluster *) NULL;
294 (void) ResetMagickMemory(&red,0,sizeof(red));
295 (void) ResetMagickMemory(&green,0,sizeof(green));
296 (void) ResetMagickMemory(&blue,0,sizeof(blue));
297 while (DefineRegion(extrema[Red],&red) != 0)
298 {
299 green.index=0;
300 while (DefineRegion(extrema[Green],&green) != 0)
301 {
302 blue.index=0;
303 while (DefineRegion(extrema[Blue],&blue) != 0)
304 {
305 /*
306 Allocate a new class.
307 */
308 if (head != (Cluster *) NULL)
309 {
310 cluster->next=(Cluster *) AcquireMagickMemory(
311 sizeof(*cluster->next));
312 cluster=cluster->next;
313 }
314 else
315 {
cristy73bd4a52010-10-05 11:24:23 +0000316 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +0000317 head=cluster;
318 }
319 if (cluster == (Cluster *) NULL)
320 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
321 image->filename);
322 /*
323 Initialize a new class.
324 */
325 cluster->count=0;
326 cluster->red=red;
327 cluster->green=green;
328 cluster->blue=blue;
329 cluster->next=(Cluster *) NULL;
330 }
331 }
332 }
333 if (head == (Cluster *) NULL)
334 {
335 /*
336 No classes were identified-- create one.
337 */
cristy73bd4a52010-10-05 11:24:23 +0000338 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +0000339 if (cluster == (Cluster *) NULL)
340 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
341 image->filename);
342 /*
343 Initialize a new class.
344 */
345 cluster->count=0;
346 cluster->red=red;
347 cluster->green=green;
348 cluster->blue=blue;
349 cluster->next=(Cluster *) NULL;
350 head=cluster;
351 }
352 /*
353 Count the pixels for each cluster.
354 */
355 status=MagickTrue;
356 count=0;
357 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000358 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000359 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000360 {
cristy4c08aed2011-07-01 19:47:50 +0000361 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000362 *p;
363
cristybb503372010-05-27 20:51:26 +0000364 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000365 x;
366
367 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000368 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000369 break;
cristybb503372010-05-27 20:51:26 +0000370 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000371 {
372 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristy4c08aed2011-07-01 19:47:50 +0000373 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000374 (cluster->red.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000375 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000376 (cluster->red.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000377 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000378 (cluster->green.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000379 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000380 (cluster->green.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000381 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +0000382 (cluster->blue.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000383 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +0000384 (cluster->blue.right+SafeMargin)))
385 {
386 /*
387 Count this pixel.
388 */
389 count++;
cristya19f1d72012-08-07 18:24:38 +0000390 cluster->red.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +0000391 GetPixelRed(image,p));
cristya19f1d72012-08-07 18:24:38 +0000392 cluster->green.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +0000393 GetPixelGreen(image,p));
cristya19f1d72012-08-07 18:24:38 +0000394 cluster->blue.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +0000395 GetPixelBlue(image,p));
cristy3ed852e2009-09-05 21:47:34 +0000396 cluster->count++;
397 break;
398 }
cristyed231572011-07-14 02:18:59 +0000399 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000400 }
401 if (image->progress_monitor != (MagickProgressMonitor) NULL)
402 {
403 MagickBooleanType
404 proceed;
405
cristyb5d5f722009-11-04 03:03:49 +0000406#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000407 #pragma omp critical (MagickCore_Classify)
408#endif
cristy78778cb2012-01-17 14:48:47 +0000409 proceed=SetImageProgress(image,SegmentImageTag,progress++,2*
410 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000411 if (proceed == MagickFalse)
412 status=MagickFalse;
413 }
414 }
415 image_view=DestroyCacheView(image_view);
416 /*
417 Remove clusters that do not meet minimum cluster threshold.
418 */
419 count=0;
420 last_cluster=head;
421 next_cluster=head;
422 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
423 {
424 next_cluster=cluster->next;
425 if ((cluster->count > 0) &&
426 (cluster->count >= (count*cluster_threshold/100.0)))
427 {
428 /*
429 Initialize cluster.
430 */
431 cluster->id=count;
432 cluster->red.center/=cluster->count;
433 cluster->green.center/=cluster->count;
434 cluster->blue.center/=cluster->count;
435 count++;
436 last_cluster=cluster;
437 continue;
438 }
439 /*
440 Delete cluster.
441 */
442 if (cluster == head)
443 head=next_cluster;
444 else
445 last_cluster->next=next_cluster;
446 cluster=(Cluster *) RelinquishMagickMemory(cluster);
447 }
cristybb503372010-05-27 20:51:26 +0000448 number_clusters=(size_t) count;
cristy3ed852e2009-09-05 21:47:34 +0000449 if (verbose != MagickFalse)
450 {
451 /*
452 Print cluster statistics.
453 */
cristyb51dff52011-05-19 16:55:47 +0000454 (void) FormatLocaleFile(stdout,"Fuzzy C-means Statistics\n");
455 (void) FormatLocaleFile(stdout,"===================\n\n");
456 (void) FormatLocaleFile(stdout,"\tCluster Threshold = %g\n",(double)
cristy4f3c0be2009-09-12 16:04:05 +0000457 cluster_threshold);
cristyb51dff52011-05-19 16:55:47 +0000458 (void) FormatLocaleFile(stdout,"\tWeighting Exponent = %g\n",(double)
cristy4f3c0be2009-09-12 16:04:05 +0000459 weighting_exponent);
cristy1e604812011-05-19 18:07:50 +0000460 (void) FormatLocaleFile(stdout,"\tTotal Number of Clusters = %.20g\n\n",
461 (double) number_clusters);
cristy3ed852e2009-09-05 21:47:34 +0000462 /*
463 Print the total number of points per cluster.
464 */
cristyb51dff52011-05-19 16:55:47 +0000465 (void) FormatLocaleFile(stdout,"\n\nNumber of Vectors Per Cluster\n");
466 (void) FormatLocaleFile(stdout,"=============================\n\n");
cristy3ed852e2009-09-05 21:47:34 +0000467 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristy1e604812011-05-19 18:07:50 +0000468 (void) FormatLocaleFile(stdout,"Cluster #%.20g = %.20g\n",(double)
469 cluster->id,(double) cluster->count);
cristy3ed852e2009-09-05 21:47:34 +0000470 /*
471 Print the cluster extents.
472 */
cristyb51dff52011-05-19 16:55:47 +0000473 (void) FormatLocaleFile(stdout,
cristy3ed852e2009-09-05 21:47:34 +0000474 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
cristyb51dff52011-05-19 16:55:47 +0000475 (void) FormatLocaleFile(stdout,"================");
cristy3ed852e2009-09-05 21:47:34 +0000476 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
477 {
cristy1e604812011-05-19 18:07:50 +0000478 (void) FormatLocaleFile(stdout,"\n\nCluster #%.20g\n\n",(double)
479 cluster->id);
480 (void) FormatLocaleFile(stdout,
481 "%.20g-%.20g %.20g-%.20g %.20g-%.20g\n",(double)
cristye8c25f92010-06-03 00:53:06 +0000482 cluster->red.left,(double) cluster->red.right,(double)
483 cluster->green.left,(double) cluster->green.right,(double)
484 cluster->blue.left,(double) cluster->blue.right);
cristy3ed852e2009-09-05 21:47:34 +0000485 }
486 /*
487 Print the cluster center values.
488 */
cristyb51dff52011-05-19 16:55:47 +0000489 (void) FormatLocaleFile(stdout,
cristy3ed852e2009-09-05 21:47:34 +0000490 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
cristyb51dff52011-05-19 16:55:47 +0000491 (void) FormatLocaleFile(stdout,"=====================");
cristy3ed852e2009-09-05 21:47:34 +0000492 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
493 {
cristy1e604812011-05-19 18:07:50 +0000494 (void) FormatLocaleFile(stdout,"\n\nCluster #%.20g\n\n",(double)
495 cluster->id);
cristyb51dff52011-05-19 16:55:47 +0000496 (void) FormatLocaleFile(stdout,"%g %g %g\n",(double)
cristy8cd5b312010-01-07 01:10:24 +0000497 cluster->red.center,(double) cluster->green.center,(double)
498 cluster->blue.center);
cristy3ed852e2009-09-05 21:47:34 +0000499 }
cristyb51dff52011-05-19 16:55:47 +0000500 (void) FormatLocaleFile(stdout,"\n");
cristy3ed852e2009-09-05 21:47:34 +0000501 }
502 if (number_clusters > 256)
503 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
504 /*
505 Speed up distance calculations.
506 */
cristya19f1d72012-08-07 18:24:38 +0000507 squares=(double *) AcquireQuantumMemory(513UL,sizeof(*squares));
508 if (squares == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000509 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
510 image->filename);
511 squares+=255;
512 for (i=(-255); i <= 255; i++)
cristya19f1d72012-08-07 18:24:38 +0000513 squares[i]=(double) i*(double) i;
cristy3ed852e2009-09-05 21:47:34 +0000514 /*
515 Allocate image colormap.
516 */
cristy018f07f2011-09-04 21:15:19 +0000517 if (AcquireImageColormap(image,number_clusters,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000518 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
519 image->filename);
520 i=0;
521 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
522 {
cristye42f6582012-02-11 17:59:50 +0000523 image->colormap[i].red=(double) ScaleCharToQuantum((unsigned char)
cristy3ed852e2009-09-05 21:47:34 +0000524 (cluster->red.center+0.5));
cristye42f6582012-02-11 17:59:50 +0000525 image->colormap[i].green=(double) ScaleCharToQuantum((unsigned char)
cristy3ed852e2009-09-05 21:47:34 +0000526 (cluster->green.center+0.5));
cristye42f6582012-02-11 17:59:50 +0000527 image->colormap[i].blue=(double) ScaleCharToQuantum((unsigned char)
cristy3ed852e2009-09-05 21:47:34 +0000528 (cluster->blue.center+0.5));
529 i++;
530 }
531 /*
532 Do course grain classes.
533 */
cristy46ff2672012-12-14 15:32:26 +0000534 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000535#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000536 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000537 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000538#endif
cristybb503372010-05-27 20:51:26 +0000539 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000540 {
541 Cluster
542 *cluster;
543
cristy101ab702011-10-13 13:06:32 +0000544 register const PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000545 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000546
cristybb503372010-05-27 20:51:26 +0000547 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000548 x;
549
cristy4c08aed2011-07-01 19:47:50 +0000550 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000551 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000552
553 if (status == MagickFalse)
554 continue;
555 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000556 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
558 status=MagickFalse;
559 continue;
560 }
cristybb503372010-05-27 20:51:26 +0000561 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000562 {
cristy4c08aed2011-07-01 19:47:50 +0000563 SetPixelIndex(image,0,q);
cristy3ed852e2009-09-05 21:47:34 +0000564 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
565 {
cristy4c08aed2011-07-01 19:47:50 +0000566 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,q)) >=
cristy3ed852e2009-09-05 21:47:34 +0000567 (cluster->red.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000568 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,q)) <=
cristy3ed852e2009-09-05 21:47:34 +0000569 (cluster->red.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000570 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,q)) >=
cristy3ed852e2009-09-05 21:47:34 +0000571 (cluster->green.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000572 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,q)) <=
cristy3ed852e2009-09-05 21:47:34 +0000573 (cluster->green.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000574 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,q)) >=
cristy3ed852e2009-09-05 21:47:34 +0000575 (cluster->blue.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +0000576 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,q)) <=
cristy3ed852e2009-09-05 21:47:34 +0000577 (cluster->blue.right+SafeMargin)))
578 {
579 /*
580 Classify this pixel.
581 */
cristy4c08aed2011-07-01 19:47:50 +0000582 SetPixelIndex(image,(Quantum) cluster->id,q);
cristy3ed852e2009-09-05 21:47:34 +0000583 break;
584 }
585 }
586 if (cluster == (Cluster *) NULL)
587 {
cristya19f1d72012-08-07 18:24:38 +0000588 double
cristy3ed852e2009-09-05 21:47:34 +0000589 distance_squared,
590 local_minima,
591 numerator,
592 ratio,
593 sum;
594
cristybb503372010-05-27 20:51:26 +0000595 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000596 j,
597 k;
598
599 /*
600 Compute fuzzy membership.
601 */
602 local_minima=0.0;
cristybb503372010-05-27 20:51:26 +0000603 for (j=0; j < (ssize_t) image->colors; j++)
cristy3ed852e2009-09-05 21:47:34 +0000604 {
605 sum=0.0;
606 p=image->colormap+j;
cristy4c08aed2011-07-01 19:47:50 +0000607 distance_squared=squares[(ssize_t) ScaleQuantumToChar(
608 GetPixelRed(image,q))-(ssize_t)
cristye42f6582012-02-11 17:59:50 +0000609 ScaleQuantumToChar(ClampToQuantum(p->red))]+squares[(ssize_t)
cristy4c08aed2011-07-01 19:47:50 +0000610 ScaleQuantumToChar(GetPixelGreen(image,q))-(ssize_t)
cristye42f6582012-02-11 17:59:50 +0000611 ScaleQuantumToChar(ClampToQuantum(p->green))]+squares[(ssize_t)
cristy4c08aed2011-07-01 19:47:50 +0000612 ScaleQuantumToChar(GetPixelBlue(image,q))-(ssize_t)
cristye42f6582012-02-11 17:59:50 +0000613 ScaleQuantumToChar(ClampToQuantum(p->blue))];
cristy3ed852e2009-09-05 21:47:34 +0000614 numerator=distance_squared;
cristybb503372010-05-27 20:51:26 +0000615 for (k=0; k < (ssize_t) image->colors; k++)
cristy3ed852e2009-09-05 21:47:34 +0000616 {
617 p=image->colormap+k;
cristy4c08aed2011-07-01 19:47:50 +0000618 distance_squared=squares[(ssize_t) ScaleQuantumToChar(
619 GetPixelRed(image,q))-(ssize_t)
cristye42f6582012-02-11 17:59:50 +0000620 ScaleQuantumToChar(ClampToQuantum(p->red))]+squares[
621 (ssize_t) ScaleQuantumToChar(GetPixelGreen(image,q))-(ssize_t)
622 ScaleQuantumToChar(ClampToQuantum(p->green))]+squares[
623 (ssize_t) ScaleQuantumToChar(GetPixelBlue(image,q))-(ssize_t)
624 ScaleQuantumToChar(ClampToQuantum(p->blue))];
cristy3ed852e2009-09-05 21:47:34 +0000625 ratio=numerator/distance_squared;
626 sum+=SegmentPower(ratio);
627 }
628 if ((sum != 0.0) && ((1.0/sum) > local_minima))
629 {
630 /*
631 Classify this pixel.
632 */
cristyb519e202012-06-07 15:29:02 +0000633 local_minima=1.0/sum;
cristy4c08aed2011-07-01 19:47:50 +0000634 SetPixelIndex(image,(Quantum) j,q);
cristy3ed852e2009-09-05 21:47:34 +0000635 }
636 }
637 }
cristyed231572011-07-14 02:18:59 +0000638 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000639 }
640 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
641 status=MagickFalse;
642 if (image->progress_monitor != (MagickProgressMonitor) NULL)
643 {
644 MagickBooleanType
645 proceed;
646
cristyb5d5f722009-11-04 03:03:49 +0000647#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000648 #pragma omp critical (MagickCore_Classify)
649#endif
650 proceed=SetImageProgress(image,SegmentImageTag,progress++,
651 2*image->rows);
652 if (proceed == MagickFalse)
653 status=MagickFalse;
654 }
655 }
656 image_view=DestroyCacheView(image_view);
cristyea1a8aa2011-10-20 13:24:06 +0000657 status&=SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000658 /*
659 Relinquish resources.
660 */
661 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
662 {
663 next_cluster=cluster->next;
664 cluster=(Cluster *) RelinquishMagickMemory(cluster);
665 }
666 squares-=255;
667 free_squares=squares;
cristya19f1d72012-08-07 18:24:38 +0000668 free_squares=(double *) RelinquishMagickMemory(free_squares);
cristy3ed852e2009-09-05 21:47:34 +0000669 return(MagickTrue);
670}
671
672/*
673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
674% %
675% %
676% %
677+ C o n s o l i d a t e C r o s s i n g s %
678% %
679% %
680% %
681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682%
683% ConsolidateCrossings() guarantees that an even number of zero crossings
684% always lie between two crossings.
685%
686% The format of the ConsolidateCrossings method is:
687%
688% ConsolidateCrossings(ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +0000689% const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +0000690%
691% A description of each parameter follows.
692%
693% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
694%
cristybb503372010-05-27 20:51:26 +0000695% o number_crossings: This size_t specifies the number of elements
cristy3ed852e2009-09-05 21:47:34 +0000696% in the zero_crossing array.
697%
698*/
699
cristybb503372010-05-27 20:51:26 +0000700static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +0000701{
702 if (x < 0)
703 return(-x);
704 return(x);
705}
706
cristybb503372010-05-27 20:51:26 +0000707static inline ssize_t MagickMax(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
cristybb503372010-05-27 20:51:26 +0000714static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000715{
716 if (x < y)
717 return(x);
718 return(y);
719}
720
721static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +0000722 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +0000723{
cristy9d314ff2011-03-09 01:30:28 +0000724 register ssize_t
725 i,
726 j,
727 k,
728 l;
729
cristybb503372010-05-27 20:51:26 +0000730 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000731 center,
732 correct,
733 count,
734 left,
735 right;
736
cristy3ed852e2009-09-05 21:47:34 +0000737 /*
738 Consolidate zero crossings.
739 */
cristybb503372010-05-27 20:51:26 +0000740 for (i=(ssize_t) number_crossings-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000741 for (j=0; j <= 255; j++)
742 {
743 if (zero_crossing[i].crossings[j] == 0)
744 continue;
745 /*
746 Find the entry that is closest to j and still preserves the
747 property that there are an even number of crossings between
748 intervals.
749 */
750 for (k=j-1; k > 0; k--)
751 if (zero_crossing[i+1].crossings[k] != 0)
752 break;
753 left=MagickMax(k,0);
754 center=j;
755 for (k=j+1; k < 255; k++)
756 if (zero_crossing[i+1].crossings[k] != 0)
757 break;
758 right=MagickMin(k,255);
759 /*
760 K is the zero crossing just left of j.
761 */
762 for (k=j-1; k > 0; k--)
763 if (zero_crossing[i].crossings[k] != 0)
764 break;
765 if (k < 0)
766 k=0;
767 /*
768 Check center for an even number of crossings between k and j.
769 */
770 correct=(-1);
771 if (zero_crossing[i+1].crossings[j] != 0)
772 {
773 count=0;
774 for (l=k+1; l < center; l++)
775 if (zero_crossing[i+1].crossings[l] != 0)
776 count++;
777 if (((count % 2) == 0) && (center != k))
778 correct=center;
779 }
780 /*
781 Check left for an even number of crossings between k and j.
782 */
783 if (correct == -1)
784 {
785 count=0;
786 for (l=k+1; l < left; l++)
787 if (zero_crossing[i+1].crossings[l] != 0)
788 count++;
789 if (((count % 2) == 0) && (left != k))
790 correct=left;
791 }
792 /*
793 Check right for an even number of crossings between k and j.
794 */
795 if (correct == -1)
796 {
797 count=0;
798 for (l=k+1; l < right; l++)
799 if (zero_crossing[i+1].crossings[l] != 0)
800 count++;
801 if (((count % 2) == 0) && (right != k))
802 correct=right;
803 }
cristycee97112010-05-28 00:44:52 +0000804 l=(ssize_t) zero_crossing[i].crossings[j];
cristy3ed852e2009-09-05 21:47:34 +0000805 zero_crossing[i].crossings[j]=0;
806 if (correct != -1)
807 zero_crossing[i].crossings[correct]=(short) l;
808 }
809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
816+ D e f i n e R e g i o n %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% DefineRegion() defines the left and right boundaries of a peak region.
823%
824% The format of the DefineRegion method is:
825%
cristybb503372010-05-27 20:51:26 +0000826% ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000827%
828% A description of each parameter follows.
829%
830% o extrema: Specifies a pointer to an array of integers. They
831% represent the peaks and valleys of the histogram for each color
832% component.
833%
834% o extents: This pointer to an ExtentPacket represent the extends
835% of a particular peak or valley of a color component.
836%
837*/
cristybb503372010-05-27 20:51:26 +0000838static ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000839{
840 /*
841 Initialize to default values.
842 */
843 extents->left=0;
844 extents->center=0.0;
845 extents->right=255;
846 /*
847 Find the left side (maxima).
848 */
849 for ( ; extents->index <= 255; extents->index++)
850 if (extrema[extents->index] > 0)
851 break;
852 if (extents->index > 255)
853 return(MagickFalse); /* no left side - no region exists */
854 extents->left=extents->index;
855 /*
856 Find the right side (minima).
857 */
858 for ( ; extents->index <= 255; extents->index++)
859 if (extrema[extents->index] < 0)
860 break;
861 extents->right=extents->index-1;
862 return(MagickTrue);
863}
864
865/*
866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
867% %
868% %
869% %
870+ D e r i v a t i v e H i s t o g r a m %
871% %
872% %
873% %
874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875%
876% DerivativeHistogram() determines the derivative of the histogram using
877% central differencing.
878%
879% The format of the DerivativeHistogram method is:
880%
cristya19f1d72012-08-07 18:24:38 +0000881% DerivativeHistogram(const double *histogram,
882% double *derivative)
cristy3ed852e2009-09-05 21:47:34 +0000883%
884% A description of each parameter follows.
885%
cristya19f1d72012-08-07 18:24:38 +0000886% o histogram: Specifies an array of doubles representing the number
cristy3ed852e2009-09-05 21:47:34 +0000887% of pixels for each intensity of a particular color component.
888%
cristya19f1d72012-08-07 18:24:38 +0000889% o derivative: This array of doubles is initialized by
cristy3ed852e2009-09-05 21:47:34 +0000890% DerivativeHistogram to the derivative of the histogram using central
891% differencing.
892%
893*/
cristya19f1d72012-08-07 18:24:38 +0000894static void DerivativeHistogram(const double *histogram,
895 double *derivative)
cristy3ed852e2009-09-05 21:47:34 +0000896{
cristybb503372010-05-27 20:51:26 +0000897 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000898 i,
899 n;
900
901 /*
902 Compute endpoints using second order polynomial interpolation.
903 */
904 n=255;
905 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
906 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
907 /*
908 Compute derivative using central differencing.
909 */
910 for (i=1; i < n; i++)
911 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
912 return;
913}
914
915/*
916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
917% %
918% %
919% %
920+ G e t I m a g e D y n a m i c T h r e s h o l d %
921% %
922% %
923% %
924%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
925%
926% GetImageDynamicThreshold() returns the dynamic threshold for an image.
927%
928% The format of the GetImageDynamicThreshold method is:
929%
930% MagickBooleanType GetImageDynamicThreshold(const Image *image,
931% const double cluster_threshold,const double smooth_threshold,
cristy4c08aed2011-07-01 19:47:50 +0000932% PixelInfo *pixel,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000933%
934% A description of each parameter follows.
935%
936% o image: the image.
937%
cristya19f1d72012-08-07 18:24:38 +0000938% o cluster_threshold: This double represents the minimum number of
cristy3ed852e2009-09-05 21:47:34 +0000939% pixels contained in a hexahedra before it can be considered valid
940% (expressed as a percentage).
941%
942% o smooth_threshold: the smoothing threshold eliminates noise in the second
943% derivative of the histogram. As the value is increased, you can expect a
944% smoother second derivative.
945%
946% o pixel: return the dynamic threshold here.
947%
948% o exception: return any errors or warnings in this structure.
949%
950*/
951MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
952 const double cluster_threshold,const double smooth_threshold,
cristy4c08aed2011-07-01 19:47:50 +0000953 PixelInfo *pixel,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000954{
955 Cluster
956 *background,
957 *cluster,
958 *object,
959 *head,
960 *last_cluster,
961 *next_cluster;
962
963 ExtentPacket
964 blue,
965 green,
966 red;
967
cristy3ed852e2009-09-05 21:47:34 +0000968 MagickBooleanType
969 proceed;
970
cristya19f1d72012-08-07 18:24:38 +0000971 double
cristy3ed852e2009-09-05 21:47:34 +0000972 threshold;
973
cristy4c08aed2011-07-01 19:47:50 +0000974 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000975 *p;
976
cristybb503372010-05-27 20:51:26 +0000977 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000978 i,
979 x;
980
981 short
982 *extrema[MaxDimension];
983
cristy9d314ff2011-03-09 01:30:28 +0000984 ssize_t
985 count,
986 *histogram[MaxDimension],
987 y;
988
cristy3ed852e2009-09-05 21:47:34 +0000989 /*
990 Allocate histogram and extrema.
991 */
992 assert(image != (Image *) NULL);
993 assert(image->signature == MagickSignature);
994 if (image->debug != MagickFalse)
995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy4c08aed2011-07-01 19:47:50 +0000996 GetPixelInfo(image,pixel);
cristy3ed852e2009-09-05 21:47:34 +0000997 for (i=0; i < MaxDimension; i++)
998 {
cristybb503372010-05-27 20:51:26 +0000999 histogram[i]=(ssize_t *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +00001000 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristybb503372010-05-27 20:51:26 +00001001 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001002 {
1003 for (i-- ; i >= 0; i--)
1004 {
1005 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001006 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001007 }
1008 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001009 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001010 return(MagickFalse);
1011 }
1012 }
1013 /*
1014 Initialize histogram.
1015 */
1016 InitializeHistogram(image,histogram,exception);
1017 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
1018 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
1019 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
1020 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
1021 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
1022 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
1023 /*
1024 Form clusters.
1025 */
1026 cluster=(Cluster *) NULL;
1027 head=(Cluster *) NULL;
1028 (void) ResetMagickMemory(&red,0,sizeof(red));
1029 (void) ResetMagickMemory(&green,0,sizeof(green));
1030 (void) ResetMagickMemory(&blue,0,sizeof(blue));
1031 while (DefineRegion(extrema[Red],&red) != 0)
1032 {
1033 green.index=0;
1034 while (DefineRegion(extrema[Green],&green) != 0)
1035 {
1036 blue.index=0;
1037 while (DefineRegion(extrema[Blue],&blue) != 0)
1038 {
1039 /*
1040 Allocate a new class.
1041 */
1042 if (head != (Cluster *) NULL)
1043 {
1044 cluster->next=(Cluster *) AcquireMagickMemory(
1045 sizeof(*cluster->next));
1046 cluster=cluster->next;
1047 }
1048 else
1049 {
cristy73bd4a52010-10-05 11:24:23 +00001050 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001051 head=cluster;
1052 }
1053 if (cluster == (Cluster *) NULL)
1054 {
1055 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001056 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001057 image->filename);
1058 return(MagickFalse);
1059 }
1060 /*
1061 Initialize a new class.
1062 */
1063 cluster->count=0;
1064 cluster->red=red;
1065 cluster->green=green;
1066 cluster->blue=blue;
1067 cluster->next=(Cluster *) NULL;
1068 }
1069 }
1070 }
1071 if (head == (Cluster *) NULL)
1072 {
1073 /*
1074 No classes were identified-- create one.
1075 */
cristy73bd4a52010-10-05 11:24:23 +00001076 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001077 if (cluster == (Cluster *) NULL)
1078 {
1079 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001080 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001081 return(MagickFalse);
1082 }
1083 /*
1084 Initialize a new class.
1085 */
1086 cluster->count=0;
1087 cluster->red=red;
1088 cluster->green=green;
1089 cluster->blue=blue;
1090 cluster->next=(Cluster *) NULL;
1091 head=cluster;
1092 }
1093 /*
1094 Count the pixels for each cluster.
1095 */
1096 count=0;
cristybb503372010-05-27 20:51:26 +00001097 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001098 {
1099 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001100 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001101 break;
cristybb503372010-05-27 20:51:26 +00001102 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001103 {
1104 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristy4c08aed2011-07-01 19:47:50 +00001105 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001106 (cluster->red.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001107 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001108 (cluster->red.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001109 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001110 (cluster->green.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001111 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001112 (cluster->green.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001113 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001114 (cluster->blue.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001115 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001116 (cluster->blue.right+SafeMargin)))
1117 {
1118 /*
1119 Count this pixel.
1120 */
1121 count++;
cristya19f1d72012-08-07 18:24:38 +00001122 cluster->red.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001123 GetPixelRed(image,p));
cristya19f1d72012-08-07 18:24:38 +00001124 cluster->green.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001125 GetPixelGreen(image,p));
cristya19f1d72012-08-07 18:24:38 +00001126 cluster->blue.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001127 GetPixelBlue(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001128 cluster->count++;
1129 break;
1130 }
cristyed231572011-07-14 02:18:59 +00001131 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001132 }
cristycee97112010-05-28 00:44:52 +00001133 proceed=SetImageProgress(image,SegmentImageTag,(MagickOffsetType) y,
1134 2*image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001135 if (proceed == MagickFalse)
1136 break;
1137 }
1138 /*
1139 Remove clusters that do not meet minimum cluster threshold.
1140 */
1141 count=0;
1142 last_cluster=head;
1143 next_cluster=head;
1144 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1145 {
1146 next_cluster=cluster->next;
1147 if ((cluster->count > 0) &&
1148 (cluster->count >= (count*cluster_threshold/100.0)))
1149 {
1150 /*
1151 Initialize cluster.
1152 */
1153 cluster->id=count;
1154 cluster->red.center/=cluster->count;
1155 cluster->green.center/=cluster->count;
1156 cluster->blue.center/=cluster->count;
1157 count++;
1158 last_cluster=cluster;
1159 continue;
1160 }
1161 /*
1162 Delete cluster.
1163 */
1164 if (cluster == head)
1165 head=next_cluster;
1166 else
1167 last_cluster->next=next_cluster;
1168 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1169 }
1170 object=head;
1171 background=head;
1172 if (count > 1)
1173 {
1174 object=head->next;
1175 for (cluster=object; cluster->next != (Cluster *) NULL; )
1176 {
1177 if (cluster->count < object->count)
1178 object=cluster;
1179 cluster=cluster->next;
1180 }
1181 background=head->next;
1182 for (cluster=background; cluster->next != (Cluster *) NULL; )
1183 {
1184 if (cluster->count > background->count)
1185 background=cluster;
1186 cluster=cluster->next;
1187 }
1188 }
cristy375bd902014-01-15 00:54:40 +00001189 if (background != (Cluster *) NULL)
1190 {
1191 threshold=(background->red.center+object->red.center)/2.0;
1192 pixel->red=(double) ScaleCharToQuantum((unsigned char)
1193 (threshold+0.5));
1194 threshold=(background->green.center+object->green.center)/2.0;
1195 pixel->green=(double) ScaleCharToQuantum((unsigned char)
1196 (threshold+0.5));
1197 threshold=(background->blue.center+object->blue.center)/2.0;
1198 pixel->blue=(double) ScaleCharToQuantum((unsigned char)
1199 (threshold+0.5));
1200 }
cristy3ed852e2009-09-05 21:47:34 +00001201 /*
1202 Relinquish resources.
1203 */
1204 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1205 {
1206 next_cluster=cluster->next;
1207 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1208 }
1209 for (i=0; i < MaxDimension; i++)
1210 {
1211 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001212 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001213 }
1214 return(MagickTrue);
1215}
1216
1217/*
1218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219% %
1220% %
1221% %
1222+ I n i t i a l i z e H i s t o g r a m %
1223% %
1224% %
1225% %
1226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1227%
1228% InitializeHistogram() computes the histogram for an image.
1229%
1230% The format of the InitializeHistogram method is:
1231%
cristybb503372010-05-27 20:51:26 +00001232% InitializeHistogram(const Image *image,ssize_t **histogram)
cristy3ed852e2009-09-05 21:47:34 +00001233%
1234% A description of each parameter follows.
1235%
1236% o image: Specifies a pointer to an Image structure; returned from
1237% ReadImage.
1238%
1239% o histogram: Specifies an array of integers representing the number
1240% of pixels for each intensity of a particular color component.
1241%
1242*/
cristybb503372010-05-27 20:51:26 +00001243static void InitializeHistogram(const Image *image,ssize_t **histogram,
cristy3ed852e2009-09-05 21:47:34 +00001244 ExceptionInfo *exception)
1245{
cristy4c08aed2011-07-01 19:47:50 +00001246 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001247 *p;
1248
cristybb503372010-05-27 20:51:26 +00001249 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001250 i,
1251 x;
1252
cristy9d314ff2011-03-09 01:30:28 +00001253 ssize_t
1254 y;
1255
cristy3ed852e2009-09-05 21:47:34 +00001256 /*
1257 Initialize histogram.
1258 */
1259 for (i=0; i <= 255; i++)
1260 {
1261 histogram[Red][i]=0;
1262 histogram[Green][i]=0;
1263 histogram[Blue][i]=0;
1264 }
cristybb503372010-05-27 20:51:26 +00001265 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001266 {
1267 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001268 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001269 break;
cristybb503372010-05-27 20:51:26 +00001270 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001271 {
cristy4c08aed2011-07-01 19:47:50 +00001272 histogram[Red][(ssize_t) ScaleQuantumToChar(GetPixelRed(image,p))]++;
1273 histogram[Green][(ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p))]++;
1274 histogram[Blue][(ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p))]++;
cristyed231572011-07-14 02:18:59 +00001275 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001276 }
1277 }
1278}
1279
1280/*
1281%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1282% %
1283% %
1284% %
1285+ I n i t i a l i z e I n t e r v a l T r e e %
1286% %
1287% %
1288% %
1289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290%
1291% InitializeIntervalTree() initializes an interval tree from the lists of
1292% zero crossings.
1293%
1294% The format of the InitializeIntervalTree method is:
1295%
cristybb503372010-05-27 20:51:26 +00001296% InitializeIntervalTree(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001297% IntervalTree *node)
1298%
1299% A description of each parameter follows.
1300%
1301% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
1302%
cristybb503372010-05-27 20:51:26 +00001303% o number_crossings: This size_t specifies the number of elements
cristy3ed852e2009-09-05 21:47:34 +00001304% in the zero_crossing array.
1305%
1306*/
1307
cristybb503372010-05-27 20:51:26 +00001308static void InitializeList(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001309 IntervalTree *node)
1310{
1311 if (node == (IntervalTree *) NULL)
1312 return;
1313 if (node->child == (IntervalTree *) NULL)
1314 list[(*number_nodes)++]=node;
1315 InitializeList(list,number_nodes,node->sibling);
1316 InitializeList(list,number_nodes,node->child);
1317}
1318
1319static void MeanStability(IntervalTree *node)
1320{
1321 register IntervalTree
1322 *child;
1323
1324 if (node == (IntervalTree *) NULL)
1325 return;
1326 node->mean_stability=0.0;
1327 child=node->child;
1328 if (child != (IntervalTree *) NULL)
1329 {
cristybb503372010-05-27 20:51:26 +00001330 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001331 count;
1332
cristya19f1d72012-08-07 18:24:38 +00001333 register double
cristy3ed852e2009-09-05 21:47:34 +00001334 sum;
1335
1336 sum=0.0;
1337 count=0;
1338 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
1339 {
1340 sum+=child->stability;
1341 count++;
1342 }
cristya19f1d72012-08-07 18:24:38 +00001343 node->mean_stability=sum/(double) count;
cristy3ed852e2009-09-05 21:47:34 +00001344 }
1345 MeanStability(node->sibling);
1346 MeanStability(node->child);
1347}
1348
1349static void Stability(IntervalTree *node)
1350{
1351 if (node == (IntervalTree *) NULL)
1352 return;
1353 if (node->child == (IntervalTree *) NULL)
1354 node->stability=0.0;
1355 else
1356 node->stability=node->tau-(node->child)->tau;
1357 Stability(node->sibling);
1358 Stability(node->child);
1359}
1360
1361static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +00001362 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +00001363{
1364 IntervalTree
1365 *head,
1366 **list,
1367 *node,
1368 *root;
1369
cristy9d314ff2011-03-09 01:30:28 +00001370 register ssize_t
1371 i;
1372
cristybb503372010-05-27 20:51:26 +00001373 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001374 j,
1375 k,
1376 left,
1377 number_nodes;
1378
cristy3ed852e2009-09-05 21:47:34 +00001379 /*
1380 Allocate interval tree.
1381 */
1382 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1383 sizeof(*list));
1384 if (list == (IntervalTree **) NULL)
1385 return((IntervalTree *) NULL);
1386 /*
1387 The root is the entire histogram.
1388 */
cristy73bd4a52010-10-05 11:24:23 +00001389 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
cristy3ed852e2009-09-05 21:47:34 +00001390 root->child=(IntervalTree *) NULL;
1391 root->sibling=(IntervalTree *) NULL;
1392 root->tau=0.0;
1393 root->left=0;
1394 root->right=255;
cristybb503372010-05-27 20:51:26 +00001395 for (i=(-1); i < (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001396 {
1397 /*
1398 Initialize list with all nodes with no children.
1399 */
1400 number_nodes=0;
1401 InitializeList(list,&number_nodes,root);
1402 /*
1403 Split list.
1404 */
1405 for (j=0; j < number_nodes; j++)
1406 {
1407 head=list[j];
1408 left=head->left;
1409 node=head;
1410 for (k=head->left+1; k < head->right; k++)
1411 {
1412 if (zero_crossing[i+1].crossings[k] != 0)
1413 {
1414 if (node == head)
1415 {
1416 node->child=(IntervalTree *) AcquireMagickMemory(
1417 sizeof(*node->child));
1418 node=node->child;
1419 }
1420 else
1421 {
1422 node->sibling=(IntervalTree *) AcquireMagickMemory(
1423 sizeof(*node->sibling));
1424 node=node->sibling;
1425 }
1426 node->tau=zero_crossing[i+1].tau;
1427 node->child=(IntervalTree *) NULL;
1428 node->sibling=(IntervalTree *) NULL;
1429 node->left=left;
1430 node->right=k;
1431 left=k;
1432 }
1433 }
1434 if (left != head->left)
1435 {
1436 node->sibling=(IntervalTree *) AcquireMagickMemory(
1437 sizeof(*node->sibling));
1438 node=node->sibling;
1439 node->tau=zero_crossing[i+1].tau;
1440 node->child=(IntervalTree *) NULL;
1441 node->sibling=(IntervalTree *) NULL;
1442 node->left=left;
1443 node->right=head->right;
1444 }
1445 }
1446 }
1447 /*
1448 Determine the stability: difference between a nodes tau and its child.
1449 */
1450 Stability(root->child);
1451 MeanStability(root->child);
1452 list=(IntervalTree **) RelinquishMagickMemory(list);
1453 return(root);
1454}
1455
1456/*
1457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458% %
1459% %
1460% %
1461+ O p t i m a l T a u %
1462% %
1463% %
1464% %
1465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466%
1467% OptimalTau() finds the optimal tau for each band of the histogram.
1468%
1469% The format of the OptimalTau method is:
1470%
cristya19f1d72012-08-07 18:24:38 +00001471% double OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001472% const double min_tau,const double delta_tau,
1473% const double smooth_threshold,short *extrema)
1474%
1475% A description of each parameter follows.
1476%
1477% o histogram: Specifies an array of integers representing the number
1478% of pixels for each intensity of a particular color component.
1479%
1480% o extrema: Specifies a pointer to an array of integers. They
1481% represent the peaks and valleys of the histogram for each color
1482% component.
1483%
1484*/
1485
cristybb503372010-05-27 20:51:26 +00001486static void ActiveNodes(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001487 IntervalTree *node)
1488{
1489 if (node == (IntervalTree *) NULL)
1490 return;
1491 if (node->stability >= node->mean_stability)
1492 {
1493 list[(*number_nodes)++]=node;
1494 ActiveNodes(list,number_nodes,node->sibling);
1495 }
1496 else
1497 {
1498 ActiveNodes(list,number_nodes,node->sibling);
1499 ActiveNodes(list,number_nodes,node->child);
1500 }
1501}
1502
1503static void FreeNodes(IntervalTree *node)
1504{
1505 if (node == (IntervalTree *) NULL)
1506 return;
1507 FreeNodes(node->sibling);
1508 FreeNodes(node->child);
1509 node=(IntervalTree *) RelinquishMagickMemory(node);
1510}
1511
cristya19f1d72012-08-07 18:24:38 +00001512static double OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001513 const double min_tau,const double delta_tau,const double smooth_threshold,
1514 short *extrema)
1515{
1516 IntervalTree
1517 **list,
1518 *node,
1519 *root;
1520
cristy9d314ff2011-03-09 01:30:28 +00001521 MagickBooleanType
1522 peak;
cristy3ed852e2009-09-05 21:47:34 +00001523
cristya19f1d72012-08-07 18:24:38 +00001524 double
cristy3ed852e2009-09-05 21:47:34 +00001525 average_tau,
1526 *derivative,
1527 *second_derivative,
1528 tau,
1529 value;
1530
cristybb503372010-05-27 20:51:26 +00001531 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001532 i,
1533 x;
1534
cristybb503372010-05-27 20:51:26 +00001535 size_t
cristy3ed852e2009-09-05 21:47:34 +00001536 count,
1537 number_crossings;
1538
cristy9d314ff2011-03-09 01:30:28 +00001539 ssize_t
1540 index,
1541 j,
1542 k,
1543 number_nodes;
1544
cristy3ed852e2009-09-05 21:47:34 +00001545 ZeroCrossing
1546 *zero_crossing;
1547
1548 /*
1549 Allocate interval tree.
1550 */
1551 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1552 sizeof(*list));
1553 if (list == (IntervalTree **) NULL)
1554 return(0.0);
1555 /*
1556 Allocate zero crossing list.
1557 */
cristybb503372010-05-27 20:51:26 +00001558 count=(size_t) ((max_tau-min_tau)/delta_tau)+2;
cristy3ed852e2009-09-05 21:47:34 +00001559 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
1560 sizeof(*zero_crossing));
1561 if (zero_crossing == (ZeroCrossing *) NULL)
1562 return(0.0);
cristybb503372010-05-27 20:51:26 +00001563 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00001564 zero_crossing[i].tau=(-1.0);
1565 /*
1566 Initialize zero crossing list.
1567 */
cristya19f1d72012-08-07 18:24:38 +00001568 derivative=(double *) AcquireQuantumMemory(256,sizeof(*derivative));
1569 second_derivative=(double *) AcquireQuantumMemory(256,
cristy3ed852e2009-09-05 21:47:34 +00001570 sizeof(*second_derivative));
cristya19f1d72012-08-07 18:24:38 +00001571 if ((derivative == (double *) NULL) ||
1572 (second_derivative == (double *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001573 ThrowFatalException(ResourceLimitFatalError,
1574 "UnableToAllocateDerivatives");
1575 i=0;
1576 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
1577 {
1578 zero_crossing[i].tau=tau;
1579 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
1580 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1581 DerivativeHistogram(derivative,second_derivative);
1582 ZeroCrossHistogram(second_derivative,smooth_threshold,
1583 zero_crossing[i].crossings);
1584 i++;
1585 }
1586 /*
1587 Add an entry for the original histogram.
1588 */
1589 zero_crossing[i].tau=0.0;
1590 for (j=0; j <= 255; j++)
cristya19f1d72012-08-07 18:24:38 +00001591 zero_crossing[i].histogram[j]=(double) histogram[j];
cristy3ed852e2009-09-05 21:47:34 +00001592 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1593 DerivativeHistogram(derivative,second_derivative);
1594 ZeroCrossHistogram(second_derivative,smooth_threshold,
1595 zero_crossing[i].crossings);
cristybb503372010-05-27 20:51:26 +00001596 number_crossings=(size_t) i;
cristya19f1d72012-08-07 18:24:38 +00001597 derivative=(double *) RelinquishMagickMemory(derivative);
1598 second_derivative=(double *)
cristy3ed852e2009-09-05 21:47:34 +00001599 RelinquishMagickMemory(second_derivative);
1600 /*
1601 Ensure the scale-space fingerprints form lines in scale-space, not loops.
1602 */
1603 ConsolidateCrossings(zero_crossing,number_crossings);
1604 /*
1605 Force endpoints to be included in the interval.
1606 */
cristybb503372010-05-27 20:51:26 +00001607 for (i=0; i <= (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001608 {
1609 for (j=0; j < 255; j++)
1610 if (zero_crossing[i].crossings[j] != 0)
1611 break;
1612 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
1613 for (j=255; j > 0; j--)
1614 if (zero_crossing[i].crossings[j] != 0)
1615 break;
1616 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
1617 }
1618 /*
1619 Initialize interval tree.
1620 */
1621 root=InitializeIntervalTree(zero_crossing,number_crossings);
1622 if (root == (IntervalTree *) NULL)
1623 return(0.0);
1624 /*
1625 Find active nodes: stability is greater (or equal) to the mean stability of
1626 its children.
1627 */
1628 number_nodes=0;
1629 ActiveNodes(list,&number_nodes,root->child);
1630 /*
1631 Initialize extrema.
1632 */
1633 for (i=0; i <= 255; i++)
1634 extrema[i]=0;
1635 for (i=0; i < number_nodes; i++)
1636 {
1637 /*
1638 Find this tau in zero crossings list.
1639 */
1640 k=0;
1641 node=list[i];
cristybb503372010-05-27 20:51:26 +00001642 for (j=0; j <= (ssize_t) number_crossings; j++)
cristy3ed852e2009-09-05 21:47:34 +00001643 if (zero_crossing[j].tau == node->tau)
1644 k=j;
1645 /*
1646 Find the value of the peak.
1647 */
1648 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
1649 MagickFalse;
1650 index=node->left;
1651 value=zero_crossing[k].histogram[index];
1652 for (x=node->left; x <= node->right; x++)
1653 {
1654 if (peak != MagickFalse)
1655 {
1656 if (zero_crossing[k].histogram[x] > value)
1657 {
1658 value=zero_crossing[k].histogram[x];
1659 index=x;
1660 }
1661 }
1662 else
1663 if (zero_crossing[k].histogram[x] < value)
1664 {
1665 value=zero_crossing[k].histogram[x];
1666 index=x;
1667 }
1668 }
1669 for (x=node->left; x <= node->right; x++)
1670 {
1671 if (index == 0)
1672 index=256;
1673 if (peak != MagickFalse)
1674 extrema[x]=(short) index;
1675 else
1676 extrema[x]=(short) (-index);
1677 }
1678 }
1679 /*
1680 Determine the average tau.
1681 */
1682 average_tau=0.0;
1683 for (i=0; i < number_nodes; i++)
1684 average_tau+=list[i]->tau;
cristya19f1d72012-08-07 18:24:38 +00001685 average_tau/=(double) number_nodes;
cristy3ed852e2009-09-05 21:47:34 +00001686 /*
1687 Relinquish resources.
1688 */
1689 FreeNodes(root);
1690 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
1691 list=(IntervalTree **) RelinquishMagickMemory(list);
1692 return(average_tau);
1693}
1694
1695/*
1696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1697% %
1698% %
1699% %
1700+ S c a l e S p a c e %
1701% %
1702% %
1703% %
1704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1705%
1706% ScaleSpace() performs a scale-space filter on the 1D histogram.
1707%
1708% The format of the ScaleSpace method is:
1709%
cristya19f1d72012-08-07 18:24:38 +00001710% ScaleSpace(const ssize_t *histogram,const double tau,
1711% double *scale_histogram)
cristy3ed852e2009-09-05 21:47:34 +00001712%
1713% A description of each parameter follows.
1714%
cristya19f1d72012-08-07 18:24:38 +00001715% o histogram: Specifies an array of doubles representing the number
cristy3ed852e2009-09-05 21:47:34 +00001716% of pixels for each intensity of a particular color component.
1717%
1718*/
1719
cristya19f1d72012-08-07 18:24:38 +00001720static void ScaleSpace(const ssize_t *histogram,const double tau,
1721 double *scale_histogram)
cristy3ed852e2009-09-05 21:47:34 +00001722{
cristya19f1d72012-08-07 18:24:38 +00001723 double
cristy3ed852e2009-09-05 21:47:34 +00001724 alpha,
1725 beta,
1726 *gamma,
1727 sum;
1728
cristybb503372010-05-27 20:51:26 +00001729 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001730 u,
1731 x;
1732
cristya19f1d72012-08-07 18:24:38 +00001733 gamma=(double *) AcquireQuantumMemory(256,sizeof(*gamma));
1734 if (gamma == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001735 ThrowFatalException(ResourceLimitFatalError,
1736 "UnableToAllocateGammaMap");
cristy3e3ec3a2012-11-03 23:11:06 +00001737 alpha=PerceptibleReciprocal(tau*sqrt(2.0*MagickPI));
1738 beta=(-1.0*PerceptibleReciprocal(2.0*tau*tau));
cristy3ed852e2009-09-05 21:47:34 +00001739 for (x=0; x <= 255; x++)
1740 gamma[x]=0.0;
1741 for (x=0; x <= 255; x++)
1742 {
1743 gamma[x]=exp((double) beta*x*x);
1744 if (gamma[x] < MagickEpsilon)
1745 break;
1746 }
1747 for (x=0; x <= 255; x++)
1748 {
1749 sum=0.0;
1750 for (u=0; u <= 255; u++)
cristya19f1d72012-08-07 18:24:38 +00001751 sum+=(double) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
cristy3ed852e2009-09-05 21:47:34 +00001752 scale_histogram[x]=alpha*sum;
1753 }
cristya19f1d72012-08-07 18:24:38 +00001754 gamma=(double *) RelinquishMagickMemory(gamma);
cristy3ed852e2009-09-05 21:47:34 +00001755}
1756
1757/*
1758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1759% %
1760% %
1761% %
1762% S e g m e n t I m a g e %
1763% %
1764% %
1765% %
1766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1767%
1768% SegmentImage() segment an image by analyzing the histograms of the color
1769% components and identifying units that are homogeneous with the fuzzy
1770% C-means technique.
1771%
1772% The format of the SegmentImage method is:
1773%
1774% MagickBooleanType SegmentImage(Image *image,
1775% const ColorspaceType colorspace,const MagickBooleanType verbose,
cristy018f07f2011-09-04 21:15:19 +00001776% const double cluster_threshold,const double smooth_threshold,
1777% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001778%
1779% A description of each parameter follows.
1780%
1781% o image: the image.
1782%
1783% o colorspace: Indicate the colorspace.
1784%
1785% o verbose: Set to MagickTrue to print detailed information about the
1786% identified classes.
1787%
1788% o cluster_threshold: This represents the minimum number of pixels
1789% contained in a hexahedra before it can be considered valid (expressed
1790% as a percentage).
1791%
1792% o smooth_threshold: the smoothing threshold eliminates noise in the second
1793% derivative of the histogram. As the value is increased, you can expect a
1794% smoother second derivative.
1795%
cristy018f07f2011-09-04 21:15:19 +00001796% o exception: return any errors or warnings in this structure.
1797%
cristy3ed852e2009-09-05 21:47:34 +00001798*/
1799MagickExport MagickBooleanType SegmentImage(Image *image,
1800 const ColorspaceType colorspace,const MagickBooleanType verbose,
cristy018f07f2011-09-04 21:15:19 +00001801 const double cluster_threshold,const double smooth_threshold,
1802 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001803{
cristy3d9f5ba2012-06-26 13:37:31 +00001804 ColorspaceType
1805 previous_colorspace;
1806
cristy3ed852e2009-09-05 21:47:34 +00001807 MagickBooleanType
1808 status;
1809
cristybb503372010-05-27 20:51:26 +00001810 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001811 i;
1812
1813 short
1814 *extrema[MaxDimension];
1815
cristy9d314ff2011-03-09 01:30:28 +00001816 ssize_t
1817 *histogram[MaxDimension];
1818
cristy3ed852e2009-09-05 21:47:34 +00001819 /*
1820 Allocate histogram and extrema.
1821 */
1822 assert(image != (Image *) NULL);
1823 assert(image->signature == MagickSignature);
1824 if (image->debug != MagickFalse)
1825 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1826 for (i=0; i < MaxDimension; i++)
1827 {
cristybb503372010-05-27 20:51:26 +00001828 histogram[i]=(ssize_t *) AcquireQuantumMemory(256,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +00001829 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
cristybb503372010-05-27 20:51:26 +00001830 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001831 {
1832 for (i-- ; i >= 0; i--)
1833 {
1834 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001835 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001836 }
1837 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1838 image->filename)
1839 }
1840 }
cristy3ed852e2009-09-05 21:47:34 +00001841 /*
1842 Initialize histogram.
1843 */
cristy3d9f5ba2012-06-26 13:37:31 +00001844 previous_colorspace=image->colorspace;
1845 (void) TransformImageColorspace(image,colorspace,exception);
cristyc82a27b2011-10-21 01:07:16 +00001846 InitializeHistogram(image,histogram,exception);
cristy3ed852e2009-09-05 21:47:34 +00001847 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
1848 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
1849 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
1850 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
1851 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
1852 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
1853 /*
1854 Classify using the fuzzy c-Means technique.
1855 */
cristy018f07f2011-09-04 21:15:19 +00001856 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose,
1857 exception);
cristy3d9f5ba2012-06-26 13:37:31 +00001858 (void) TransformImageColorspace(image,previous_colorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001859 /*
1860 Relinquish resources.
1861 */
1862 for (i=0; i < MaxDimension; i++)
1863 {
1864 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001865 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001866 }
1867 return(status);
1868}
1869
1870/*
1871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1872% %
1873% %
1874% %
1875+ Z e r o C r o s s H i s t o g r a m %
1876% %
1877% %
1878% %
1879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1880%
1881% ZeroCrossHistogram() find the zero crossings in a histogram and marks
1882% directions as: 1 is negative to positive; 0 is zero crossing; and -1
1883% is positive to negative.
1884%
1885% The format of the ZeroCrossHistogram method is:
1886%
cristya19f1d72012-08-07 18:24:38 +00001887% ZeroCrossHistogram(double *second_derivative,
1888% const double smooth_threshold,short *crossings)
cristy3ed852e2009-09-05 21:47:34 +00001889%
1890% A description of each parameter follows.
1891%
cristya19f1d72012-08-07 18:24:38 +00001892% o second_derivative: Specifies an array of doubles representing the
cristy3ed852e2009-09-05 21:47:34 +00001893% second derivative of the histogram of a particular color component.
1894%
1895% o crossings: This array of integers is initialized with
1896% -1, 0, or 1 representing the slope of the first derivative of the
1897% of a particular color component.
1898%
1899*/
cristya19f1d72012-08-07 18:24:38 +00001900static void ZeroCrossHistogram(double *second_derivative,
1901 const double smooth_threshold,short *crossings)
cristy3ed852e2009-09-05 21:47:34 +00001902{
cristybb503372010-05-27 20:51:26 +00001903 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001904 i;
1905
cristy9d314ff2011-03-09 01:30:28 +00001906 ssize_t
1907 parity;
1908
cristy3ed852e2009-09-05 21:47:34 +00001909 /*
1910 Merge low numbers to zero to help prevent noise.
1911 */
1912 for (i=0; i <= 255; i++)
1913 if ((second_derivative[i] < smooth_threshold) &&
1914 (second_derivative[i] >= -smooth_threshold))
1915 second_derivative[i]=0.0;
1916 /*
1917 Mark zero crossings.
1918 */
1919 parity=0;
1920 for (i=0; i <= 255; i++)
1921 {
1922 crossings[i]=0;
1923 if (second_derivative[i] < 0.0)
1924 {
1925 if (parity > 0)
1926 crossings[i]=(-1);
1927 parity=1;
1928 }
1929 else
1930 if (second_derivative[i] > 0.0)
1931 {
1932 if (parity < 0)
1933 crossings[i]=1;
1934 parity=(-1);
1935 }
1936 }
1937}