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 CacheView
00245 *image_view;
00246
00247 Cluster
00248 *cluster,
00249 *head,
00250 *last_cluster,
00251 *next_cluster;
00252
00253 ExceptionInfo
00254 *exception;
00255
00256 ExtentPacket
00257 blue,
00258 green,
00259 red;
00260
00261 long
00262 count,
00263 progress,
00264 y;
00265
00266 MagickRealType
00267 *free_squares;
00268
00269 MagickStatusType
00270 status;
00271
00272 register long
00273 i;
00274
00275 register MagickRealType
00276 *squares;
00277
00278 unsigned long
00279 number_clusters;
00280
00281
00282
00283
00284 cluster=(Cluster *) NULL;
00285 head=(Cluster *) NULL;
00286 (void) ResetMagickMemory(&red,0,sizeof(red));
00287 (void) ResetMagickMemory(&green,0,sizeof(green));
00288 (void) ResetMagickMemory(&blue,0,sizeof(blue));
00289 while (DefineRegion(extrema[Red],&red) != 0)
00290 {
00291 green.index=0;
00292 while (DefineRegion(extrema[Green],&green) != 0)
00293 {
00294 blue.index=0;
00295 while (DefineRegion(extrema[Blue],&blue) != 0)
00296 {
00297
00298
00299
00300 if (head != (Cluster *) NULL)
00301 {
00302 cluster->next=(Cluster *) AcquireMagickMemory(
00303 sizeof(*cluster->next));
00304 cluster=cluster->next;
00305 }
00306 else
00307 {
00308 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
00309 head=cluster;
00310 }
00311 if (cluster == (Cluster *) NULL)
00312 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00313 image->filename);
00314
00315
00316
00317 cluster->count=0;
00318 cluster->red=red;
00319 cluster->green=green;
00320 cluster->blue=blue;
00321 cluster->next=(Cluster *) NULL;
00322 }
00323 }
00324 }
00325 if (head == (Cluster *) NULL)
00326 {
00327
00328
00329
00330 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
00331 if (cluster == (Cluster *) NULL)
00332 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00333 image->filename);
00334
00335
00336
00337 cluster->count=0;
00338 cluster->red=red;
00339 cluster->green=green;
00340 cluster->blue=blue;
00341 cluster->next=(Cluster *) NULL;
00342 head=cluster;
00343 }
00344
00345
00346
00347 status=MagickTrue;
00348 count=0;
00349 progress=0;
00350 exception=(&image->exception);
00351 image_view=AcquireCacheView(image);
00352 for (y=0; y < (long) image->rows; y++)
00353 {
00354 register const PixelPacket
00355 *p;
00356
00357 register long
00358 x;
00359
00360 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00361 if (p == (const PixelPacket *) NULL)
00362 break;
00363 for (x=0; x < (long) image->columns; x++)
00364 {
00365 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00366 if (((long) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
00367 (cluster->red.left-SafeMargin)) &&
00368 ((long) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
00369 (cluster->red.right+SafeMargin)) &&
00370 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
00371 (cluster->green.left-SafeMargin)) &&
00372 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
00373 (cluster->green.right+SafeMargin)) &&
00374 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
00375 (cluster->blue.left-SafeMargin)) &&
00376 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
00377 (cluster->blue.right+SafeMargin)))
00378 {
00379
00380
00381
00382 count++;
00383 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(GetRedPixelComponent(p));
00384 cluster->green.center+=(MagickRealType)
00385 ScaleQuantumToChar(GetGreenPixelComponent(p));
00386 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(GetBluePixelComponent(p));
00387 cluster->count++;
00388 break;
00389 }
00390 p++;
00391 }
00392 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00393 {
00394 MagickBooleanType
00395 proceed;
00396
00397 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00398 #pragma omp critical (MagickCore_Classify)
00399 #endif
00400 proceed=SetImageProgress(image,SegmentImageTag,progress++,
00401 2*image->rows);
00402 if (proceed == MagickFalse)
00403 status=MagickFalse;
00404 }
00405 }
00406 image_view=DestroyCacheView(image_view);
00407
00408
00409
00410 count=0;
00411 last_cluster=head;
00412 next_cluster=head;
00413 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
00414 {
00415 next_cluster=cluster->next;
00416 if ((cluster->count > 0) &&
00417 (cluster->count >= (count*cluster_threshold/100.0)))
00418 {
00419
00420
00421
00422 cluster->id=count;
00423 cluster->red.center/=cluster->count;
00424 cluster->green.center/=cluster->count;
00425 cluster->blue.center/=cluster->count;
00426 count++;
00427 last_cluster=cluster;
00428 continue;
00429 }
00430
00431
00432
00433 if (cluster == head)
00434 head=next_cluster;
00435 else
00436 last_cluster->next=next_cluster;
00437 cluster=(Cluster *) RelinquishMagickMemory(cluster);
00438 }
00439 number_clusters=(unsigned long) count;
00440 if (verbose != MagickFalse)
00441 {
00442
00443
00444
00445 (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
00446 (void) fprintf(stdout,"===================\n\n");
00447 (void) fprintf(stdout,"\tCluster Threshold = %g\n",(double)
00448 cluster_threshold);
00449 (void) fprintf(stdout,"\tWeighting Exponent = %g\n",(double)
00450 weighting_exponent);
00451 (void) fprintf(stdout,"\tTotal Number of Clusters = %lu\n\n",
00452 number_clusters);
00453
00454
00455
00456 (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
00457 (void) fprintf(stdout,"=============================\n\n");
00458 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00459 (void) fprintf(stdout,"Cluster #%ld = %ld\n",cluster->id,
00460 cluster->count);
00461
00462
00463
00464 (void) fprintf(stdout,
00465 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
00466 (void) fprintf(stdout,"================");
00467 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00468 {
00469 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
00470 (void) fprintf(stdout,"%ld-%ld %ld-%ld %ld-%ld\n",cluster->red.left,
00471 cluster->red.right,cluster->green.left,cluster->green.right,
00472 cluster->blue.left,cluster->blue.right);
00473 }
00474
00475
00476
00477 (void) fprintf(stdout,
00478 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
00479 (void) fprintf(stdout,"=====================");
00480 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00481 {
00482 (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
00483 (void) fprintf(stdout,"%g %g %g\n",(double)
00484 cluster->red.center,(double) cluster->green.center,(double)
00485 cluster->blue.center);
00486 }
00487 (void) fprintf(stdout,"\n");
00488 }
00489 if (number_clusters > 256)
00490 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
00491
00492
00493
00494 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
00495 if (squares == (MagickRealType *) NULL)
00496 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00497 image->filename);
00498 squares+=255;
00499 for (i=(-255); i <= 255; i++)
00500 squares[i]=(MagickRealType) i*(MagickRealType) i;
00501
00502
00503
00504 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
00505 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00506 image->filename);
00507 i=0;
00508 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00509 {
00510 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
00511 (cluster->red.center+0.5));
00512 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
00513 (cluster->green.center+0.5));
00514 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
00515 (cluster->blue.center+0.5));
00516 i++;
00517 }
00518
00519
00520
00521 exception=(&image->exception);
00522 image_view=AcquireCacheView(image);
00523 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00524 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00525 #endif
00526 for (y=0; y < (long) image->rows; y++)
00527 {
00528 Cluster
00529 *cluster;
00530
00531 register const PixelPacket
00532 *restrict p;
00533
00534 register IndexPacket
00535 *restrict indexes;
00536
00537 register long
00538 x;
00539
00540 register PixelPacket
00541 *restrict q;
00542
00543 if (status == MagickFalse)
00544 continue;
00545 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00546 if (q == (PixelPacket *) NULL)
00547 {
00548 status=MagickFalse;
00549 continue;
00550 }
00551 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00552 for (x=0; x < (long) image->columns; x++)
00553 {
00554 indexes[x]=(IndexPacket) 0;
00555 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00556 {
00557 if (((long) ScaleQuantumToChar(q->red) >=
00558 (cluster->red.left-SafeMargin)) &&
00559 ((long) ScaleQuantumToChar(q->red) <=
00560 (cluster->red.right+SafeMargin)) &&
00561 ((long) ScaleQuantumToChar(q->green) >=
00562 (cluster->green.left-SafeMargin)) &&
00563 ((long) ScaleQuantumToChar(q->green) <=
00564 (cluster->green.right+SafeMargin)) &&
00565 ((long) ScaleQuantumToChar(q->blue) >=
00566 (cluster->blue.left-SafeMargin)) &&
00567 ((long) ScaleQuantumToChar(q->blue) <=
00568 (cluster->blue.right+SafeMargin)))
00569 {
00570
00571
00572
00573 indexes[x]=(IndexPacket) cluster->id;
00574 break;
00575 }
00576 }
00577 if (cluster == (Cluster *) NULL)
00578 {
00579 MagickRealType
00580 distance_squared,
00581 local_minima,
00582 numerator,
00583 ratio,
00584 sum;
00585
00586 register long
00587 j,
00588 k;
00589
00590
00591
00592
00593 local_minima=0.0;
00594 for (j=0; j < (long) image->colors; j++)
00595 {
00596 sum=0.0;
00597 p=image->colormap+j;
00598 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00599 (long) ScaleQuantumToChar(GetRedPixelComponent(p))]+
00600 squares[(long) ScaleQuantumToChar(q->green)-
00601 (long) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
00602 squares[(long) ScaleQuantumToChar(q->blue)-
00603 (long) ScaleQuantumToChar(GetBluePixelComponent(p))];
00604 numerator=distance_squared;
00605 for (k=0; k < (long) image->colors; k++)
00606 {
00607 p=image->colormap+k;
00608 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00609 (long) ScaleQuantumToChar(GetRedPixelComponent(p))]+
00610 squares[(long) ScaleQuantumToChar(q->green)-
00611 (long) ScaleQuantumToChar(GetGreenPixelComponent(p))]+
00612 squares[(long) ScaleQuantumToChar(q->blue)-
00613 (long) ScaleQuantumToChar(GetBluePixelComponent(p))];
00614 ratio=numerator/distance_squared;
00615 sum+=SegmentPower(ratio);
00616 }
00617 if ((sum != 0.0) && ((1.0/sum) > local_minima))
00618 {
00619
00620
00621
00622 local_minima=1.0/sum;
00623 indexes[x]=(IndexPacket) j;
00624 }
00625 }
00626 }
00627 q++;
00628 }
00629 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00630 status=MagickFalse;
00631 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00632 {
00633 MagickBooleanType
00634 proceed;
00635
00636 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00637 #pragma omp critical (MagickCore_Classify)
00638 #endif
00639 proceed=SetImageProgress(image,SegmentImageTag,progress++,
00640 2*image->rows);
00641 if (proceed == MagickFalse)
00642 status=MagickFalse;
00643 }
00644 }
00645 image_view=DestroyCacheView(image_view);
00646 status&=SyncImage(image);
00647
00648
00649
00650 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
00651 {
00652 next_cluster=cluster->next;
00653 cluster=(Cluster *) RelinquishMagickMemory(cluster);
00654 }
00655 squares-=255;
00656 free_squares=squares;
00657 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
00658 return(MagickTrue);
00659 }
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689 static inline long MagickAbsoluteValue(const long x)
00690 {
00691 if (x < 0)
00692 return(-x);
00693 return(x);
00694 }
00695
00696 static inline long MagickMax(const long x,const long y)
00697 {
00698 if (x > y)
00699 return(x);
00700 return(y);
00701 }
00702
00703 static inline long MagickMin(const long x,const long y)
00704 {
00705 if (x < y)
00706 return(x);
00707 return(y);
00708 }
00709
00710 static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
00711 const unsigned long number_crossings)
00712 {
00713 long
00714 center,
00715 correct,
00716 count,
00717 left,
00718 right;
00719
00720 register long
00721 i,
00722 j,
00723 k,
00724 l;
00725
00726
00727
00728
00729 for (i=(long) number_crossings-1; i >= 0; i--)
00730 for (j=0; j <= 255; j++)
00731 {
00732 if (zero_crossing[i].crossings[j] == 0)
00733 continue;
00734
00735
00736
00737
00738
00739 for (k=j-1; k > 0; k--)
00740 if (zero_crossing[i+1].crossings[k] != 0)
00741 break;
00742 left=MagickMax(k,0);
00743 center=j;
00744 for (k=j+1; k < 255; k++)
00745 if (zero_crossing[i+1].crossings[k] != 0)
00746 break;
00747 right=MagickMin(k,255);
00748
00749
00750
00751 for (k=j-1; k > 0; k--)
00752 if (zero_crossing[i].crossings[k] != 0)
00753 break;
00754 if (k < 0)
00755 k=0;
00756
00757
00758
00759 correct=(-1);
00760 if (zero_crossing[i+1].crossings[j] != 0)
00761 {
00762 count=0;
00763 for (l=k+1; l < center; l++)
00764 if (zero_crossing[i+1].crossings[l] != 0)
00765 count++;
00766 if (((count % 2) == 0) && (center != k))
00767 correct=center;
00768 }
00769
00770
00771
00772 if (correct == -1)
00773 {
00774 count=0;
00775 for (l=k+1; l < left; l++)
00776 if (zero_crossing[i+1].crossings[l] != 0)
00777 count++;
00778 if (((count % 2) == 0) && (left != k))
00779 correct=left;
00780 }
00781
00782
00783
00784 if (correct == -1)
00785 {
00786 count=0;
00787 for (l=k+1; l < right; l++)
00788 if (zero_crossing[i+1].crossings[l] != 0)
00789 count++;
00790 if (((count % 2) == 0) && (right != k))
00791 correct=right;
00792 }
00793 l=zero_crossing[i].crossings[j];
00794 zero_crossing[i].crossings[j]=0;
00795 if (correct != -1)
00796 zero_crossing[i].crossings[correct]=(short) l;
00797 }
00798 }
00799
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 static long DefineRegion(const short *extrema,ExtentPacket *extents)
00828 {
00829
00830
00831
00832 extents->left=0;
00833 extents->center=0.0;
00834 extents->right=255;
00835
00836
00837
00838 for ( ; extents->index <= 255; extents->index++)
00839 if (extrema[extents->index] > 0)
00840 break;
00841 if (extents->index > 255)
00842 return(MagickFalse);
00843 extents->left=extents->index;
00844
00845
00846
00847 for ( ; extents->index <= 255; extents->index++)
00848 if (extrema[extents->index] < 0)
00849 break;
00850 extents->right=extents->index-1;
00851 return(MagickTrue);
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 static void DerivativeHistogram(const MagickRealType *histogram,
00884 MagickRealType *derivative)
00885 {
00886 register long
00887 i,
00888 n;
00889
00890
00891
00892
00893 n=255;
00894 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
00895 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
00896
00897
00898
00899 for (i=1; i < n; i++)
00900 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
00901 return;
00902 }
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940 MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
00941 const double cluster_threshold,const double smooth_threshold,
00942 MagickPixelPacket *pixel,ExceptionInfo *exception)
00943 {
00944 Cluster
00945 *background,
00946 *cluster,
00947 *object,
00948 *head,
00949 *last_cluster,
00950 *next_cluster;
00951
00952 ExtentPacket
00953 blue,
00954 green,
00955 red;
00956
00957 long
00958 count,
00959 *histogram[MaxDimension],
00960 y;
00961
00962 MagickBooleanType
00963 proceed;
00964
00965 MagickRealType
00966 threshold;
00967
00968 register const PixelPacket
00969 *p;
00970
00971 register long
00972 i,
00973 x;
00974
00975 short
00976 *extrema[MaxDimension];
00977
00978
00979
00980
00981 assert(image != (Image *) NULL);
00982 assert(image->signature == MagickSignature);
00983 if (image->debug != MagickFalse)
00984 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00985 GetMagickPixelPacket(image,pixel);
00986 for (i=0; i < MaxDimension; i++)
00987 {
00988 histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00989 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00990 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
00991 {
00992 for (i-- ; i >= 0; i--)
00993 {
00994 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
00995 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
00996 }
00997 (void) ThrowMagickException(exception,GetMagickModule(),
00998 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00999 return(MagickFalse);
01000 }
01001 }
01002
01003
01004
01005 InitializeHistogram(image,histogram,exception);
01006 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
01007 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
01008 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
01009 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
01010 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
01011 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
01012
01013
01014
01015 cluster=(Cluster *) NULL;
01016 head=(Cluster *) NULL;
01017 (void) ResetMagickMemory(&red,0,sizeof(red));
01018 (void) ResetMagickMemory(&green,0,sizeof(green));
01019 (void) ResetMagickMemory(&blue,0,sizeof(blue));
01020 while (DefineRegion(extrema[Red],&red) != 0)
01021 {
01022 green.index=0;
01023 while (DefineRegion(extrema[Green],&green) != 0)
01024 {
01025 blue.index=0;
01026 while (DefineRegion(extrema[Blue],&blue) != 0)
01027 {
01028
01029
01030
01031 if (head != (Cluster *) NULL)
01032 {
01033 cluster->next=(Cluster *) AcquireMagickMemory(
01034 sizeof(*cluster->next));
01035 cluster=cluster->next;
01036 }
01037 else
01038 {
01039 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
01040 head=cluster;
01041 }
01042 if (cluster == (Cluster *) NULL)
01043 {
01044 (void) ThrowMagickException(exception,GetMagickModule(),
01045 ResourceLimitError,"MemoryAllocationFailed","`%s'",
01046 image->filename);
01047 return(MagickFalse);
01048 }
01049
01050
01051
01052 cluster->count=0;
01053 cluster->red=red;
01054 cluster->green=green;
01055 cluster->blue=blue;
01056 cluster->next=(Cluster *) NULL;
01057 }
01058 }
01059 }
01060 if (head == (Cluster *) NULL)
01061 {
01062
01063
01064
01065 cluster=(Cluster *) AcquireAlignedMemory(1,sizeof(*cluster));
01066 if (cluster == (Cluster *) NULL)
01067 {
01068 (void) ThrowMagickException(exception,GetMagickModule(),
01069 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01070 return(MagickFalse);
01071 }
01072
01073
01074
01075 cluster->count=0;
01076 cluster->red=red;
01077 cluster->green=green;
01078 cluster->blue=blue;
01079 cluster->next=(Cluster *) NULL;
01080 head=cluster;
01081 }
01082
01083
01084
01085 count=0;
01086 for (y=0; y < (long) image->rows; y++)
01087 {
01088 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01089 if (p == (const PixelPacket *) NULL)
01090 break;
01091 for (x=0; x < (long) image->columns; x++)
01092 {
01093 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
01094 if (((long) ScaleQuantumToChar(GetRedPixelComponent(p)) >=
01095 (cluster->red.left-SafeMargin)) &&
01096 ((long) ScaleQuantumToChar(GetRedPixelComponent(p)) <=
01097 (cluster->red.right+SafeMargin)) &&
01098 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) >=
01099 (cluster->green.left-SafeMargin)) &&
01100 ((long) ScaleQuantumToChar(GetGreenPixelComponent(p)) <=
01101 (cluster->green.right+SafeMargin)) &&
01102 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) >=
01103 (cluster->blue.left-SafeMargin)) &&
01104 ((long) ScaleQuantumToChar(GetBluePixelComponent(p)) <=
01105 (cluster->blue.right+SafeMargin)))
01106 {
01107
01108
01109
01110 count++;
01111 cluster->red.center+=(MagickRealType)
01112 ScaleQuantumToChar(GetRedPixelComponent(p));
01113 cluster->green.center+=(MagickRealType)
01114 ScaleQuantumToChar(GetGreenPixelComponent(p));
01115 cluster->blue.center+=(MagickRealType)
01116 ScaleQuantumToChar(GetBluePixelComponent(p));
01117 cluster->count++;
01118 break;
01119 }
01120 p++;
01121 }
01122 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
01123 if (proceed == MagickFalse)
01124 break;
01125 }
01126
01127
01128
01129 count=0;
01130 last_cluster=head;
01131 next_cluster=head;
01132 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
01133 {
01134 next_cluster=cluster->next;
01135 if ((cluster->count > 0) &&
01136 (cluster->count >= (count*cluster_threshold/100.0)))
01137 {
01138
01139
01140
01141 cluster->id=count;
01142 cluster->red.center/=cluster->count;
01143 cluster->green.center/=cluster->count;
01144 cluster->blue.center/=cluster->count;
01145 count++;
01146 last_cluster=cluster;
01147 continue;
01148 }
01149
01150
01151
01152 if (cluster == head)
01153 head=next_cluster;
01154 else
01155 last_cluster->next=next_cluster;
01156 cluster=(Cluster *) RelinquishMagickMemory(cluster);
01157 }
01158 object=head;
01159 background=head;
01160 if (count > 1)
01161 {
01162 object=head->next;
01163 for (cluster=object; cluster->next != (Cluster *) NULL; )
01164 {
01165 if (cluster->count < object->count)
01166 object=cluster;
01167 cluster=cluster->next;
01168 }
01169 background=head->next;
01170 for (cluster=background; cluster->next != (Cluster *) NULL; )
01171 {
01172 if (cluster->count > background->count)
01173 background=cluster;
01174 cluster=cluster->next;
01175 }
01176 }
01177 threshold=(background->red.center+object->red.center)/2.0;
01178 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
01179 (threshold+0.5));
01180 threshold=(background->green.center+object->green.center)/2.0;
01181 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
01182 (threshold+0.5));
01183 threshold=(background->blue.center+object->blue.center)/2.0;
01184 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
01185 (threshold+0.5));
01186
01187
01188
01189 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
01190 {
01191 next_cluster=cluster->next;
01192 cluster=(Cluster *) RelinquishMagickMemory(cluster);
01193 }
01194 for (i=0; i < MaxDimension; i++)
01195 {
01196 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01197 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01198 }
01199 return(MagickTrue);
01200 }
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228 static void InitializeHistogram(const Image *image,long **histogram,
01229 ExceptionInfo *exception)
01230 {
01231 long
01232 y;
01233
01234 register const PixelPacket
01235 *p;
01236
01237 register long
01238 i,
01239 x;
01240
01241
01242
01243
01244 for (i=0; i <= 255; i++)
01245 {
01246 histogram[Red][i]=0;
01247 histogram[Green][i]=0;
01248 histogram[Blue][i]=0;
01249 }
01250 for (y=0; y < (long) image->rows; y++)
01251 {
01252 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01253 if (p == (const PixelPacket *) NULL)
01254 break;
01255 for (x=0; x < (long) image->columns; x++)
01256 {
01257 histogram[Red][(long) ScaleQuantumToChar(GetRedPixelComponent(p))]++;
01258 histogram[Green][(long) ScaleQuantumToChar(GetGreenPixelComponent(p))]++;
01259 histogram[Blue][(long) ScaleQuantumToChar(GetBluePixelComponent(p))]++;
01260 p++;
01261 }
01262 }
01263 }
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293 static void InitializeList(IntervalTree **list,long *number_nodes,
01294 IntervalTree *node)
01295 {
01296 if (node == (IntervalTree *) NULL)
01297 return;
01298 if (node->child == (IntervalTree *) NULL)
01299 list[(*number_nodes)++]=node;
01300 InitializeList(list,number_nodes,node->sibling);
01301 InitializeList(list,number_nodes,node->child);
01302 }
01303
01304 static void MeanStability(IntervalTree *node)
01305 {
01306 register IntervalTree
01307 *child;
01308
01309 if (node == (IntervalTree *) NULL)
01310 return;
01311 node->mean_stability=0.0;
01312 child=node->child;
01313 if (child != (IntervalTree *) NULL)
01314 {
01315 register long
01316 count;
01317
01318 register MagickRealType
01319 sum;
01320
01321 sum=0.0;
01322 count=0;
01323 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
01324 {
01325 sum+=child->stability;
01326 count++;
01327 }
01328 node->mean_stability=sum/(MagickRealType) count;
01329 }
01330 MeanStability(node->sibling);
01331 MeanStability(node->child);
01332 }
01333
01334 static void Stability(IntervalTree *node)
01335 {
01336 if (node == (IntervalTree *) NULL)
01337 return;
01338 if (node->child == (IntervalTree *) NULL)
01339 node->stability=0.0;
01340 else
01341 node->stability=node->tau-(node->child)->tau;
01342 Stability(node->sibling);
01343 Stability(node->child);
01344 }
01345
01346 static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
01347 const unsigned long number_crossings)
01348 {
01349 IntervalTree
01350 *head,
01351 **list,
01352 *node,
01353 *root;
01354
01355 long
01356 j,
01357 k,
01358 left,
01359 number_nodes;
01360
01361 register long
01362 i;
01363
01364
01365
01366
01367 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
01368 sizeof(*list));
01369 if (list == (IntervalTree **) NULL)
01370 return((IntervalTree *) NULL);
01371
01372
01373
01374 root=(IntervalTree *) AcquireAlignedMemory(1,sizeof(*root));
01375 root->child=(IntervalTree *) NULL;
01376 root->sibling=(IntervalTree *) NULL;
01377 root->tau=0.0;
01378 root->left=0;
01379 root->right=255;
01380 for (i=(-1); i < (long) number_crossings; i++)
01381 {
01382
01383
01384
01385 number_nodes=0;
01386 InitializeList(list,&number_nodes,root);
01387
01388
01389
01390 for (j=0; j < number_nodes; j++)
01391 {
01392 head=list[j];
01393 left=head->left;
01394 node=head;
01395 for (k=head->left+1; k < head->right; k++)
01396 {
01397 if (zero_crossing[i+1].crossings[k] != 0)
01398 {
01399 if (node == head)
01400 {
01401 node->child=(IntervalTree *) AcquireMagickMemory(
01402 sizeof(*node->child));
01403 node=node->child;
01404 }
01405 else
01406 {
01407 node->sibling=(IntervalTree *) AcquireMagickMemory(
01408 sizeof(*node->sibling));
01409 node=node->sibling;
01410 }
01411 node->tau=zero_crossing[i+1].tau;
01412 node->child=(IntervalTree *) NULL;
01413 node->sibling=(IntervalTree *) NULL;
01414 node->left=left;
01415 node->right=k;
01416 left=k;
01417 }
01418 }
01419 if (left != head->left)
01420 {
01421 node->sibling=(IntervalTree *) AcquireMagickMemory(
01422 sizeof(*node->sibling));
01423 node=node->sibling;
01424 node->tau=zero_crossing[i+1].tau;
01425 node->child=(IntervalTree *) NULL;
01426 node->sibling=(IntervalTree *) NULL;
01427 node->left=left;
01428 node->right=head->right;
01429 }
01430 }
01431 }
01432
01433
01434
01435 Stability(root->child);
01436 MeanStability(root->child);
01437 list=(IntervalTree **) RelinquishMagickMemory(list);
01438 return(root);
01439 }
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471 static void ActiveNodes(IntervalTree **list,long *number_nodes,
01472 IntervalTree *node)
01473 {
01474 if (node == (IntervalTree *) NULL)
01475 return;
01476 if (node->stability >= node->mean_stability)
01477 {
01478 list[(*number_nodes)++]=node;
01479 ActiveNodes(list,number_nodes,node->sibling);
01480 }
01481 else
01482 {
01483 ActiveNodes(list,number_nodes,node->sibling);
01484 ActiveNodes(list,number_nodes,node->child);
01485 }
01486 }
01487
01488 static void FreeNodes(IntervalTree *node)
01489 {
01490 if (node == (IntervalTree *) NULL)
01491 return;
01492 FreeNodes(node->sibling);
01493 FreeNodes(node->child);
01494 node=(IntervalTree *) RelinquishMagickMemory(node);
01495 }
01496
01497 static MagickRealType OptimalTau(const long *histogram,const double max_tau,
01498 const double min_tau,const double delta_tau,const double smooth_threshold,
01499 short *extrema)
01500 {
01501 IntervalTree
01502 **list,
01503 *node,
01504 *root;
01505
01506 long
01507 index,
01508 j,
01509 k,
01510 number_nodes;
01511
01512 MagickRealType
01513 average_tau,
01514 *derivative,
01515 *second_derivative,
01516 tau,
01517 value;
01518
01519 register long
01520 i,
01521 x;
01522
01523 MagickBooleanType
01524 peak;
01525
01526 unsigned long
01527 count,
01528 number_crossings;
01529
01530 ZeroCrossing
01531 *zero_crossing;
01532
01533
01534
01535
01536 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
01537 sizeof(*list));
01538 if (list == (IntervalTree **) NULL)
01539 return(0.0);
01540
01541
01542
01543 count=(unsigned long) ((max_tau-min_tau)/delta_tau)+2;
01544 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
01545 sizeof(*zero_crossing));
01546 if (zero_crossing == (ZeroCrossing *) NULL)
01547 return(0.0);
01548 for (i=0; i < (long) count; i++)
01549 zero_crossing[i].tau=(-1.0);
01550
01551
01552
01553 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
01554 second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
01555 sizeof(*second_derivative));
01556 if ((derivative == (MagickRealType *) NULL) ||
01557 (second_derivative == (MagickRealType *) NULL))
01558 ThrowFatalException(ResourceLimitFatalError,
01559 "UnableToAllocateDerivatives");
01560 i=0;
01561 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
01562 {
01563 zero_crossing[i].tau=tau;
01564 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
01565 DerivativeHistogram(zero_crossing[i].histogram,derivative);
01566 DerivativeHistogram(derivative,second_derivative);
01567 ZeroCrossHistogram(second_derivative,smooth_threshold,
01568 zero_crossing[i].crossings);
01569 i++;
01570 }
01571
01572
01573
01574 zero_crossing[i].tau=0.0;
01575 for (j=0; j <= 255; j++)
01576 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
01577 DerivativeHistogram(zero_crossing[i].histogram,derivative);
01578 DerivativeHistogram(derivative,second_derivative);
01579 ZeroCrossHistogram(second_derivative,smooth_threshold,
01580 zero_crossing[i].crossings);
01581 number_crossings=(unsigned long) i;
01582 derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
01583 second_derivative=(MagickRealType *)
01584 RelinquishMagickMemory(second_derivative);
01585
01586
01587
01588 ConsolidateCrossings(zero_crossing,number_crossings);
01589
01590
01591
01592 for (i=0; i <= (long) number_crossings; i++)
01593 {
01594 for (j=0; j < 255; j++)
01595 if (zero_crossing[i].crossings[j] != 0)
01596 break;
01597 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
01598 for (j=255; j > 0; j--)
01599 if (zero_crossing[i].crossings[j] != 0)
01600 break;
01601 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
01602 }
01603
01604
01605
01606 root=InitializeIntervalTree(zero_crossing,number_crossings);
01607 if (root == (IntervalTree *) NULL)
01608 return(0.0);
01609
01610
01611
01612
01613 number_nodes=0;
01614 ActiveNodes(list,&number_nodes,root->child);
01615
01616
01617
01618 for (i=0; i <= 255; i++)
01619 extrema[i]=0;
01620 for (i=0; i < number_nodes; i++)
01621 {
01622
01623
01624
01625 k=0;
01626 node=list[i];
01627 for (j=0; j <= (long) number_crossings; j++)
01628 if (zero_crossing[j].tau == node->tau)
01629 k=j;
01630
01631
01632
01633 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
01634 MagickFalse;
01635 index=node->left;
01636 value=zero_crossing[k].histogram[index];
01637 for (x=node->left; x <= node->right; x++)
01638 {
01639 if (peak != MagickFalse)
01640 {
01641 if (zero_crossing[k].histogram[x] > value)
01642 {
01643 value=zero_crossing[k].histogram[x];
01644 index=x;
01645 }
01646 }
01647 else
01648 if (zero_crossing[k].histogram[x] < value)
01649 {
01650 value=zero_crossing[k].histogram[x];
01651 index=x;
01652 }
01653 }
01654 for (x=node->left; x <= node->right; x++)
01655 {
01656 if (index == 0)
01657 index=256;
01658 if (peak != MagickFalse)
01659 extrema[x]=(short) index;
01660 else
01661 extrema[x]=(short) (-index);
01662 }
01663 }
01664
01665
01666
01667 average_tau=0.0;
01668 for (i=0; i < number_nodes; i++)
01669 average_tau+=list[i]->tau;
01670 average_tau/=(MagickRealType) number_nodes;
01671
01672
01673
01674 FreeNodes(root);
01675 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
01676 list=(IntervalTree **) RelinquishMagickMemory(list);
01677 return(average_tau);
01678 }
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705 static void ScaleSpace(const long *histogram,const MagickRealType tau,
01706 MagickRealType *scale_histogram)
01707 {
01708 MagickRealType
01709 alpha,
01710 beta,
01711 *gamma,
01712 sum;
01713
01714 register long
01715 u,
01716 x;
01717
01718 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
01719 if (gamma == (MagickRealType *) NULL)
01720 ThrowFatalException(ResourceLimitFatalError,
01721 "UnableToAllocateGammaMap");
01722 alpha=1.0/(tau*sqrt(2.0*MagickPI));
01723 beta=(-1.0/(2.0*tau*tau));
01724 for (x=0; x <= 255; x++)
01725 gamma[x]=0.0;
01726 for (x=0; x <= 255; x++)
01727 {
01728 gamma[x]=exp((double) beta*x*x);
01729 if (gamma[x] < MagickEpsilon)
01730 break;
01731 }
01732 for (x=0; x <= 255; x++)
01733 {
01734 sum=0.0;
01735 for (u=0; u <= 255; u++)
01736 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
01737 scale_histogram[x]=alpha*sum;
01738 }
01739 gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
01740 }
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781 MagickExport MagickBooleanType SegmentImage(Image *image,
01782 const ColorspaceType colorspace,const MagickBooleanType verbose,
01783 const double cluster_threshold,const double smooth_threshold)
01784 {
01785 long
01786 *histogram[MaxDimension];
01787
01788 MagickBooleanType
01789 status;
01790
01791 register long
01792 i;
01793
01794 short
01795 *extrema[MaxDimension];
01796
01797
01798
01799
01800 assert(image != (Image *) NULL);
01801 assert(image->signature == MagickSignature);
01802 if (image->debug != MagickFalse)
01803 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01804 for (i=0; i < MaxDimension; i++)
01805 {
01806 histogram[i]=(long *) AcquireQuantumMemory(256,sizeof(**histogram));
01807 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
01808 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
01809 {
01810 for (i-- ; i >= 0; i--)
01811 {
01812 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01813 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01814 }
01815 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01816 image->filename)
01817 }
01818 }
01819 if (colorspace != RGBColorspace)
01820 (void) TransformImageColorspace(image,colorspace);
01821
01822
01823
01824 InitializeHistogram(image,histogram,&image->exception);
01825 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
01826 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
01827 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
01828 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
01829 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
01830 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
01831
01832
01833
01834 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
01835 if (colorspace != RGBColorspace)
01836 (void) TransformImageColorspace(image,colorspace);
01837
01838
01839
01840 for (i=0; i < MaxDimension; i++)
01841 {
01842 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01843 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01844 }
01845 return(status);
01846 }
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
01878 static void ZeroCrossHistogram(MagickRealType *second_derivative,
01879 const MagickRealType smooth_threshold,short *crossings)
01880 {
01881 long
01882 parity;
01883
01884 register long
01885 i;
01886
01887
01888
01889
01890 for (i=0; i <= 255; i++)
01891 if ((second_derivative[i] < smooth_threshold) &&
01892 (second_derivative[i] >= -smooth_threshold))
01893 second_derivative[i]=0.0;
01894
01895
01896
01897 parity=0;
01898 for (i=0; i <= 255; i++)
01899 {
01900 crossings[i]=0;
01901 if (second_derivative[i] < 0.0)
01902 {
01903 if (parity > 0)
01904 crossings[i]=(-1);
01905 parity=1;
01906 }
01907 else
01908 if (second_derivative[i] > 0.0)
01909 {
01910 if (parity < 0)
01911 crossings[i]=1;
01912 parity=(-1);
01913 }
01914 }
01915 }