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 long
00259 count,
00260 progress,
00261 y;
00262
00263 MagickRealType
00264 *free_squares;
00265
00266 MagickStatusType
00267 status;
00268
00269 register long
00270 i;
00271
00272 register MagickRealType
00273 *squares;
00274
00275 unsigned long
00276 number_clusters;
00277
00278 CacheView
00279 *image_view;
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 *) AcquireMagickMemory(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 *) AcquireMagickMemory(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(p->red) >=
00367 (cluster->red.left-SafeMargin)) &&
00368 ((long) ScaleQuantumToChar(p->red) <=
00369 (cluster->red.right+SafeMargin)) &&
00370 ((long) ScaleQuantumToChar(p->green) >=
00371 (cluster->green.left-SafeMargin)) &&
00372 ((long) ScaleQuantumToChar(p->green) <=
00373 (cluster->green.right+SafeMargin)) &&
00374 ((long) ScaleQuantumToChar(p->blue) >=
00375 (cluster->blue.left-SafeMargin)) &&
00376 ((long) ScaleQuantumToChar(p->blue) <=
00377 (cluster->blue.right+SafeMargin)))
00378 {
00379
00380
00381
00382 count++;
00383 cluster->red.center+=(MagickRealType) ScaleQuantumToChar(p->red);
00384 cluster->green.center+=(MagickRealType)
00385 ScaleQuantumToChar(p->green);
00386 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(p->blue);
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) cluster->red.center,
00484 (double) cluster->green.center,(double) cluster->blue.center);
00485 }
00486 (void) fprintf(stdout,"\n");
00487 }
00488 if (number_clusters > 256)
00489 ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
00490
00491
00492
00493 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
00494 if (squares == (MagickRealType *) NULL)
00495 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00496 image->filename);
00497 squares+=255;
00498 for (i=(-255); i <= 255; i++)
00499 squares[i]=(MagickRealType) i*(MagickRealType) i;
00500
00501
00502
00503 if (AcquireImageColormap(image,number_clusters) == MagickFalse)
00504 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00505 image->filename);
00506 i=0;
00507 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00508 {
00509 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
00510 (cluster->red.center+0.5));
00511 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
00512 (cluster->green.center+0.5));
00513 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
00514 (cluster->blue.center+0.5));
00515 i++;
00516 }
00517
00518
00519
00520 exception=(&image->exception);
00521 image_view=AcquireCacheView(image);
00522 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00523 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00524 #endif
00525 for (y=0; y < (long) image->rows; y++)
00526 {
00527 Cluster
00528 *cluster;
00529
00530 register const PixelPacket
00531 *__restrict p;
00532
00533 register IndexPacket
00534 *__restrict indexes;
00535
00536 register long
00537 x;
00538
00539 register PixelPacket
00540 *__restrict q;
00541
00542 if (status == MagickFalse)
00543 continue;
00544 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00545 if (q == (PixelPacket *) NULL)
00546 {
00547 status=MagickFalse;
00548 continue;
00549 }
00550 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00551 for (x=0; x < (long) image->columns; x++)
00552 {
00553 indexes[x]=(IndexPacket) 0;
00554 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
00555 {
00556 if (((long) ScaleQuantumToChar(q->red) >=
00557 (cluster->red.left-SafeMargin)) &&
00558 ((long) ScaleQuantumToChar(q->red) <=
00559 (cluster->red.right+SafeMargin)) &&
00560 ((long) ScaleQuantumToChar(q->green) >=
00561 (cluster->green.left-SafeMargin)) &&
00562 ((long) ScaleQuantumToChar(q->green) <=
00563 (cluster->green.right+SafeMargin)) &&
00564 ((long) ScaleQuantumToChar(q->blue) >=
00565 (cluster->blue.left-SafeMargin)) &&
00566 ((long) ScaleQuantumToChar(q->blue) <=
00567 (cluster->blue.right+SafeMargin)))
00568 {
00569
00570
00571
00572 indexes[x]=(IndexPacket) cluster->id;
00573 break;
00574 }
00575 }
00576 if (cluster == (Cluster *) NULL)
00577 {
00578 MagickRealType
00579 distance_squared,
00580 local_minima,
00581 numerator,
00582 ratio,
00583 sum;
00584
00585 register long
00586 j,
00587 k;
00588
00589
00590
00591
00592 local_minima=0.0;
00593 for (j=0; j < (long) image->colors; j++)
00594 {
00595 sum=0.0;
00596 p=image->colormap+j;
00597 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00598 (long) ScaleQuantumToChar(p->red)]+
00599 squares[(long) ScaleQuantumToChar(q->green)-
00600 (long) ScaleQuantumToChar(p->green)]+
00601 squares[(long) ScaleQuantumToChar(q->blue)-
00602 (long) ScaleQuantumToChar(p->blue)];
00603 numerator=distance_squared;
00604 for (k=0; k < (long) image->colors; k++)
00605 {
00606 p=image->colormap+k;
00607 distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
00608 (long) ScaleQuantumToChar(p->red)]+
00609 squares[(long) ScaleQuantumToChar(q->green)-
00610 (long) ScaleQuantumToChar(p->green)]+
00611 squares[(long) ScaleQuantumToChar(q->blue)-
00612 (long) ScaleQuantumToChar(p->blue)];
00613 ratio=numerator/distance_squared;
00614 sum+=SegmentPower(ratio);
00615 }
00616 if ((sum != 0.0) && ((1.0/sum) > local_minima))
00617 {
00618
00619
00620
00621 local_minima=1.0/sum;
00622 indexes[x]=(IndexPacket) j;
00623 }
00624 }
00625 }
00626 q++;
00627 }
00628 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00629 status=MagickFalse;
00630 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00631 {
00632 MagickBooleanType
00633 proceed;
00634
00635 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00636 #pragma omp critical (MagickCore_Classify)
00637 #endif
00638 proceed=SetImageProgress(image,SegmentImageTag,progress++,
00639 2*image->rows);
00640 if (proceed == MagickFalse)
00641 status=MagickFalse;
00642 }
00643 }
00644 image_view=DestroyCacheView(image_view);
00645 status&=SyncImage(image);
00646
00647
00648
00649 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
00650 {
00651 next_cluster=cluster->next;
00652 cluster=(Cluster *) RelinquishMagickMemory(cluster);
00653 }
00654 squares-=255;
00655 free_squares=squares;
00656 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
00657 return(MagickTrue);
00658 }
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 static inline long MagickAbsoluteValue(const long x)
00689 {
00690 if (x < 0)
00691 return(-x);
00692 return(x);
00693 }
00694
00695 static inline long MagickMax(const long x,const long y)
00696 {
00697 if (x > y)
00698 return(x);
00699 return(y);
00700 }
00701
00702 static inline long MagickMin(const long x,const long y)
00703 {
00704 if (x < y)
00705 return(x);
00706 return(y);
00707 }
00708
00709 static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
00710 const unsigned long number_crossings)
00711 {
00712 long
00713 center,
00714 correct,
00715 count,
00716 left,
00717 right;
00718
00719 register long
00720 i,
00721 j,
00722 k,
00723 l;
00724
00725
00726
00727
00728 for (i=(long) number_crossings-1; i >= 0; i--)
00729 for (j=0; j <= 255; j++)
00730 {
00731 if (zero_crossing[i].crossings[j] == 0)
00732 continue;
00733
00734
00735
00736
00737
00738 for (k=j-1; k > 0; k--)
00739 if (zero_crossing[i+1].crossings[k] != 0)
00740 break;
00741 left=MagickMax(k,0);
00742 center=j;
00743 for (k=j+1; k < 255; k++)
00744 if (zero_crossing[i+1].crossings[k] != 0)
00745 break;
00746 right=MagickMin(k,255);
00747
00748
00749
00750 for (k=j-1; k > 0; k--)
00751 if (zero_crossing[i].crossings[k] != 0)
00752 break;
00753 if (k < 0)
00754 k=0;
00755
00756
00757
00758 correct=(-1);
00759 if (zero_crossing[i+1].crossings[j] != 0)
00760 {
00761 count=0;
00762 for (l=k+1; l < center; l++)
00763 if (zero_crossing[i+1].crossings[l] != 0)
00764 count++;
00765 if (((count % 2) == 0) && (center != k))
00766 correct=center;
00767 }
00768
00769
00770
00771 if (correct == -1)
00772 {
00773 count=0;
00774 for (l=k+1; l < left; l++)
00775 if (zero_crossing[i+1].crossings[l] != 0)
00776 count++;
00777 if (((count % 2) == 0) && (left != k))
00778 correct=left;
00779 }
00780
00781
00782
00783 if (correct == -1)
00784 {
00785 count=0;
00786 for (l=k+1; l < right; l++)
00787 if (zero_crossing[i+1].crossings[l] != 0)
00788 count++;
00789 if (((count % 2) == 0) && (right != k))
00790 correct=right;
00791 }
00792 l=zero_crossing[i].crossings[j];
00793 zero_crossing[i].crossings[j]=0;
00794 if (correct != -1)
00795 zero_crossing[i].crossings[correct]=(short) l;
00796 }
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 static long DefineRegion(const short *extrema,ExtentPacket *extents)
00827 {
00828
00829
00830
00831 extents->left=0;
00832 extents->center=0.0;
00833 extents->right=255;
00834
00835
00836
00837 for ( ; extents->index <= 255; extents->index++)
00838 if (extrema[extents->index] > 0)
00839 break;
00840 if (extents->index > 255)
00841 return(MagickFalse);
00842 extents->left=extents->index;
00843
00844
00845
00846 for ( ; extents->index <= 255; extents->index++)
00847 if (extrema[extents->index] < 0)
00848 break;
00849 extents->right=extents->index-1;
00850 return(MagickTrue);
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 static void DerivativeHistogram(const MagickRealType *histogram,
00883 MagickRealType *derivative)
00884 {
00885 register long
00886 i,
00887 n;
00888
00889
00890
00891
00892 n=255;
00893 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
00894 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
00895
00896
00897
00898 for (i=1; i < n; i++)
00899 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
00900 return;
00901 }
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 MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
00940 const double cluster_threshold,const double smooth_threshold,
00941 MagickPixelPacket *pixel,ExceptionInfo *exception)
00942 {
00943 Cluster
00944 *background,
00945 *cluster,
00946 *object,
00947 *head,
00948 *last_cluster,
00949 *next_cluster;
00950
00951 ExtentPacket
00952 blue,
00953 green,
00954 red;
00955
00956 long
00957 count,
00958 *histogram[MaxDimension],
00959 y;
00960
00961 MagickBooleanType
00962 proceed;
00963
00964 MagickRealType
00965 threshold;
00966
00967 register const PixelPacket
00968 *p;
00969
00970 register long
00971 i,
00972 x;
00973
00974 short
00975 *extrema[MaxDimension];
00976
00977
00978
00979
00980 assert(image != (Image *) NULL);
00981 assert(image->signature == MagickSignature);
00982 if (image->debug != MagickFalse)
00983 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00984 GetMagickPixelPacket(image,pixel);
00985 for (i=0; i < MaxDimension; i++)
00986 {
00987 histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00988 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
00989 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
00990 {
00991 for (i-- ; i >= 0; i--)
00992 {
00993 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
00994 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
00995 }
00996 (void) ThrowMagickException(exception,GetMagickModule(),
00997 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00998 return(MagickFalse);
00999 }
01000 }
01001
01002
01003
01004 InitializeHistogram(image,histogram,exception);
01005 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
01006 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
01007 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
01008 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
01009 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
01010 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
01011
01012
01013
01014 cluster=(Cluster *) NULL;
01015 head=(Cluster *) NULL;
01016 (void) ResetMagickMemory(&red,0,sizeof(red));
01017 (void) ResetMagickMemory(&green,0,sizeof(green));
01018 (void) ResetMagickMemory(&blue,0,sizeof(blue));
01019 while (DefineRegion(extrema[Red],&red) != 0)
01020 {
01021 green.index=0;
01022 while (DefineRegion(extrema[Green],&green) != 0)
01023 {
01024 blue.index=0;
01025 while (DefineRegion(extrema[Blue],&blue) != 0)
01026 {
01027
01028
01029
01030 if (head != (Cluster *) NULL)
01031 {
01032 cluster->next=(Cluster *) AcquireMagickMemory(
01033 sizeof(*cluster->next));
01034 cluster=cluster->next;
01035 }
01036 else
01037 {
01038 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
01039 head=cluster;
01040 }
01041 if (cluster == (Cluster *) NULL)
01042 {
01043 (void) ThrowMagickException(exception,GetMagickModule(),
01044 ResourceLimitError,"MemoryAllocationFailed","`%s'",
01045 image->filename);
01046 return(MagickFalse);
01047 }
01048
01049
01050
01051 cluster->count=0;
01052 cluster->red=red;
01053 cluster->green=green;
01054 cluster->blue=blue;
01055 cluster->next=(Cluster *) NULL;
01056 }
01057 }
01058 }
01059 if (head == (Cluster *) NULL)
01060 {
01061
01062
01063
01064 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
01065 if (cluster == (Cluster *) NULL)
01066 {
01067 (void) ThrowMagickException(exception,GetMagickModule(),
01068 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01069 return(MagickFalse);
01070 }
01071
01072
01073
01074 cluster->count=0;
01075 cluster->red=red;
01076 cluster->green=green;
01077 cluster->blue=blue;
01078 cluster->next=(Cluster *) NULL;
01079 head=cluster;
01080 }
01081
01082
01083
01084 count=0;
01085 for (y=0; y < (long) image->rows; y++)
01086 {
01087 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01088 if (p == (const PixelPacket *) NULL)
01089 break;
01090 for (x=0; x < (long) image->columns; x++)
01091 {
01092 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
01093 if (((long) ScaleQuantumToChar(p->red) >=
01094 (cluster->red.left-SafeMargin)) &&
01095 ((long) ScaleQuantumToChar(p->red) <=
01096 (cluster->red.right+SafeMargin)) &&
01097 ((long) ScaleQuantumToChar(p->green) >=
01098 (cluster->green.left-SafeMargin)) &&
01099 ((long) ScaleQuantumToChar(p->green) <=
01100 (cluster->green.right+SafeMargin)) &&
01101 ((long) ScaleQuantumToChar(p->blue) >=
01102 (cluster->blue.left-SafeMargin)) &&
01103 ((long) ScaleQuantumToChar(p->blue) <=
01104 (cluster->blue.right+SafeMargin)))
01105 {
01106
01107
01108
01109 count++;
01110 cluster->red.center+=(MagickRealType)
01111 ScaleQuantumToChar(p->red);
01112 cluster->green.center+=(MagickRealType)
01113 ScaleQuantumToChar(p->green);
01114 cluster->blue.center+=(MagickRealType)
01115 ScaleQuantumToChar(p->blue);
01116 cluster->count++;
01117 break;
01118 }
01119 p++;
01120 }
01121 proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
01122 if (proceed == MagickFalse)
01123 break;
01124 }
01125
01126
01127
01128 count=0;
01129 last_cluster=head;
01130 next_cluster=head;
01131 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
01132 {
01133 next_cluster=cluster->next;
01134 if ((cluster->count > 0) &&
01135 (cluster->count >= (count*cluster_threshold/100.0)))
01136 {
01137
01138
01139
01140 cluster->id=count;
01141 cluster->red.center/=cluster->count;
01142 cluster->green.center/=cluster->count;
01143 cluster->blue.center/=cluster->count;
01144 count++;
01145 last_cluster=cluster;
01146 continue;
01147 }
01148
01149
01150
01151 if (cluster == head)
01152 head=next_cluster;
01153 else
01154 last_cluster->next=next_cluster;
01155 cluster=(Cluster *) RelinquishMagickMemory(cluster);
01156 }
01157 object=head;
01158 background=head;
01159 if (count > 1)
01160 {
01161 object=head->next;
01162 for (cluster=object; cluster->next != (Cluster *) NULL; )
01163 {
01164 if (cluster->count < object->count)
01165 object=cluster;
01166 cluster=cluster->next;
01167 }
01168 background=head->next;
01169 for (cluster=background; cluster->next != (Cluster *) NULL; )
01170 {
01171 if (cluster->count > background->count)
01172 background=cluster;
01173 cluster=cluster->next;
01174 }
01175 }
01176 threshold=(background->red.center+object->red.center)/2.0;
01177 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
01178 (threshold+0.5));
01179 threshold=(background->green.center+object->green.center)/2.0;
01180 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
01181 (threshold+0.5));
01182 threshold=(background->blue.center+object->blue.center)/2.0;
01183 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
01184 (threshold+0.5));
01185
01186
01187
01188 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
01189 {
01190 next_cluster=cluster->next;
01191 cluster=(Cluster *) RelinquishMagickMemory(cluster);
01192 }
01193 for (i=0; i < MaxDimension; i++)
01194 {
01195 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01196 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01197 }
01198 return(MagickTrue);
01199 }
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 static void InitializeHistogram(const Image *image,long **histogram,
01228 ExceptionInfo *exception)
01229 {
01230 long
01231 y;
01232
01233 register const PixelPacket
01234 *p;
01235
01236 register long
01237 i,
01238 x;
01239
01240
01241
01242
01243 for (i=0; i <= 255; i++)
01244 {
01245 histogram[Red][i]=0;
01246 histogram[Green][i]=0;
01247 histogram[Blue][i]=0;
01248 }
01249 for (y=0; y < (long) image->rows; y++)
01250 {
01251 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01252 if (p == (const PixelPacket *) NULL)
01253 break;
01254 for (x=0; x < (long) image->columns; x++)
01255 {
01256 histogram[Red][(long) ScaleQuantumToChar(p->red)]++;
01257 histogram[Green][(long) ScaleQuantumToChar(p->green)]++;
01258 histogram[Blue][(long) ScaleQuantumToChar(p->blue)]++;
01259 p++;
01260 }
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 static void InitializeList(IntervalTree **list,long *number_nodes,
01293 IntervalTree *node)
01294 {
01295 if (node == (IntervalTree *) NULL)
01296 return;
01297 if (node->child == (IntervalTree *) NULL)
01298 list[(*number_nodes)++]=node;
01299 InitializeList(list,number_nodes,node->sibling);
01300 InitializeList(list,number_nodes,node->child);
01301 }
01302
01303 static void MeanStability(IntervalTree *node)
01304 {
01305 register IntervalTree
01306 *child;
01307
01308 if (node == (IntervalTree *) NULL)
01309 return;
01310 node->mean_stability=0.0;
01311 child=node->child;
01312 if (child != (IntervalTree *) NULL)
01313 {
01314 register long
01315 count;
01316
01317 register MagickRealType
01318 sum;
01319
01320 sum=0.0;
01321 count=0;
01322 for ( ; child != (IntervalTree *) NULL; child=child->sibling)
01323 {
01324 sum+=child->stability;
01325 count++;
01326 }
01327 node->mean_stability=sum/(MagickRealType) count;
01328 }
01329 MeanStability(node->sibling);
01330 MeanStability(node->child);
01331 }
01332
01333 static void Stability(IntervalTree *node)
01334 {
01335 if (node == (IntervalTree *) NULL)
01336 return;
01337 if (node->child == (IntervalTree *) NULL)
01338 node->stability=0.0;
01339 else
01340 node->stability=node->tau-(node->child)->tau;
01341 Stability(node->sibling);
01342 Stability(node->child);
01343 }
01344
01345 static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
01346 const unsigned long number_crossings)
01347 {
01348 IntervalTree
01349 *head,
01350 **list,
01351 *node,
01352 *root;
01353
01354 long
01355 j,
01356 k,
01357 left,
01358 number_nodes;
01359
01360 register long
01361 i;
01362
01363
01364
01365
01366 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
01367 sizeof(*list));
01368 if (list == (IntervalTree **) NULL)
01369 return((IntervalTree *) NULL);
01370
01371
01372
01373 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
01374 root->child=(IntervalTree *) NULL;
01375 root->sibling=(IntervalTree *) NULL;
01376 root->tau=0.0;
01377 root->left=0;
01378 root->right=255;
01379 for (i=(-1); i < (long) number_crossings; i++)
01380 {
01381
01382
01383
01384 number_nodes=0;
01385 InitializeList(list,&number_nodes,root);
01386
01387
01388
01389 for (j=0; j < number_nodes; j++)
01390 {
01391 head=list[j];
01392 left=head->left;
01393 node=head;
01394 for (k=head->left+1; k < head->right; k++)
01395 {
01396 if (zero_crossing[i+1].crossings[k] != 0)
01397 {
01398 if (node == head)
01399 {
01400 node->child=(IntervalTree *) AcquireMagickMemory(
01401 sizeof(*node->child));
01402 node=node->child;
01403 }
01404 else
01405 {
01406 node->sibling=(IntervalTree *) AcquireMagickMemory(
01407 sizeof(*node->sibling));
01408 node=node->sibling;
01409 }
01410 node->tau=zero_crossing[i+1].tau;
01411 node->child=(IntervalTree *) NULL;
01412 node->sibling=(IntervalTree *) NULL;
01413 node->left=left;
01414 node->right=k;
01415 left=k;
01416 }
01417 }
01418 if (left != head->left)
01419 {
01420 node->sibling=(IntervalTree *) AcquireMagickMemory(
01421 sizeof(*node->sibling));
01422 node=node->sibling;
01423 node->tau=zero_crossing[i+1].tau;
01424 node->child=(IntervalTree *) NULL;
01425 node->sibling=(IntervalTree *) NULL;
01426 node->left=left;
01427 node->right=head->right;
01428 }
01429 }
01430 }
01431
01432
01433
01434 Stability(root->child);
01435 MeanStability(root->child);
01436 list=(IntervalTree **) RelinquishMagickMemory(list);
01437 return(root);
01438 }
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 static void ActiveNodes(IntervalTree **list,long *number_nodes,
01471 IntervalTree *node)
01472 {
01473 if (node == (IntervalTree *) NULL)
01474 return;
01475 if (node->stability >= node->mean_stability)
01476 {
01477 list[(*number_nodes)++]=node;
01478 ActiveNodes(list,number_nodes,node->sibling);
01479 }
01480 else
01481 {
01482 ActiveNodes(list,number_nodes,node->sibling);
01483 ActiveNodes(list,number_nodes,node->child);
01484 }
01485 }
01486
01487 static void FreeNodes(IntervalTree *node)
01488 {
01489 if (node == (IntervalTree *) NULL)
01490 return;
01491 FreeNodes(node->sibling);
01492 FreeNodes(node->child);
01493 node=(IntervalTree *) RelinquishMagickMemory(node);
01494 }
01495
01496 static MagickRealType OptimalTau(const long *histogram,const double max_tau,
01497 const double min_tau,const double delta_tau,const double smooth_threshold,
01498 short *extrema)
01499 {
01500 IntervalTree
01501 **list,
01502 *node,
01503 *root;
01504
01505 long
01506 index,
01507 j,
01508 k,
01509 number_nodes;
01510
01511 MagickRealType
01512 average_tau,
01513 *derivative,
01514 *second_derivative,
01515 tau,
01516 value;
01517
01518 register long
01519 i,
01520 x;
01521
01522 MagickBooleanType
01523 peak;
01524
01525 unsigned long
01526 count,
01527 number_crossings;
01528
01529 ZeroCrossing
01530 *zero_crossing;
01531
01532
01533
01534
01535 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
01536 sizeof(*list));
01537 if (list == (IntervalTree **) NULL)
01538 return(0.0);
01539
01540
01541
01542 count=(unsigned long) ((max_tau-min_tau)/delta_tau)+2;
01543 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
01544 sizeof(*zero_crossing));
01545 if (zero_crossing == (ZeroCrossing *) NULL)
01546 return(0.0);
01547 for (i=0; i < (long) count; i++)
01548 zero_crossing[i].tau=(-1.0);
01549
01550
01551
01552 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
01553 second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
01554 sizeof(*second_derivative));
01555 if ((derivative == (MagickRealType *) NULL) ||
01556 (second_derivative == (MagickRealType *) NULL))
01557 ThrowFatalException(ResourceLimitFatalError,
01558 "UnableToAllocateDerivatives");
01559 i=0;
01560 for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
01561 {
01562 zero_crossing[i].tau=tau;
01563 ScaleSpace(histogram,tau,zero_crossing[i].histogram);
01564 DerivativeHistogram(zero_crossing[i].histogram,derivative);
01565 DerivativeHistogram(derivative,second_derivative);
01566 ZeroCrossHistogram(second_derivative,smooth_threshold,
01567 zero_crossing[i].crossings);
01568 i++;
01569 }
01570
01571
01572
01573 zero_crossing[i].tau=0.0;
01574 for (j=0; j <= 255; j++)
01575 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
01576 DerivativeHistogram(zero_crossing[i].histogram,derivative);
01577 DerivativeHistogram(derivative,second_derivative);
01578 ZeroCrossHistogram(second_derivative,smooth_threshold,
01579 zero_crossing[i].crossings);
01580 number_crossings=(unsigned long) i;
01581 derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
01582 second_derivative=(MagickRealType *)
01583 RelinquishMagickMemory(second_derivative);
01584
01585
01586
01587 ConsolidateCrossings(zero_crossing,number_crossings);
01588
01589
01590
01591 for (i=0; i <= (long) number_crossings; i++)
01592 {
01593 for (j=0; j < 255; j++)
01594 if (zero_crossing[i].crossings[j] != 0)
01595 break;
01596 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
01597 for (j=255; j > 0; j--)
01598 if (zero_crossing[i].crossings[j] != 0)
01599 break;
01600 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
01601 }
01602
01603
01604
01605 root=InitializeIntervalTree(zero_crossing,number_crossings);
01606 if (root == (IntervalTree *) NULL)
01607 return(0.0);
01608
01609
01610
01611
01612 number_nodes=0;
01613 ActiveNodes(list,&number_nodes,root->child);
01614
01615
01616
01617 for (i=0; i <= 255; i++)
01618 extrema[i]=0;
01619 for (i=0; i < number_nodes; i++)
01620 {
01621
01622
01623
01624 k=0;
01625 node=list[i];
01626 for (j=0; j <= (long) number_crossings; j++)
01627 if (zero_crossing[j].tau == node->tau)
01628 k=j;
01629
01630
01631
01632 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
01633 MagickFalse;
01634 index=node->left;
01635 value=zero_crossing[k].histogram[index];
01636 for (x=node->left; x <= node->right; x++)
01637 {
01638 if (peak != MagickFalse)
01639 {
01640 if (zero_crossing[k].histogram[x] > value)
01641 {
01642 value=zero_crossing[k].histogram[x];
01643 index=x;
01644 }
01645 }
01646 else
01647 if (zero_crossing[k].histogram[x] < value)
01648 {
01649 value=zero_crossing[k].histogram[x];
01650 index=x;
01651 }
01652 }
01653 for (x=node->left; x <= node->right; x++)
01654 {
01655 if (index == 0)
01656 index=256;
01657 if (peak != MagickFalse)
01658 extrema[x]=(short) index;
01659 else
01660 extrema[x]=(short) (-index);
01661 }
01662 }
01663
01664
01665
01666 average_tau=0.0;
01667 for (i=0; i < number_nodes; i++)
01668 average_tau+=list[i]->tau;
01669 average_tau/=(MagickRealType) number_nodes;
01670
01671
01672
01673 FreeNodes(root);
01674 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
01675 list=(IntervalTree **) RelinquishMagickMemory(list);
01676 return(average_tau);
01677 }
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 static void ScaleSpace(const long *histogram,const MagickRealType tau,
01705 MagickRealType *scale_histogram)
01706 {
01707 MagickRealType
01708 alpha,
01709 beta,
01710 *gamma,
01711 sum;
01712
01713 register long
01714 u,
01715 x;
01716
01717 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
01718 if (gamma == (MagickRealType *) NULL)
01719 ThrowFatalException(ResourceLimitFatalError,
01720 "UnableToAllocateGammaMap");
01721 alpha=1.0/(tau*sqrt(2.0*MagickPI));
01722 beta=(-1.0/(2.0*tau*tau));
01723 for (x=0; x <= 255; x++)
01724 gamma[x]=0.0;
01725 for (x=0; x <= 255; x++)
01726 {
01727 gamma[x]=exp((double) beta*x*x);
01728 if (gamma[x] < MagickEpsilon)
01729 break;
01730 }
01731 for (x=0; x <= 255; x++)
01732 {
01733 sum=0.0;
01734 for (u=0; u <= 255; u++)
01735 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
01736 scale_histogram[x]=alpha*sum;
01737 }
01738 gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
01739 }
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 MagickExport MagickBooleanType SegmentImage(Image *image,
01781 const ColorspaceType colorspace,const MagickBooleanType verbose,
01782 const double cluster_threshold,const double smooth_threshold)
01783 {
01784 long
01785 *histogram[MaxDimension];
01786
01787 MagickBooleanType
01788 status;
01789
01790 register long
01791 i;
01792
01793 short
01794 *extrema[MaxDimension];
01795
01796
01797
01798
01799 assert(image != (Image *) NULL);
01800 assert(image->signature == MagickSignature);
01801 if (image->debug != MagickFalse)
01802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01803 for (i=0; i < MaxDimension; i++)
01804 {
01805 histogram[i]=(long *) AcquireQuantumMemory(256,sizeof(**histogram));
01806 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
01807 if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
01808 {
01809 for (i-- ; i >= 0; i--)
01810 {
01811 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01812 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01813 }
01814 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01815 image->filename)
01816 }
01817 }
01818 if (colorspace != RGBColorspace)
01819 (void) TransformImageColorspace(image,colorspace);
01820
01821
01822
01823 InitializeHistogram(image,histogram,&image->exception);
01824 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
01825 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
01826 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
01827 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
01828 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
01829 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
01830
01831
01832
01833 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
01834 if (colorspace != RGBColorspace)
01835 (void) TransformImageColorspace(image,colorspace);
01836
01837
01838
01839 for (i=0; i < MaxDimension; i++)
01840 {
01841 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
01842 histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
01843 }
01844 return(status);
01845 }
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 static void ZeroCrossHistogram(MagickRealType *second_derivative,
01878 const MagickRealType smooth_threshold,short *crossings)
01879 {
01880 long
01881 parity;
01882
01883 register long
01884 i;
01885
01886
01887
01888
01889 for (i=0; i <= 255; i++)
01890 if ((second_derivative[i] < smooth_threshold) &&
01891 (second_derivative[i] >= -smooth_threshold))
01892 second_derivative[i]=0.0;
01893
01894
01895
01896 parity=0;
01897 for (i=0; i <= 255; i++)
01898 {
01899 crossings[i]=0;
01900 if (second_derivative[i] < 0.0)
01901 {
01902 if (parity > 0)
01903 crossings[i]=(-1);
01904 parity=1;
01905 }
01906 else
01907 if (second_derivative[i] > 0.0)
01908 {
01909 if (parity < 0)
01910 crossings[i]=1;
01911 parity=(-1);
01912 }
01913 }
01914 }