00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 #include "magick/studio.h"
00086 #include "magick/cache.h"
00087 #include "magick/color.h"
00088 #include "magick/colorspace.h"
00089 #include "magick/exception.h"
00090 #include "magick/exception-private.h"
00091 #include "magick/image.h"
00092 #include "magick/image-private.h"
00093 #include "magick/memory_.h"
00094 #include "magick/monitor.h"
00095 #include "magick/monitor-private.h"
00096 #include "magick/quantize.h"
00097 #include "magick/quantum.h"
00098 #include "magick/quantum-private.h"
00099 #include "magick/segment.h"
00100 #include "magick/string_.h"
00101
00102
00103
00104
00105 #define MaxDimension 3
00106 #define DeltaTau 0.5f
00107 #if defined(FastClassify)
00108 #define WeightingExponent 2.0
00109 #define SegmentPower(ratio) (ratio)
00110 #else
00111 #define WeightingExponent 2.5
00112 #define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
00113 #endif
00114 #define Tau 5.2f
00115
00116
00117
00118
00119 typedef struct _ExtentPacket
00120 {
00121 MagickRealType
00122 center;
00123
00124 long
00125 index,
00126 left,
00127 right;
00128 } ExtentPacket;
00129
00130 typedef struct _Cluster
00131 {
00132 struct _Cluster
00133 *next;
00134
00135 ExtentPacket
00136 red,
00137 green,
00138 blue;
00139
00140 long
00141 count,
00142 id;
00143 } Cluster;
00144
00145 typedef struct _IntervalTree
00146 {
00147 MagickRealType
00148 tau;
00149
00150 long
00151 left,
00152 right;
00153
00154 MagickRealType
00155 mean_stability,
00156 stability;
00157
00158 struct _IntervalTree
00159 *sibling,
00160 *child;
00161 } IntervalTree;
00162
00163 typedef struct _ZeroCrossing
00164 {
00165 MagickRealType
00166 tau,
00167 histogram[256];
00168
00169 short
00170 crossings[256];
00171 } ZeroCrossing;
00172
00173
00174
00175
00176 static const int
00177 Blue = 2,
00178 Green = 1,
00179 Red = 0,
00180 SafeMargin = 3,
00181 TreeLength = 600;
00182
00183
00184
00185
00186 static MagickRealType
00187 OptimalTau(const long *,const double,const double,const double,
00188 const double,short *);
00189
00190 static long
00191 DefineRegion(const short *,ExtentPacket *);
00192
00193 static void
00194 InitializeHistogram(const Image *,long **,ExceptionInfo *),
00195 ScaleSpace(const long *,const MagickRealType,MagickRealType *),
00196 ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *);
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238 static MagickBooleanType Classify(Image *image,short **extrema,
00239 const MagickRealType cluster_threshold,
00240 const MagickRealType weighting_exponent,const MagickBooleanType verbose)
00241 {
00242 #define SegmentImageTag "Segment/Image"
00243
00244 Cluster
00245 *cluster,
00246 *head,
00247 *last_cluster,
00248 *next_cluster;
00249
00250 ExceptionInfo
00251 *exception;
00252
00253 ExtentPacket
00254 blue,
00255 green,
00256 red;
00257
00258 IndexPacket
00259 index;
00260
00261 long
00262 count,
00263 j,
00264 k,
00265 y;
00266
00267 MagickBooleanType
00268 proceed;
00269
00270 MagickRealType
00271 distance_squared,
00272 *free_squares,
00273 local_minima,
00274 numerator,
00275 ratio,
00276 sum;
00277
00278 register const PixelPacket
00279 *p;
00280
00281 register IndexPacket
00282 *indexes;
00283
00284 register long
00285 i,
00286 x;
00287
00288 register MagickRealType
00289 *squares;
00290
00291 register PixelPacket
00292 *q;
00293
00294 unsigned long
00295 number_clusters;
00296
00297
00298
00299
00300 cluster=(Cluster *) NULL;
00301 head=(Cluster *) NULL;
00302 (void) ResetMagickMemory(&red,0,sizeof(red));
00303 (void) ResetMagickMemory(&green,0,sizeof(green));
00304 (void) ResetMagickMemory(&blue,0,sizeof(blue));
00305 while (DefineRegion(extrema[Red],&red) != 0)
00306 {
00307 green.index=0;
00308 while (DefineRegion(extrema[Green],&green) != 0)
00309 {
00310 blue.index=0;
00311 while (DefineRegion(extrema[Blue],&blue) != 0)
00312 {
00313
00314
00315
00316 if (head != (Cluster *) NULL)
00317 {
00318 cluster->next=(Cluster *) AcquireMagickMemory(
00319 sizeof(*cluster->next));
00320 cluster=cluster->next;
00321 }
00322 else
00323 {
00324 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
00325 head=cluster;
00326 }
00327 if (cluster == (Cluster *) NULL)
00328 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00329 image->filename);
00330
00331
00332
00333 cluster->count=0;
00334 cluster->red=red;
00335 cluster->green=green;
00336 cluster->blue=blue;
00337 cluster->next=(Cluster *) NULL;
00338 }
00339 }
00340 }
00341 if (head == (Cluster *) NULL)
00342 {
00343
00344
00345
00346 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
00347 if (cluster == (Cluster *) NULL)
00348 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00349 image->filename);
00350
00351
00352
00353 cluster->count=0;
00354 cluster->red=red;
00355 cluster->green=green;
00356 cluster->blue=blue;
00357 cluster->next=(Cluster *) NULL;
00358 head=cluster;
00359 }
00360
00361
00362
00363 count=0;
00364 exception=(&image->exception);
00365 for (y=0; y < (long) image->rows; y++)
00366 {
00367 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
00368 if (p == (const PixelPacket *) NULL)
00369 break;
00370 for (x=0; x < (long) image->columns; x++)
00371 {
00372 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00373 if (((long) ScaleQuantumToChar(p->red) >=
00374 (cluster->red.left-SafeMargin)) &&
00375 ((long) ScaleQuantumToChar(p->red) <=
00376 (cluster->red.right+SafeMargin)) &&
00377 ((long) ScaleQuantumToChar(p->green) >=
00378 (cluster->green.left-SafeMargin)) &&
00379 ((long) ScaleQuantumToChar(p->green) <=
00380 (cluster->green.right+SafeMargin)) &&
00381 ((long) ScaleQuantumToChar(p->blue) >=
00382 (cluster->blue.left-SafeMargin)) &&
00383 ((long) ScaleQuantumToChar(p->blue) <=
00384 (cluster->blue.right+SafeMargin)))
00385 {
00386
00387
00388
00389 count++;
00390 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(p->red);
00391 cluster->green.center+=(MagickRealType)
00392 ScaleQuantumToChar(p->green);
00393 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(p->blue);
00394 cluster->count++;
00395 break;
00396 }
00397 p++;
00398 }
00399 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
00400 if (proceed == MagickFalse)
00401 break;
00402 }
00403
00404
00405
00406 count=0;
00407 last_cluster=head;
00408 next_cluster=head;
00409 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
00410 {
00411 next_cluster=cluster->next;
00412 if ((cluster->count > 0) &&
00413 (cluster->count >= (count*cluster_threshold/100.0)))
00414 {
00415
00416
00417
00418 cluster->id=count;
00419 cluster->red.center/=cluster->count;
00420 cluster->green.center/=cluster->count;
00421 cluster->blue.center/=cluster->count;
00422 count++;
00423 last_cluster=cluster;
00424 continue;
00425 }
00426
00427
00428
00429 if (cluster == head)
00430 head=next_cluster;
00431 else
00432 last_cluster->next=next_cluster;
00433 cluster=(Cluster *) RelinquishMagickMemory(cluster);
00434 }
00435 number_clusters=(unsigned long) count;
00436 if (verbose != MagickFalse)
00437 {
00438
00439
00440
00441 (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
00442 (void) fprintf(stdout,"===================\n\n");
00443 (void) fprintf(stdout,"\tTotal Number of Clusters = %lu\n\n",
00444 number_clusters);
00445
00446
00447
00448 (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
00449 (void) fprintf(stdout,"=============================\n\n");
00450 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00451 (void) fprintf(stdout,"Cluster #%ld = %ld\n",cluster->id,
00452 cluster->count);
00453
00454
00455
00456 (void) fprintf(stdout,
00457 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
00458 (void) fprintf(stdout,"================");
00459 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00460 {
00461 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
00462 (void) fprintf(stdout,"%ld-%ld %ld-%ld %ld-%ld\n",cluster->red.left,
00463 cluster->red.right,cluster->green.left,cluster->green.right,
00464 cluster->blue.left,cluster->blue.right);
00465 }
00466
00467
00468
00469 (void) fprintf(stdout,
00470 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
00471 (void) fprintf(stdout,"=====================");
00472 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00473 {
00474 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
00475 (void) fprintf(stdout,"%g %g %g\n",(double) cluster->red.center,
00476 (double) cluster->green.center,(double) cluster->blue.center);
00477 }
00478 (void) fprintf(stdout,"\n");
00479 }
00480 if (number_clusters > 256)
00481 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
00482
00483
00484
00485 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
00486 if (squares == (MagickRealType *) NULL)
00487 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00488 image->filename);
00489 squares+=255;
00490 for (i=(-255); i <= 255; i++)
00491 squares[i]=(MagickRealType) i*(MagickRealType) i;
00492
00493
00494
00495 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
00496 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00497 image->filename);
00498 cluster=head;
00499 for (i=0; i < (long) number_clusters; i++)
00500 {
00501 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
00502 (cluster->red.center+0.5));
00503 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
00504 (cluster->green.center+0.5));
00505 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
00506 (cluster->blue.center+0.5));
00507 cluster=cluster->next;
00508 }
00509
00510
00511
00512 exception=(&image->exception);
00513 for (y=0; y < (long) image->rows; y++)
00514 {
00515 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
00516 if (q == (PixelPacket *) NULL)
00517 break;
00518 indexes=GetAuthenticIndexQueue(image);
00519 for (x=0; x < (long) image->columns; x++)
00520 {
00521 index=(IndexPacket) 0;
00522 cluster=head;
00523 for (i=0; i < (long) number_clusters; i++)
00524 {
00525 if (((long) ScaleQuantumToChar(q->red) >=
00526 (cluster->red.left-SafeMargin)) &&
00527 ((long) ScaleQuantumToChar(q->red) <=
00528 (cluster->red.right+SafeMargin)) &&
00529 ((long) ScaleQuantumToChar(q->green) >=
00530 (cluster->green.left-SafeMargin)) &&
00531 ((long) ScaleQuantumToChar(q->green) <=
00532 (cluster->green.right+SafeMargin)) &&
00533 ((long) ScaleQuantumToChar(q->blue) >=
00534 (cluster->blue.left-SafeMargin)) &&
00535 ((long) ScaleQuantumToChar(q->blue) <=
00536 (cluster->blue.right+SafeMargin)))
00537 {
00538
00539
00540
00541 index=(IndexPacket) cluster->id;
00542 break;
00543 }
00544 cluster=cluster->next;
00545 }
00546 if (cluster == (Cluster *) NULL)
00547 {
00548
00549
00550
00551 local_minima=0.0;
00552 for (j=0; j < (long) image->colors; j++)
00553 {
00554 sum=0.0;
00555 p=image->colormap+j;
00556 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00557 (long) ScaleQuantumToChar(p->red)]+
00558 squares[(long) ScaleQuantumToChar(q->green)-
00559 (long) ScaleQuantumToChar(p->green)]+
00560 squares[(long) ScaleQuantumToChar(q->blue)-
00561 (long) ScaleQuantumToChar(p->blue)];
00562 numerator=distance_squared;
00563 for (k=0; k < (long) image->colors; k++)
00564 {
00565 p=image->colormap+k;
00566 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00567 (long) ScaleQuantumToChar(p->red)]+
00568 squares[(long) ScaleQuantumToChar(q->green)-
00569 (long) ScaleQuantumToChar(p->green)]+
00570 squares[(long) ScaleQuantumToChar(q->blue)-
00571 (long) ScaleQuantumToChar(p->blue)];
00572 ratio=numerator/distance_squared;
00573 sum+=SegmentPower(ratio);
00574 }
00575 if ((sum != 0.0) && ((1.0/sum) > local_minima))
00576 {
00577
00578
00579
00580 local_minima=1.0/sum;
00581 index=(IndexPacket) j;
00582 }
00583 }
00584 }
00585 indexes[x]=index;
00586 *q=image->colormap[(long) index];
00587 q++;
00588 }
00589 if (SyncAuthenticPixels(image,exception) == MagickFalse)
00590 break;
00591 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
00592 if (proceed == MagickFalse)
00593 break;
00594 }
00595
00596
00597
00598 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
00599 {
00600 next_cluster=cluster->next;
00601 cluster=(Cluster *) RelinquishMagickMemory(cluster);
00602 }
00603 squares-=255;
00604 free_squares=squares;
00605 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
00606 return(MagickTrue);
00607 }
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637 static inline long MagickAbsoluteValue(const long x)
00638 {
00639 if (x < 0)
00640 return(-x);
00641 return(x);
00642 }
00643
00644 static inline long MagickMax(const long x,const long y)
00645 {
00646 if (x > y)
00647 return(x);
00648 return(y);
00649 }
00650
00651 static inline long MagickMin(const long x,const long y)
00652 {
00653 if (x < y)
00654 return(x);
00655 return(y);
00656 }
00657
00658 static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
00659 const unsigned long number_crossings)
00660 {
00661 long
00662 center,
00663 correct,
00664 count,
00665 left,
00666 right;
00667
00668 register long
00669 i,
00670 j,
00671 k,
00672 l;
00673
00674
00675
00676
00677 for (i=(long) number_crossings-1; i >= 0; i--)
00678 for (j=0; j <= 255; j++)
00679 {
00680 if (zero_crossing[i].crossings[j] == 0)
00681 continue;
00682
00683
00684
00685
00686
00687 for (k=j-1; k > 0; k--)
00688 if (zero_crossing[i+1].crossings[k] != 0)
00689 break;
00690 left=MagickMax(k,0);
00691 center=j;
00692 for (k=j+1; k < 255; k++)
00693 if (zero_crossing[i+1].crossings[k] != 0)
00694 break;
00695 right=MagickMin(k,255);
00696
00697
00698
00699 for (k=j-1; k > 0; k--)
00700 if (zero_crossing[i].crossings[k] != 0)
00701 break;
00702 if (k < 0)
00703 k=0;
00704
00705
00706
00707 correct=(-1);
00708 if (zero_crossing[i+1].crossings[j] != 0)
00709 {
00710 count=0;
00711 for (l=k+1; l < center; l++)
00712 if (zero_crossing[i+1].crossings[l] != 0)
00713 count++;
00714 if (((count % 2) == 0) && (center != k))
00715 correct=center;
00716 }
00717
00718
00719
00720 if (correct == -1)
00721 {
00722 count=0;
00723 for (l=k+1; l < left; l++)
00724 if (zero_crossing[i+1].crossings[l] != 0)
00725 count++;
00726 if (((count % 2) == 0) && (left != k))
00727 correct=left;
00728 }
00729
00730
00731
00732 if (correct == -1)
00733 {
00734 count=0;
00735 for (l=k+1; l < right; l++)
00736 if (zero_crossing[i+1].crossings[l] != 0)
00737 count++;
00738 if (((count % 2) == 0) && (right != k))
00739 correct=right;
00740 }
00741 l=zero_crossing[i].crossings[j];
00742 zero_crossing[i].crossings[j]=0;
00743 if (correct != -1)
00744 zero_crossing[i].crossings[correct]=(short) l;
00745 }
00746 }
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775 static long DefineRegion(const short *extrema,ExtentPacket *extents)
00776 {
00777
00778
00779
00780 extents->left=0;
00781 extents->center=0.0;
00782 extents->right=255;
00783
00784
00785
00786 for ( ; extents->index <= 255; extents->index++)
00787 if (extrema[extents->index] > 0)
00788 break;
00789 if (extents->index > 255)
00790 return(MagickFalse);
00791 extents->left=extents->index;
00792
00793
00794
00795 for ( ; extents->index <= 255; extents->index++)
00796 if (extrema[extents->index] < 0)
00797 break;
00798 extents->right=extents->index-1;
00799 return(MagickTrue);
00800 }
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831 static void DerivativeHistogram(const MagickRealType *histogram,
00832 MagickRealType *derivative)
00833 {
00834 register long
00835 i,
00836 n;
00837
00838
00839
00840
00841 n=255;
00842 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
00843 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
00844
00845
00846
00847 for (i=1; i < n; i++)
00848 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
00849 return;
00850 }
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888 MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
00889 const double cluster_threshold,const double smooth_threshold,
00890 MagickPixelPacket *pixel,ExceptionInfo *exception)
00891 {
00892 Cluster
00893 *background,
00894 *cluster,
00895 *object,
00896 *head,
00897 *last_cluster,
00898 *next_cluster;
00899
00900 ExtentPacket
00901 blue,
00902 green,
00903 red;
00904
00905 long
00906 count,
00907 *histogram[MaxDimension],
00908 y;
00909
00910 MagickBooleanType
00911 proceed;
00912
00913 MagickRealType
00914 threshold;
00915
00916 register const PixelPacket
00917 *p;
00918
00919 register long
00920 i,
00921 x;
00922
00923 short
00924 *extrema[MaxDimension];
00925
00926
00927
00928
00929 assert(image != (Image *) NULL);
00930 assert(image->signature == MagickSignature);
00931 if (image->debug != MagickFalse)
00932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00933 GetMagickPixelPacket(image,pixel);
00934 for (i=0; i < MaxDimension; i++)
00935 {
00936 histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00937 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00938 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
00939 {
00940 for (i-- ; i >= 0; i--)
00941 {
00942 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
00943 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
00944 }
00945 (void) ThrowMagickException(exception,GetMagickModule(),
00946 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00947 return(MagickFalse);
00948 }
00949 }
00950
00951
00952
00953 InitializeHistogram(image,histogram,exception);
00954 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
00955 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
00956 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
00957 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
00958 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
00959 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
00960
00961
00962
00963 cluster=(Cluster *) NULL;
00964 head=(Cluster *) NULL;
00965 (void) ResetMagickMemory(&red,0,sizeof(red));
00966 (void) ResetMagickMemory(&green,0,sizeof(green));
00967 (void) ResetMagickMemory(&blue,0,sizeof(blue));
00968 while (DefineRegion(extrema[Red],&red) != 0)
00969 {
00970 green.index=0;
00971 while (DefineRegion(extrema[Green],&green) != 0)
00972 {
00973 blue.index=0;
00974 while (DefineRegion(extrema[Blue],&blue) != 0)
00975 {
00976
00977
00978
00979 if (head != (Cluster *) NULL)
00980 {
00981 cluster->next=(Cluster *) AcquireMagickMemory(
00982 sizeof(*cluster->next));
00983 cluster=cluster->next;
00984 }
00985 else
00986 {
00987 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
00988 head=cluster;
00989 }
00990 if (cluster == (Cluster *) NULL)
00991 {
00992 (void) ThrowMagickException(exception,GetMagickModule(),
00993 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00994 image->filename);
00995 return(MagickFalse);
00996 }
00997
00998
00999
01000 cluster->count=0;
01001 cluster->red=red;
01002 cluster->green=green;
01003 cluster->blue=blue;
01004 cluster->next=(Cluster *) NULL;
01005 }
01006 }
01007 }
01008 if (head == (Cluster *) NULL)
01009 {
01010
01011
01012
01013 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
01014 if (cluster == (Cluster *) NULL)
01015 {
01016 (void) ThrowMagickException(exception,GetMagickModule(),
01017 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01018 return(MagickFalse);
01019 }
01020
01021
01022
01023 cluster->count=0;
01024 cluster->red=red;
01025 cluster->green=green;
01026 cluster->blue=blue;
01027 cluster->next=(Cluster *) NULL;
01028 head=cluster;
01029 }
01030
01031
01032
01033 count=0;
01034 for (y=0; y < (long) image->rows; y++)
01035 {
01036 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01037 if (p == (const PixelPacket *) NULL)
01038 break;
01039 for (x=0; x < (long) image->columns; x++)
01040 {
01041 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
01042 if (((long) ScaleQuantumToChar(p->red) >=
01043 (cluster->red.left-SafeMargin)) &&
01044 ((long) ScaleQuantumToChar(p->red) <=
01045 (cluster->red.right+SafeMargin)) &&
01046 ((long) ScaleQuantumToChar(p->green) >=
01047 (cluster->green.left-SafeMargin)) &&
01048 ((long) ScaleQuantumToChar(p->green) <=
01049 (cluster->green.right+SafeMargin)) &&
01050 ((long) ScaleQuantumToChar(p->blue) >=
01051 (cluster->blue.left-SafeMargin)) &&
01052 ((long) ScaleQuantumToChar(p->blue) <=
01053 (cluster->blue.right+SafeMargin)))
01054 {
01055
01056
01057
01058 count++;
01059 cluster->red.center+=(MagickRealType)
01060 ScaleQuantumToChar(p->red);
01061 cluster->green.center+=(MagickRealType)
01062 ScaleQuantumToChar(p->green);
01063 cluster->blue.center+=(MagickRealType)
01064 ScaleQuantumToChar(p->blue);
01065 cluster->count++;
01066 break;
01067 }
01068 p++;
01069 }
01070 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
01071 if (proceed == MagickFalse)
01072 break;
01073 }
01074
01075
01076
01077 count=0;
01078 last_cluster=head;
01079 next_cluster=head;
01080 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
01081 {
01082 next_cluster=cluster->next;
01083 if ((cluster->count > 0) &&
01084 (cluster->count >= (count*cluster_threshold/100.0)))
01085 {
01086
01087
01088
01089 cluster->id=count;
01090 cluster->red.center/=cluster->count;
01091 cluster->green.center/=cluster->count;
01092 cluster->blue.center/=cluster->count;
01093 count++;
01094 last_cluster=cluster;
01095 continue;
01096 }
01097
01098
01099
01100 if (cluster == head)
01101 head=next_cluster;
01102 else
01103 last_cluster->next=next_cluster;
01104 cluster=(Cluster *) RelinquishMagickMemory(cluster);
01105 }
01106 object=head;
01107 background=head;
01108