Coverage Report

Created: 2026-03-31 06:56

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