blob: 27e6b9fbe74c9655a389637e84dca7fedbadcc21 [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% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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
dirk05d2ff72015-11-18 23:13:43 +0100545 *magick_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
dirk05d2ff72015-11-18 23:13:43 +0100551 *magick_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*/
cristy3ed852e2009-09-05 21:47:34 +0000699static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +0000700 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +0000701{
cristy9d314ff2011-03-09 01:30:28 +0000702 register ssize_t
703 i,
704 j,
705 k,
706 l;
707
cristybb503372010-05-27 20:51:26 +0000708 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000709 center,
710 correct,
711 count,
712 left,
713 right;
714
cristy3ed852e2009-09-05 21:47:34 +0000715 /*
716 Consolidate zero crossings.
717 */
cristybb503372010-05-27 20:51:26 +0000718 for (i=(ssize_t) number_crossings-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000719 for (j=0; j <= 255; j++)
720 {
721 if (zero_crossing[i].crossings[j] == 0)
722 continue;
723 /*
724 Find the entry that is closest to j and still preserves the
725 property that there are an even number of crossings between
726 intervals.
727 */
728 for (k=j-1; k > 0; k--)
729 if (zero_crossing[i+1].crossings[k] != 0)
730 break;
731 left=MagickMax(k,0);
732 center=j;
733 for (k=j+1; k < 255; k++)
734 if (zero_crossing[i+1].crossings[k] != 0)
735 break;
736 right=MagickMin(k,255);
737 /*
738 K is the zero crossing just left of j.
739 */
740 for (k=j-1; k > 0; k--)
741 if (zero_crossing[i].crossings[k] != 0)
742 break;
743 if (k < 0)
744 k=0;
745 /*
746 Check center for an even number of crossings between k and j.
747 */
748 correct=(-1);
749 if (zero_crossing[i+1].crossings[j] != 0)
750 {
751 count=0;
752 for (l=k+1; l < center; l++)
753 if (zero_crossing[i+1].crossings[l] != 0)
754 count++;
755 if (((count % 2) == 0) && (center != k))
756 correct=center;
757 }
758 /*
759 Check left for an even number of crossings between k and j.
760 */
761 if (correct == -1)
762 {
763 count=0;
764 for (l=k+1; l < left; l++)
765 if (zero_crossing[i+1].crossings[l] != 0)
766 count++;
767 if (((count % 2) == 0) && (left != k))
768 correct=left;
769 }
770 /*
771 Check right for an even number of crossings between k and j.
772 */
773 if (correct == -1)
774 {
775 count=0;
776 for (l=k+1; l < right; l++)
777 if (zero_crossing[i+1].crossings[l] != 0)
778 count++;
779 if (((count % 2) == 0) && (right != k))
780 correct=right;
781 }
cristycee97112010-05-28 00:44:52 +0000782 l=(ssize_t) zero_crossing[i].crossings[j];
cristy3ed852e2009-09-05 21:47:34 +0000783 zero_crossing[i].crossings[j]=0;
784 if (correct != -1)
785 zero_crossing[i].crossings[correct]=(short) l;
786 }
787}
788
789/*
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791% %
792% %
793% %
794+ D e f i n e R e g i o n %
795% %
796% %
797% %
798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799%
800% DefineRegion() defines the left and right boundaries of a peak region.
801%
802% The format of the DefineRegion method is:
803%
cristybb503372010-05-27 20:51:26 +0000804% ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000805%
806% A description of each parameter follows.
807%
808% o extrema: Specifies a pointer to an array of integers. They
809% represent the peaks and valleys of the histogram for each color
810% component.
811%
812% o extents: This pointer to an ExtentPacket represent the extends
813% of a particular peak or valley of a color component.
814%
815*/
cristybb503372010-05-27 20:51:26 +0000816static ssize_t DefineRegion(const short *extrema,ExtentPacket *extents)
cristy3ed852e2009-09-05 21:47:34 +0000817{
818 /*
819 Initialize to default values.
820 */
821 extents->left=0;
822 extents->center=0.0;
823 extents->right=255;
824 /*
825 Find the left side (maxima).
826 */
827 for ( ; extents->index <= 255; extents->index++)
828 if (extrema[extents->index] > 0)
829 break;
830 if (extents->index > 255)
831 return(MagickFalse); /* no left side - no region exists */
832 extents->left=extents->index;
833 /*
834 Find the right side (minima).
835 */
836 for ( ; extents->index <= 255; extents->index++)
837 if (extrema[extents->index] < 0)
838 break;
839 extents->right=extents->index-1;
840 return(MagickTrue);
841}
842
843/*
844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845% %
846% %
847% %
848+ D e r i v a t i v e H i s t o g r a m %
849% %
850% %
851% %
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853%
854% DerivativeHistogram() determines the derivative of the histogram using
855% central differencing.
856%
857% The format of the DerivativeHistogram method is:
858%
cristya19f1d72012-08-07 18:24:38 +0000859% DerivativeHistogram(const double *histogram,
860% double *derivative)
cristy3ed852e2009-09-05 21:47:34 +0000861%
862% A description of each parameter follows.
863%
cristya19f1d72012-08-07 18:24:38 +0000864% o histogram: Specifies an array of doubles representing the number
cristy3ed852e2009-09-05 21:47:34 +0000865% of pixels for each intensity of a particular color component.
866%
cristya19f1d72012-08-07 18:24:38 +0000867% o derivative: This array of doubles is initialized by
cristy3ed852e2009-09-05 21:47:34 +0000868% DerivativeHistogram to the derivative of the histogram using central
869% differencing.
870%
871*/
cristya19f1d72012-08-07 18:24:38 +0000872static void DerivativeHistogram(const double *histogram,
873 double *derivative)
cristy3ed852e2009-09-05 21:47:34 +0000874{
cristybb503372010-05-27 20:51:26 +0000875 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000876 i,
877 n;
878
879 /*
880 Compute endpoints using second order polynomial interpolation.
881 */
882 n=255;
883 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
884 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
885 /*
886 Compute derivative using central differencing.
887 */
888 for (i=1; i < n; i++)
889 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
890 return;
891}
892
893/*
894%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
895% %
896% %
897% %
898+ G e t I m a g e D y n a m i c T h r e s h o l d %
899% %
900% %
901% %
902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903%
904% GetImageDynamicThreshold() returns the dynamic threshold for an image.
905%
906% The format of the GetImageDynamicThreshold method is:
907%
908% MagickBooleanType GetImageDynamicThreshold(const Image *image,
909% const double cluster_threshold,const double smooth_threshold,
cristy4c08aed2011-07-01 19:47:50 +0000910% PixelInfo *pixel,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000911%
912% A description of each parameter follows.
913%
914% o image: the image.
915%
cristya19f1d72012-08-07 18:24:38 +0000916% o cluster_threshold: This double represents the minimum number of
cristy3ed852e2009-09-05 21:47:34 +0000917% pixels contained in a hexahedra before it can be considered valid
918% (expressed as a percentage).
919%
920% o smooth_threshold: the smoothing threshold eliminates noise in the second
921% derivative of the histogram. As the value is increased, you can expect a
922% smoother second derivative.
923%
924% o pixel: return the dynamic threshold here.
925%
926% o exception: return any errors or warnings in this structure.
927%
928*/
929MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
930 const double cluster_threshold,const double smooth_threshold,
cristy4c08aed2011-07-01 19:47:50 +0000931 PixelInfo *pixel,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000932{
933 Cluster
934 *background,
935 *cluster,
936 *object,
937 *head,
938 *last_cluster,
939 *next_cluster;
940
941 ExtentPacket
942 blue,
943 green,
944 red;
945
cristy3ed852e2009-09-05 21:47:34 +0000946 MagickBooleanType
947 proceed;
948
cristya19f1d72012-08-07 18:24:38 +0000949 double
cristy3ed852e2009-09-05 21:47:34 +0000950 threshold;
951
cristy4c08aed2011-07-01 19:47:50 +0000952 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000953 *p;
954
cristybb503372010-05-27 20:51:26 +0000955 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000956 i,
957 x;
958
959 short
960 *extrema[MaxDimension];
961
cristy9d314ff2011-03-09 01:30:28 +0000962 ssize_t
963 count,
964 *histogram[MaxDimension],
965 y;
966
cristy3ed852e2009-09-05 21:47:34 +0000967 /*
968 Allocate histogram and extrema.
969 */
970 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000971 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000972 if (image->debug != MagickFalse)
973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy4c08aed2011-07-01 19:47:50 +0000974 GetPixelInfo(image,pixel);
cristy3ed852e2009-09-05 21:47:34 +0000975 for (i=0; i < MaxDimension; i++)
976 {
cristybb503372010-05-27 20:51:26 +0000977 histogram[i]=(ssize_t *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +0000978 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
cristybb503372010-05-27 20:51:26 +0000979 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000980 {
981 for (i-- ; i >= 0; i--)
982 {
983 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +0000984 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +0000985 }
986 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000987 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000988 return(MagickFalse);
989 }
990 }
991 /*
992 Initialize histogram.
993 */
994 InitializeHistogram(image,histogram,exception);
995 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
996 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
997 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
998 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
999 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
1000 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
1001 /*
1002 Form clusters.
1003 */
1004 cluster=(Cluster *) NULL;
1005 head=(Cluster *) NULL;
1006 (void) ResetMagickMemory(&red,0,sizeof(red));
1007 (void) ResetMagickMemory(&green,0,sizeof(green));
1008 (void) ResetMagickMemory(&blue,0,sizeof(blue));
1009 while (DefineRegion(extrema[Red],&red) != 0)
1010 {
1011 green.index=0;
1012 while (DefineRegion(extrema[Green],&green) != 0)
1013 {
1014 blue.index=0;
1015 while (DefineRegion(extrema[Blue],&blue) != 0)
1016 {
1017 /*
1018 Allocate a new class.
1019 */
1020 if (head != (Cluster *) NULL)
1021 {
1022 cluster->next=(Cluster *) AcquireMagickMemory(
1023 sizeof(*cluster->next));
1024 cluster=cluster->next;
1025 }
1026 else
1027 {
cristy73bd4a52010-10-05 11:24:23 +00001028 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001029 head=cluster;
1030 }
1031 if (cluster == (Cluster *) NULL)
1032 {
1033 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001034 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001035 image->filename);
1036 return(MagickFalse);
1037 }
1038 /*
1039 Initialize a new class.
1040 */
1041 cluster->count=0;
1042 cluster->red=red;
1043 cluster->green=green;
1044 cluster->blue=blue;
1045 cluster->next=(Cluster *) NULL;
1046 }
1047 }
1048 }
1049 if (head == (Cluster *) NULL)
1050 {
1051 /*
1052 No classes were identified-- create one.
1053 */
cristy73bd4a52010-10-05 11:24:23 +00001054 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
cristy3ed852e2009-09-05 21:47:34 +00001055 if (cluster == (Cluster *) NULL)
1056 {
1057 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001058 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001059 return(MagickFalse);
1060 }
1061 /*
1062 Initialize a new class.
1063 */
1064 cluster->count=0;
1065 cluster->red=red;
1066 cluster->green=green;
1067 cluster->blue=blue;
1068 cluster->next=(Cluster *) NULL;
1069 head=cluster;
1070 }
1071 /*
1072 Count the pixels for each cluster.
1073 */
1074 count=0;
cristybb503372010-05-27 20:51:26 +00001075 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001076 {
1077 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001078 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001079 break;
cristybb503372010-05-27 20:51:26 +00001080 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001081 {
1082 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
cristy4c08aed2011-07-01 19:47:50 +00001083 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001084 (cluster->red.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001085 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001086 (cluster->red.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001087 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001088 (cluster->green.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001089 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001090 (cluster->green.right+SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001091 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) >=
cristy3ed852e2009-09-05 21:47:34 +00001092 (cluster->blue.left-SafeMargin)) &&
cristy4c08aed2011-07-01 19:47:50 +00001093 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) <=
cristy3ed852e2009-09-05 21:47:34 +00001094 (cluster->blue.right+SafeMargin)))
1095 {
1096 /*
1097 Count this pixel.
1098 */
1099 count++;
cristya19f1d72012-08-07 18:24:38 +00001100 cluster->red.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001101 GetPixelRed(image,p));
cristya19f1d72012-08-07 18:24:38 +00001102 cluster->green.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001103 GetPixelGreen(image,p));
cristya19f1d72012-08-07 18:24:38 +00001104 cluster->blue.center+=(double) ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001105 GetPixelBlue(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001106 cluster->count++;
1107 break;
1108 }
cristyed231572011-07-14 02:18:59 +00001109 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001110 }
cristycee97112010-05-28 00:44:52 +00001111 proceed=SetImageProgress(image,SegmentImageTag,(MagickOffsetType) y,
1112 2*image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001113 if (proceed == MagickFalse)
1114 break;
1115 }
1116 /*
1117 Remove clusters that do not meet minimum cluster threshold.
1118 */
1119 count=0;
1120 last_cluster=head;
1121 next_cluster=head;
1122 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1123 {
1124 next_cluster=cluster->next;
1125 if ((cluster->count > 0) &&
1126 (cluster->count >= (count*cluster_threshold/100.0)))
1127 {
1128 /*
1129 Initialize cluster.
1130 */
1131 cluster->id=count;
1132 cluster->red.center/=cluster->count;
1133 cluster->green.center/=cluster->count;
1134 cluster->blue.center/=cluster->count;
1135 count++;
1136 last_cluster=cluster;
1137 continue;
1138 }
1139 /*
1140 Delete cluster.
1141 */
1142 if (cluster == head)
1143 head=next_cluster;
1144 else
1145 last_cluster->next=next_cluster;
1146 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1147 }
1148 object=head;
1149 background=head;
1150 if (count > 1)
1151 {
1152 object=head->next;
1153 for (cluster=object; cluster->next != (Cluster *) NULL; )
1154 {
1155 if (cluster->count < object->count)
1156 object=cluster;
1157 cluster=cluster->next;
1158 }
1159 background=head->next;
1160 for (cluster=background; cluster->next != (Cluster *) NULL; )
1161 {
1162 if (cluster->count > background->count)
1163 background=cluster;
1164 cluster=cluster->next;
1165 }
1166 }
cristy375bd902014-01-15 00:54:40 +00001167 if (background != (Cluster *) NULL)
1168 {
1169 threshold=(background->red.center+object->red.center)/2.0;
1170 pixel->red=(double) ScaleCharToQuantum((unsigned char)
1171 (threshold+0.5));
1172 threshold=(background->green.center+object->green.center)/2.0;
1173 pixel->green=(double) ScaleCharToQuantum((unsigned char)
1174 (threshold+0.5));
1175 threshold=(background->blue.center+object->blue.center)/2.0;
1176 pixel->blue=(double) ScaleCharToQuantum((unsigned char)
1177 (threshold+0.5));
1178 }
cristy3ed852e2009-09-05 21:47:34 +00001179 /*
1180 Relinquish resources.
1181 */
1182 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
1183 {
1184 next_cluster=cluster->next;
1185 cluster=(Cluster *) RelinquishMagickMemory(cluster);
1186 }
1187 for (i=0; i < MaxDimension; i++)
1188 {
1189 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001190 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001191 }
1192 return(MagickTrue);
1193}
1194
1195/*
1196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197% %
1198% %
1199% %
1200+ I n i t i a l i z e H i s t o g r a m %
1201% %
1202% %
1203% %
1204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205%
1206% InitializeHistogram() computes the histogram for an image.
1207%
1208% The format of the InitializeHistogram method is:
1209%
cristybb503372010-05-27 20:51:26 +00001210% InitializeHistogram(const Image *image,ssize_t **histogram)
cristy3ed852e2009-09-05 21:47:34 +00001211%
1212% A description of each parameter follows.
1213%
1214% o image: Specifies a pointer to an Image structure; returned from
1215% ReadImage.
1216%
1217% o histogram: Specifies an array of integers representing the number
1218% of pixels for each intensity of a particular color component.
1219%
1220*/
cristybb503372010-05-27 20:51:26 +00001221static void InitializeHistogram(const Image *image,ssize_t **histogram,
cristy3ed852e2009-09-05 21:47:34 +00001222 ExceptionInfo *exception)
1223{
cristy4c08aed2011-07-01 19:47:50 +00001224 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001225 *p;
1226
cristybb503372010-05-27 20:51:26 +00001227 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001228 i,
1229 x;
1230
cristy9d314ff2011-03-09 01:30:28 +00001231 ssize_t
1232 y;
1233
cristy3ed852e2009-09-05 21:47:34 +00001234 /*
1235 Initialize histogram.
1236 */
1237 for (i=0; i <= 255; i++)
1238 {
1239 histogram[Red][i]=0;
1240 histogram[Green][i]=0;
1241 histogram[Blue][i]=0;
1242 }
cristybb503372010-05-27 20:51:26 +00001243 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001244 {
1245 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001246 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001247 break;
cristybb503372010-05-27 20:51:26 +00001248 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001249 {
cristy4c08aed2011-07-01 19:47:50 +00001250 histogram[Red][(ssize_t) ScaleQuantumToChar(GetPixelRed(image,p))]++;
1251 histogram[Green][(ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p))]++;
1252 histogram[Blue][(ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p))]++;
cristyed231572011-07-14 02:18:59 +00001253 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001254 }
1255 }
1256}
1257
1258/*
1259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1260% %
1261% %
1262% %
1263+ I n i t i a l i z e I n t e r v a l T r e e %
1264% %
1265% %
1266% %
1267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268%
1269% InitializeIntervalTree() initializes an interval tree from the lists of
1270% zero crossings.
1271%
1272% The format of the InitializeIntervalTree method is:
1273%
cristybb503372010-05-27 20:51:26 +00001274% InitializeIntervalTree(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001275% IntervalTree *node)
1276%
1277% A description of each parameter follows.
1278%
1279% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
1280%
cristybb503372010-05-27 20:51:26 +00001281% o number_crossings: This size_t specifies the number of elements
cristy3ed852e2009-09-05 21:47:34 +00001282% in the zero_crossing array.
1283%
1284*/
1285
cristybb503372010-05-27 20:51:26 +00001286static void InitializeList(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001287 IntervalTree *node)
1288{
1289 if (node == (IntervalTree *) NULL)
1290 return;
1291 if (node->child == (IntervalTree *) NULL)
1292 list[(*number_nodes)++]=node;
1293 InitializeList(list,number_nodes,node->sibling);
1294 InitializeList(list,number_nodes,node->child);
1295}
1296
1297static void MeanStability(IntervalTree *node)
1298{
1299 register IntervalTree
1300 *child;
1301
1302 if (node == (IntervalTree *) NULL)
1303 return;
1304 node->mean_stability=0.0;
1305 child=node->child;
1306 if (child != (IntervalTree *) NULL)
1307 {
cristybb503372010-05-27 20:51:26 +00001308 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001309 count;
1310
cristya19f1d72012-08-07 18:24:38 +00001311 register double
cristy3ed852e2009-09-05 21:47:34 +00001312 sum;
1313
1314 sum=0.0;
1315 count=0;
1316 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
1317 {
1318 sum+=child->stability;
1319 count++;
1320 }
cristya19f1d72012-08-07 18:24:38 +00001321 node->mean_stability=sum/(double) count;
cristy3ed852e2009-09-05 21:47:34 +00001322 }
1323 MeanStability(node->sibling);
1324 MeanStability(node->child);
1325}
1326
1327static void Stability(IntervalTree *node)
1328{
1329 if (node == (IntervalTree *) NULL)
1330 return;
1331 if (node->child == (IntervalTree *) NULL)
1332 node->stability=0.0;
1333 else
1334 node->stability=node->tau-(node->child)->tau;
1335 Stability(node->sibling);
1336 Stability(node->child);
1337}
1338
1339static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
cristybb503372010-05-27 20:51:26 +00001340 const size_t number_crossings)
cristy3ed852e2009-09-05 21:47:34 +00001341{
1342 IntervalTree
1343 *head,
1344 **list,
1345 *node,
1346 *root;
1347
cristy9d314ff2011-03-09 01:30:28 +00001348 register ssize_t
1349 i;
1350
cristybb503372010-05-27 20:51:26 +00001351 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001352 j,
1353 k,
1354 left,
1355 number_nodes;
1356
cristy3ed852e2009-09-05 21:47:34 +00001357 /*
1358 Allocate interval tree.
1359 */
1360 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1361 sizeof(*list));
1362 if (list == (IntervalTree **) NULL)
1363 return((IntervalTree *) NULL);
1364 /*
1365 The root is the entire histogram.
1366 */
cristy73bd4a52010-10-05 11:24:23 +00001367 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
cristy3ed852e2009-09-05 21:47:34 +00001368 root->child=(IntervalTree *) NULL;
1369 root->sibling=(IntervalTree *) NULL;
1370 root->tau=0.0;
1371 root->left=0;
1372 root->right=255;
cristybb503372010-05-27 20:51:26 +00001373 for (i=(-1); i < (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001374 {
1375 /*
1376 Initialize list with all nodes with no children.
1377 */
1378 number_nodes=0;
1379 InitializeList(list,&number_nodes,root);
1380 /*
1381 Split list.
1382 */
1383 for (j=0; j < number_nodes; j++)
1384 {
1385 head=list[j];
1386 left=head->left;
1387 node=head;
1388 for (k=head->left+1; k < head->right; k++)
1389 {
1390 if (zero_crossing[i+1].crossings[k] != 0)
1391 {
1392 if (node == head)
1393 {
1394 node->child=(IntervalTree *) AcquireMagickMemory(
1395 sizeof(*node->child));
1396 node=node->child;
1397 }
1398 else
1399 {
1400 node->sibling=(IntervalTree *) AcquireMagickMemory(
1401 sizeof(*node->sibling));
1402 node=node->sibling;
1403 }
1404 node->tau=zero_crossing[i+1].tau;
1405 node->child=(IntervalTree *) NULL;
1406 node->sibling=(IntervalTree *) NULL;
1407 node->left=left;
1408 node->right=k;
1409 left=k;
1410 }
1411 }
1412 if (left != head->left)
1413 {
1414 node->sibling=(IntervalTree *) AcquireMagickMemory(
1415 sizeof(*node->sibling));
1416 node=node->sibling;
1417 node->tau=zero_crossing[i+1].tau;
1418 node->child=(IntervalTree *) NULL;
1419 node->sibling=(IntervalTree *) NULL;
1420 node->left=left;
1421 node->right=head->right;
1422 }
1423 }
1424 }
1425 /*
1426 Determine the stability: difference between a nodes tau and its child.
1427 */
1428 Stability(root->child);
1429 MeanStability(root->child);
1430 list=(IntervalTree **) RelinquishMagickMemory(list);
1431 return(root);
1432}
1433
1434/*
1435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436% %
1437% %
1438% %
1439+ O p t i m a l T a u %
1440% %
1441% %
1442% %
1443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444%
1445% OptimalTau() finds the optimal tau for each band of the histogram.
1446%
1447% The format of the OptimalTau method is:
1448%
cristya19f1d72012-08-07 18:24:38 +00001449% double OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001450% const double min_tau,const double delta_tau,
1451% const double smooth_threshold,short *extrema)
1452%
1453% A description of each parameter follows.
1454%
1455% o histogram: Specifies an array of integers representing the number
1456% of pixels for each intensity of a particular color component.
1457%
1458% o extrema: Specifies a pointer to an array of integers. They
1459% represent the peaks and valleys of the histogram for each color
1460% component.
1461%
1462*/
1463
cristybb503372010-05-27 20:51:26 +00001464static void ActiveNodes(IntervalTree **list,ssize_t *number_nodes,
cristy3ed852e2009-09-05 21:47:34 +00001465 IntervalTree *node)
1466{
1467 if (node == (IntervalTree *) NULL)
1468 return;
1469 if (node->stability >= node->mean_stability)
1470 {
1471 list[(*number_nodes)++]=node;
1472 ActiveNodes(list,number_nodes,node->sibling);
1473 }
1474 else
1475 {
1476 ActiveNodes(list,number_nodes,node->sibling);
1477 ActiveNodes(list,number_nodes,node->child);
1478 }
1479}
1480
1481static void FreeNodes(IntervalTree *node)
1482{
1483 if (node == (IntervalTree *) NULL)
1484 return;
1485 FreeNodes(node->sibling);
1486 FreeNodes(node->child);
1487 node=(IntervalTree *) RelinquishMagickMemory(node);
1488}
1489
cristya19f1d72012-08-07 18:24:38 +00001490static double OptimalTau(const ssize_t *histogram,const double max_tau,
cristy3ed852e2009-09-05 21:47:34 +00001491 const double min_tau,const double delta_tau,const double smooth_threshold,
1492 short *extrema)
1493{
1494 IntervalTree
1495 **list,
1496 *node,
1497 *root;
1498
cristy9d314ff2011-03-09 01:30:28 +00001499 MagickBooleanType
1500 peak;
cristy3ed852e2009-09-05 21:47:34 +00001501
cristya19f1d72012-08-07 18:24:38 +00001502 double
cristy3ed852e2009-09-05 21:47:34 +00001503 average_tau,
1504 *derivative,
1505 *second_derivative,
1506 tau,
1507 value;
1508
cristybb503372010-05-27 20:51:26 +00001509 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001510 i,
1511 x;
1512
cristybb503372010-05-27 20:51:26 +00001513 size_t
cristy3ed852e2009-09-05 21:47:34 +00001514 count,
1515 number_crossings;
1516
cristy9d314ff2011-03-09 01:30:28 +00001517 ssize_t
1518 index,
1519 j,
1520 k,
1521 number_nodes;
1522
cristy3ed852e2009-09-05 21:47:34 +00001523 ZeroCrossing
1524 *zero_crossing;
1525
1526 /*
1527 Allocate interval tree.
1528 */
1529 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
1530 sizeof(*list));
1531 if (list == (IntervalTree **) NULL)
1532 return(0.0);
1533 /*
1534 Allocate zero crossing list.
1535 */
cristybb503372010-05-27 20:51:26 +00001536 count=(size_t) ((max_tau-min_tau)/delta_tau)+2;
cristy3ed852e2009-09-05 21:47:34 +00001537 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
1538 sizeof(*zero_crossing));
1539 if (zero_crossing == (ZeroCrossing *) NULL)
1540 return(0.0);
cristybb503372010-05-27 20:51:26 +00001541 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00001542 zero_crossing[i].tau=(-1.0);
1543 /*
1544 Initialize zero crossing list.
1545 */
cristya19f1d72012-08-07 18:24:38 +00001546 derivative=(double *) AcquireQuantumMemory(256,sizeof(*derivative));
1547 second_derivative=(double *) AcquireQuantumMemory(256,
cristy3ed852e2009-09-05 21:47:34 +00001548 sizeof(*second_derivative));
cristya19f1d72012-08-07 18:24:38 +00001549 if ((derivative == (double *) NULL) ||
1550 (second_derivative == (double *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001551 ThrowFatalException(ResourceLimitFatalError,
1552 "UnableToAllocateDerivatives");
1553 i=0;
1554 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
1555 {
1556 zero_crossing[i].tau=tau;
1557 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
1558 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1559 DerivativeHistogram(derivative,second_derivative);
1560 ZeroCrossHistogram(second_derivative,smooth_threshold,
1561 zero_crossing[i].crossings);
1562 i++;
1563 }
1564 /*
1565 Add an entry for the original histogram.
1566 */
1567 zero_crossing[i].tau=0.0;
1568 for (j=0; j <= 255; j++)
cristya19f1d72012-08-07 18:24:38 +00001569 zero_crossing[i].histogram[j]=(double) histogram[j];
cristy3ed852e2009-09-05 21:47:34 +00001570 DerivativeHistogram(zero_crossing[i].histogram,derivative);
1571 DerivativeHistogram(derivative,second_derivative);
1572 ZeroCrossHistogram(second_derivative,smooth_threshold,
1573 zero_crossing[i].crossings);
cristybb503372010-05-27 20:51:26 +00001574 number_crossings=(size_t) i;
cristya19f1d72012-08-07 18:24:38 +00001575 derivative=(double *) RelinquishMagickMemory(derivative);
1576 second_derivative=(double *)
cristy3ed852e2009-09-05 21:47:34 +00001577 RelinquishMagickMemory(second_derivative);
1578 /*
1579 Ensure the scale-space fingerprints form lines in scale-space, not loops.
1580 */
1581 ConsolidateCrossings(zero_crossing,number_crossings);
1582 /*
1583 Force endpoints to be included in the interval.
1584 */
cristybb503372010-05-27 20:51:26 +00001585 for (i=0; i <= (ssize_t) number_crossings; i++)
cristy3ed852e2009-09-05 21:47:34 +00001586 {
1587 for (j=0; j < 255; j++)
1588 if (zero_crossing[i].crossings[j] != 0)
1589 break;
1590 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
1591 for (j=255; j > 0; j--)
1592 if (zero_crossing[i].crossings[j] != 0)
1593 break;
1594 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
1595 }
1596 /*
1597 Initialize interval tree.
1598 */
1599 root=InitializeIntervalTree(zero_crossing,number_crossings);
1600 if (root == (IntervalTree *) NULL)
1601 return(0.0);
1602 /*
1603 Find active nodes: stability is greater (or equal) to the mean stability of
1604 its children.
1605 */
1606 number_nodes=0;
1607 ActiveNodes(list,&number_nodes,root->child);
1608 /*
1609 Initialize extrema.
1610 */
1611 for (i=0; i <= 255; i++)
1612 extrema[i]=0;
1613 for (i=0; i < number_nodes; i++)
1614 {
1615 /*
1616 Find this tau in zero crossings list.
1617 */
1618 k=0;
1619 node=list[i];
cristybb503372010-05-27 20:51:26 +00001620 for (j=0; j <= (ssize_t) number_crossings; j++)
cristy3ed852e2009-09-05 21:47:34 +00001621 if (zero_crossing[j].tau == node->tau)
1622 k=j;
1623 /*
1624 Find the value of the peak.
1625 */
1626 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
1627 MagickFalse;
1628 index=node->left;
1629 value=zero_crossing[k].histogram[index];
1630 for (x=node->left; x <= node->right; x++)
1631 {
1632 if (peak != MagickFalse)
1633 {
1634 if (zero_crossing[k].histogram[x] > value)
1635 {
1636 value=zero_crossing[k].histogram[x];
1637 index=x;
1638 }
1639 }
1640 else
1641 if (zero_crossing[k].histogram[x] < value)
1642 {
1643 value=zero_crossing[k].histogram[x];
1644 index=x;
1645 }
1646 }
1647 for (x=node->left; x <= node->right; x++)
1648 {
1649 if (index == 0)
1650 index=256;
1651 if (peak != MagickFalse)
1652 extrema[x]=(short) index;
1653 else
1654 extrema[x]=(short) (-index);
1655 }
1656 }
1657 /*
1658 Determine the average tau.
1659 */
1660 average_tau=0.0;
1661 for (i=0; i < number_nodes; i++)
1662 average_tau+=list[i]->tau;
cristya19f1d72012-08-07 18:24:38 +00001663 average_tau/=(double) number_nodes;
cristy3ed852e2009-09-05 21:47:34 +00001664 /*
1665 Relinquish resources.
1666 */
1667 FreeNodes(root);
1668 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
1669 list=(IntervalTree **) RelinquishMagickMemory(list);
1670 return(average_tau);
1671}
1672
1673/*
1674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1675% %
1676% %
1677% %
1678+ S c a l e S p a c e %
1679% %
1680% %
1681% %
1682%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1683%
1684% ScaleSpace() performs a scale-space filter on the 1D histogram.
1685%
1686% The format of the ScaleSpace method is:
1687%
cristya19f1d72012-08-07 18:24:38 +00001688% ScaleSpace(const ssize_t *histogram,const double tau,
1689% double *scale_histogram)
cristy3ed852e2009-09-05 21:47:34 +00001690%
1691% A description of each parameter follows.
1692%
cristya19f1d72012-08-07 18:24:38 +00001693% o histogram: Specifies an array of doubles representing the number
cristy3ed852e2009-09-05 21:47:34 +00001694% of pixels for each intensity of a particular color component.
1695%
1696*/
1697
cristya19f1d72012-08-07 18:24:38 +00001698static void ScaleSpace(const ssize_t *histogram,const double tau,
1699 double *scale_histogram)
cristy3ed852e2009-09-05 21:47:34 +00001700{
cristya19f1d72012-08-07 18:24:38 +00001701 double
cristy3ed852e2009-09-05 21:47:34 +00001702 alpha,
1703 beta,
1704 *gamma,
1705 sum;
1706
cristybb503372010-05-27 20:51:26 +00001707 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001708 u,
1709 x;
1710
cristya19f1d72012-08-07 18:24:38 +00001711 gamma=(double *) AcquireQuantumMemory(256,sizeof(*gamma));
1712 if (gamma == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001713 ThrowFatalException(ResourceLimitFatalError,
1714 "UnableToAllocateGammaMap");
cristy3e3ec3a2012-11-03 23:11:06 +00001715 alpha=PerceptibleReciprocal(tau*sqrt(2.0*MagickPI));
1716 beta=(-1.0*PerceptibleReciprocal(2.0*tau*tau));
cristy3ed852e2009-09-05 21:47:34 +00001717 for (x=0; x <= 255; x++)
1718 gamma[x]=0.0;
1719 for (x=0; x <= 255; x++)
1720 {
1721 gamma[x]=exp((double) beta*x*x);
1722 if (gamma[x] < MagickEpsilon)
1723 break;
1724 }
1725 for (x=0; x <= 255; x++)
1726 {
1727 sum=0.0;
1728 for (u=0; u <= 255; u++)
cristya19f1d72012-08-07 18:24:38 +00001729 sum+=(double) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
cristy3ed852e2009-09-05 21:47:34 +00001730 scale_histogram[x]=alpha*sum;
1731 }
cristya19f1d72012-08-07 18:24:38 +00001732 gamma=(double *) RelinquishMagickMemory(gamma);
cristy3ed852e2009-09-05 21:47:34 +00001733}
1734
1735/*
1736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1737% %
1738% %
1739% %
1740% S e g m e n t I m a g e %
1741% %
1742% %
1743% %
1744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745%
1746% SegmentImage() segment an image by analyzing the histograms of the color
1747% components and identifying units that are homogeneous with the fuzzy
1748% C-means technique.
1749%
1750% The format of the SegmentImage method is:
1751%
1752% MagickBooleanType SegmentImage(Image *image,
1753% const ColorspaceType colorspace,const MagickBooleanType verbose,
cristy018f07f2011-09-04 21:15:19 +00001754% const double cluster_threshold,const double smooth_threshold,
1755% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001756%
1757% A description of each parameter follows.
1758%
1759% o image: the image.
1760%
1761% o colorspace: Indicate the colorspace.
1762%
1763% o verbose: Set to MagickTrue to print detailed information about the
1764% identified classes.
1765%
1766% o cluster_threshold: This represents the minimum number of pixels
1767% contained in a hexahedra before it can be considered valid (expressed
1768% as a percentage).
1769%
1770% o smooth_threshold: the smoothing threshold eliminates noise in the second
1771% derivative of the histogram. As the value is increased, you can expect a
1772% smoother second derivative.
1773%
cristy018f07f2011-09-04 21:15:19 +00001774% o exception: return any errors or warnings in this structure.
1775%
cristy3ed852e2009-09-05 21:47:34 +00001776*/
1777MagickExport MagickBooleanType SegmentImage(Image *image,
1778 const ColorspaceType colorspace,const MagickBooleanType verbose,
cristy018f07f2011-09-04 21:15:19 +00001779 const double cluster_threshold,const double smooth_threshold,
1780 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001781{
cristy3d9f5ba2012-06-26 13:37:31 +00001782 ColorspaceType
1783 previous_colorspace;
1784
cristy3ed852e2009-09-05 21:47:34 +00001785 MagickBooleanType
1786 status;
1787
cristybb503372010-05-27 20:51:26 +00001788 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001789 i;
1790
1791 short
1792 *extrema[MaxDimension];
1793
cristy9d314ff2011-03-09 01:30:28 +00001794 ssize_t
1795 *histogram[MaxDimension];
1796
cristy3ed852e2009-09-05 21:47:34 +00001797 /*
1798 Allocate histogram and extrema.
1799 */
1800 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001801 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001802 if (image->debug != MagickFalse)
1803 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1804 for (i=0; i < MaxDimension; i++)
1805 {
cristybb503372010-05-27 20:51:26 +00001806 histogram[i]=(ssize_t *) AcquireQuantumMemory(256,sizeof(**histogram));
cristy3ed852e2009-09-05 21:47:34 +00001807 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
cristybb503372010-05-27 20:51:26 +00001808 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001809 {
1810 for (i-- ; i >= 0; i--)
1811 {
1812 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001813 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001814 }
1815 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1816 image->filename)
1817 }
1818 }
cristy3ed852e2009-09-05 21:47:34 +00001819 /*
1820 Initialize histogram.
1821 */
cristy3d9f5ba2012-06-26 13:37:31 +00001822 previous_colorspace=image->colorspace;
1823 (void) TransformImageColorspace(image,colorspace,exception);
cristyc82a27b2011-10-21 01:07:16 +00001824 InitializeHistogram(image,histogram,exception);
cristy3ed852e2009-09-05 21:47:34 +00001825 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
1826 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
1827 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
1828 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
1829 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
1830 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
1831 /*
1832 Classify using the fuzzy c-Means technique.
1833 */
cristy018f07f2011-09-04 21:15:19 +00001834 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose,
1835 exception);
cristy3d9f5ba2012-06-26 13:37:31 +00001836 (void) TransformImageColorspace(image,previous_colorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001837 /*
1838 Relinquish resources.
1839 */
1840 for (i=0; i < MaxDimension; i++)
1841 {
1842 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
cristybb503372010-05-27 20:51:26 +00001843 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]);
cristy3ed852e2009-09-05 21:47:34 +00001844 }
1845 return(status);
1846}
1847
1848/*
1849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1850% %
1851% %
1852% %
1853+ Z e r o C r o s s H i s t o g r a m %
1854% %
1855% %
1856% %
1857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1858%
1859% ZeroCrossHistogram() find the zero crossings in a histogram and marks
1860% directions as: 1 is negative to positive; 0 is zero crossing; and -1
1861% is positive to negative.
1862%
1863% The format of the ZeroCrossHistogram method is:
1864%
cristya19f1d72012-08-07 18:24:38 +00001865% ZeroCrossHistogram(double *second_derivative,
1866% const double smooth_threshold,short *crossings)
cristy3ed852e2009-09-05 21:47:34 +00001867%
1868% A description of each parameter follows.
1869%
cristya19f1d72012-08-07 18:24:38 +00001870% o second_derivative: Specifies an array of doubles representing the
cristy3ed852e2009-09-05 21:47:34 +00001871% second derivative of the histogram of a particular color component.
1872%
1873% o crossings: This array of integers is initialized with
1874% -1, 0, or 1 representing the slope of the first derivative of the
1875% of a particular color component.
1876%
1877*/
cristya19f1d72012-08-07 18:24:38 +00001878static void ZeroCrossHistogram(double *second_derivative,
1879 const double smooth_threshold,short *crossings)
cristy3ed852e2009-09-05 21:47:34 +00001880{
cristybb503372010-05-27 20:51:26 +00001881 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001882 i;
1883
cristy9d314ff2011-03-09 01:30:28 +00001884 ssize_t
1885 parity;
1886
cristy3ed852e2009-09-05 21:47:34 +00001887 /*
1888 Merge low numbers to zero to help prevent noise.
1889 */
1890 for (i=0; i <= 255; i++)
1891 if ((second_derivative[i] < smooth_threshold) &&
1892 (second_derivative[i] >= -smooth_threshold))
1893 second_derivative[i]=0.0;
1894 /*
1895 Mark zero crossings.
1896 */
1897 parity=0;
1898 for (i=0; i <= 255; i++)
1899 {
1900 crossings[i]=0;
1901 if (second_derivative[i] < 0.0)
1902 {
1903 if (parity > 0)
1904 crossings[i]=(-1);
1905 parity=1;
1906 }
1907 else
1908 if (second_derivative[i] > 0.0)
1909 {
1910 if (parity < 0)
1911 crossings[i]=1;
1912 parity=(-1);
1913 }
1914 }
1915}