|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % SSSSS EEEEE GGGG M M EEEEE N N TTTTT % 00007 % SS E G MM MM E NN N T % 00008 % SSS EEE G GGG M M M EEE N N N T % 00009 % SS E G G M M E N NN T % 00010 % SSSSS EEEEE GGGG M M EEEEE N N T % 00011 % % 00012 % % 00013 % MagickCore Methods to Segment an Image with Thresholding Fuzzy c-Means % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % April 1993 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % Segment segments an image by analyzing the histograms of the color 00037 % components and identifying units that are homogeneous with the fuzzy 00038 % c-means technique. The scale-space filter analyzes the histograms of 00039 % the three color components of the image and identifies a set of 00040 % classes. The extents of each class is used to coarsely segment the 00041 % image with thresholding. The color associated with each class is 00042 % determined by the mean color of all pixels within the extents of a 00043 % particular class. Finally, any unclassified pixels are assigned to 00044 % the closest class with the fuzzy c-means technique. 00045 % 00046 % The fuzzy c-Means algorithm can be summarized as follows: 00047 % 00048 % o Build a histogram, one for each color component of the image. 00049 % 00050 % o For each histogram, successively apply the scale-space filter and 00051 % build an interval tree of zero crossings in the second derivative 00052 % at each scale. Analyze this scale-space ``fingerprint'' to 00053 % determine which peaks and valleys in the histogram are most 00054 % predominant. 00055 % 00056 % o The fingerprint defines intervals on the axis of the histogram. 00057 % Each interval contains either a minima or a maxima in the original 00058 % signal. If each color component lies within the maxima interval, 00059 % that pixel is considered ``classified'' and is assigned an unique 00060 % class number. 00061 % 00062 % o Any pixel that fails to be classified in the above thresholding 00063 % pass is classified using the fuzzy c-Means technique. It is 00064 % assigned to one of the classes discovered in the histogram analysis 00065 % phase. 00066 % 00067 % The fuzzy c-Means technique attempts to cluster a pixel by finding 00068 % the local minima of the generalized within group sum of squared error 00069 % objective function. A pixel is assigned to the closest class of 00070 % which the fuzzy membership has a maximum value. 00071 % 00072 % Segment is strongly based on software written by Andy Gallo, 00073 % University of Delaware. 00074 % 00075 % The following reference was used in creating this program: 00076 % 00077 % Young Won Lim, Sang Uk Lee, "On The Color Image Segmentation 00078 % Algorithm Based on the Thresholding and the Fuzzy c-Means 00079 % Techniques", Pattern Recognition, Volume 23, Number 9, pages 00080 % 935-952, 1990. 00081 % 00082 % 00083 */ 00084 00085 #include "MagickCore/studio.h" 00086 #include "MagickCore/cache.h" 00087 #include "MagickCore/color.h" 00088 #include "MagickCore/colormap.h" 00089 #include "MagickCore/colorspace.h" 00090 #include "MagickCore/colorspace-private.h" 00091 #include "MagickCore/exception.h" 00092 #include "MagickCore/exception-private.h" 00093 #include "MagickCore/image.h" 00094 #include "MagickCore/image-private.h" 00095 #include "MagickCore/memory_.h" 00096 #include "MagickCore/monitor.h" 00097 #include "MagickCore/monitor-private.h" 00098 #include "MagickCore/pixel-accessor.h" 00099 #include "MagickCore/quantize.h" 00100 #include "MagickCore/quantum.h" 00101 #include "MagickCore/quantum-private.h" 00102 #include "MagickCore/segment.h" 00103 #include "MagickCore/string_.h" 00104 00105 /* 00106 Define declarations. 00107 */ 00108 #define MaxDimension 3 00109 #define DeltaTau 0.5f 00110 #if defined(FastClassify) 00111 #define WeightingExponent 2.0 00112 #define SegmentPower(ratio) (ratio) 00113 #else 00114 #define WeightingExponent 2.5 00115 #define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0))); 00116 #endif 00117 #define Tau 5.2f 00118 00119 /* 00120 Typedef declarations. 00121 */ 00122 typedef struct _ExtentPacket 00123 { 00124 MagickRealType 00125 center; 00126 00127 ssize_t 00128 index, 00129 left, 00130 right; 00131 } ExtentPacket; 00132 00133 typedef struct _Cluster 00134 { 00135 struct _Cluster 00136 *next; 00137 00138 ExtentPacket 00139 red, 00140 green, 00141 blue; 00142 00143 ssize_t 00144 count, 00145 id; 00146 } Cluster; 00147 00148 typedef struct _IntervalTree 00149 { 00150 MagickRealType 00151 tau; 00152 00153 ssize_t 00154 left, 00155 right; 00156 00157 MagickRealType 00158 mean_stability, 00159 stability; 00160 00161 struct _IntervalTree 00162 *sibling, 00163 *child; 00164 } IntervalTree; 00165 00166 typedef struct _ZeroCrossing 00167 { 00168 MagickRealType 00169 tau, 00170 histogram[256]; 00171 00172 short 00173 crossings[256]; 00174 } ZeroCrossing; 00175 00176 /* 00177 Constant declarations. 00178 */ 00179 static const int 00180 Blue = 2, 00181 Green = 1, 00182 Red = 0, 00183 SafeMargin = 3, 00184 TreeLength = 600; 00185 00186 /* 00187 Method prototypes. 00188 */ 00189 static MagickRealType 00190 OptimalTau(const ssize_t *,const double,const double,const double, 00191 const double,short *); 00192 00193 static ssize_t 00194 DefineRegion(const short *,ExtentPacket *); 00195 00196 static void 00197 InitializeHistogram(const Image *,ssize_t **,ExceptionInfo *), 00198 ScaleSpace(const ssize_t *,const MagickRealType,MagickRealType *), 00199 ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *); 00200 00201 /* 00202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00203 % % 00204 % % 00205 % % 00206 + C l a s s i f y % 00207 % % 00208 % % 00209 % % 00210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00211 % 00212 % Classify() defines one or more classes. Each pixel is thresholded to 00213 % determine which class it belongs to. If the class is not identified it is 00214 % assigned to the closest class based on the fuzzy c-Means technique. 00215 % 00216 % The format of the Classify method is: 00217 % 00218 % MagickBooleanType Classify(Image *image,short **extrema, 00219 % const MagickRealType cluster_threshold, 00220 % const MagickRealType weighting_exponent, 00221 % const MagickBooleanType verbose,ExceptionInfo *exception) 00222 % 00223 % A description of each parameter follows. 00224 % 00225 % o image: the image. 00226 % 00227 % o extrema: Specifies a pointer to an array of integers. They 00228 % represent the peaks and valleys of the histogram for each color 00229 % component. 00230 % 00231 % o cluster_threshold: This MagickRealType represents the minimum number of 00232 % pixels contained in a hexahedra before it can be considered valid 00233 % (expressed as a percentage). 00234 % 00235 % o weighting_exponent: Specifies the membership weighting exponent. 00236 % 00237 % o verbose: A value greater than zero prints detailed information about 00238 % the identified classes. 00239 % 00240 % o exception: return any errors or warnings in this structure. 00241 % 00242 */ 00243 static MagickBooleanType Classify(Image *image,short **extrema, 00244 const MagickRealType cluster_threshold, 00245 const MagickRealType weighting_exponent,const MagickBooleanType verbose, 00246 ExceptionInfo *exception) 00247 { 00248 #define SegmentImageTag "Segment/Image" 00249 00250 CacheView 00251 *image_view; 00252 00253 Cluster 00254 *cluster, 00255 *head, 00256 *last_cluster, 00257 *next_cluster; 00258 00259 ExtentPacket 00260 blue, 00261 green, 00262 red; 00263 00264 MagickOffsetType 00265 progress; 00266 00267 MagickRealType 00268 *free_squares; 00269 00270 MagickStatusType 00271 status; 00272 00273 register ssize_t 00274 i; 00275 00276 register MagickRealType 00277 *squares; 00278 00279 size_t 00280 number_clusters; 00281 00282 ssize_t 00283 count, 00284 y; 00285 00286 /* 00287 Form clusters. 00288 */ 00289 cluster=(Cluster *) NULL; 00290 head=(Cluster *) NULL; 00291 (void) ResetMagickMemory(&red,0,sizeof(red)); 00292 (void) ResetMagickMemory(&green,0,sizeof(green)); 00293 (void) ResetMagickMemory(&blue,0,sizeof(blue)); 00294 while (DefineRegion(extrema[Red],&red) != 0) 00295 { 00296 green.index=0; 00297 while (DefineRegion(extrema[Green],&green) != 0) 00298 { 00299 blue.index=0; 00300 while (DefineRegion(extrema[Blue],&blue) != 0) 00301 { 00302 /* 00303 Allocate a new class. 00304 */ 00305 if (head != (Cluster *) NULL) 00306 { 00307 cluster->next=(Cluster *) AcquireMagickMemory( 00308 sizeof(*cluster->next)); 00309 cluster=cluster->next; 00310 } 00311 else 00312 { 00313 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster)); 00314 head=cluster; 00315 } 00316 if (cluster == (Cluster *) NULL) 00317 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00318 image->filename); 00319 /* 00320 Initialize a new class. 00321 */ 00322 cluster->count=0; 00323 cluster->red=red; 00324 cluster->green=green; 00325 cluster->blue=blue; 00326 cluster->next=(Cluster *) NULL; 00327 } 00328 } 00329 } 00330 if (head == (Cluster *) NULL) 00331 { 00332 /* 00333 No classes were identified-- create one. 00334 */ 00335 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster)); 00336 if (cluster == (Cluster *) NULL) 00337 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00338 image->filename); 00339 /* 00340 Initialize a new class. 00341 */ 00342 cluster->count=0; 00343 cluster->red=red; 00344 cluster->green=green; 00345 cluster->blue=blue; 00346 cluster->next=(Cluster *) NULL; 00347 head=cluster; 00348 } 00349 /* 00350 Count the pixels for each cluster. 00351 */ 00352 status=MagickTrue; 00353 count=0; 00354 progress=0; 00355 image_view=AcquireCacheView(image); 00356 for (y=0; y < (ssize_t) image->rows; y++) 00357 { 00358 register const Quantum 00359 *p; 00360 00361 register ssize_t 00362 x; 00363 00364 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00365 if (p == (const Quantum *) NULL) 00366 break; 00367 for (x=0; x < (ssize_t) image->columns; x++) 00368 { 00369 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00370 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) >= 00371 (cluster->red.left-SafeMargin)) && 00372 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) <= 00373 (cluster->red.right+SafeMargin)) && 00374 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) >= 00375 (cluster->green.left-SafeMargin)) && 00376 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) <= 00377 (cluster->green.right+SafeMargin)) && 00378 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) >= 00379 (cluster->blue.left-SafeMargin)) && 00380 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) <= 00381 (cluster->blue.right+SafeMargin))) 00382 { 00383 /* 00384 Count this pixel. 00385 */ 00386 count++; 00387 cluster->red.center+=(MagickRealType) ScaleQuantumToChar( 00388 GetPixelRed(image,p)); 00389 cluster->green.center+=(MagickRealType) ScaleQuantumToChar( 00390 GetPixelGreen(image,p)); 00391 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar( 00392 GetPixelBlue(image,p)); 00393 cluster->count++; 00394 break; 00395 } 00396 p+=GetPixelChannels(image); 00397 } 00398 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00399 { 00400 MagickBooleanType 00401 proceed; 00402 00403 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00404 #pragma omp critical (MagickCore_Classify) 00405 #endif 00406 proceed=SetImageProgress(image,SegmentImageTag,progress++,2* 00407 image->rows); 00408 if (proceed == MagickFalse) 00409 status=MagickFalse; 00410 } 00411 } 00412 image_view=DestroyCacheView(image_view); 00413 /* 00414 Remove clusters that do not meet minimum cluster threshold. 00415 */ 00416 count=0; 00417 last_cluster=head; 00418 next_cluster=head; 00419 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster) 00420 { 00421 next_cluster=cluster->next; 00422 if ((cluster->count > 0) && 00423 (cluster->count >= (count*cluster_threshold/100.0))) 00424 { 00425 /* 00426 Initialize cluster. 00427 */ 00428 cluster->id=count; 00429 cluster->red.center/=cluster->count; 00430 cluster->green.center/=cluster->count; 00431 cluster->blue.center/=cluster->count; 00432 count++; 00433 last_cluster=cluster; 00434 continue; 00435 } 00436 /* 00437 Delete cluster. 00438 */ 00439 if (cluster == head) 00440 head=next_cluster; 00441 else 00442 last_cluster->next=next_cluster; 00443 cluster=(Cluster *) RelinquishMagickMemory(cluster); 00444 } 00445 number_clusters=(size_t) count; 00446 if (verbose != MagickFalse) 00447 { 00448 /* 00449 Print cluster statistics. 00450 */ 00451 (void) FormatLocaleFile(stdout,"Fuzzy C-means Statistics\n"); 00452 (void) FormatLocaleFile(stdout,"===================\n\n"); 00453 (void) FormatLocaleFile(stdout,"\tCluster Threshold = %g\n",(double) 00454 cluster_threshold); 00455 (void) FormatLocaleFile(stdout,"\tWeighting Exponent = %g\n",(double) 00456 weighting_exponent); 00457 (void) FormatLocaleFile(stdout,"\tTotal Number of Clusters = %.20g\n\n", 00458 (double) number_clusters); 00459 /* 00460 Print the total number of points per cluster. 00461 */ 00462 (void) FormatLocaleFile(stdout,"\n\nNumber of Vectors Per Cluster\n"); 00463 (void) FormatLocaleFile(stdout,"=============================\n\n"); 00464 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00465 (void) FormatLocaleFile(stdout,"Cluster #%.20g = %.20g\n",(double) 00466 cluster->id,(double) cluster->count); 00467 /* 00468 Print the cluster extents. 00469 */ 00470 (void) FormatLocaleFile(stdout, 00471 "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension); 00472 (void) FormatLocaleFile(stdout,"================"); 00473 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00474 { 00475 (void) FormatLocaleFile(stdout,"\n\nCluster #%.20g\n\n",(double) 00476 cluster->id); 00477 (void) FormatLocaleFile(stdout, 00478 "%.20g-%.20g %.20g-%.20g %.20g-%.20g\n",(double) 00479 cluster->red.left,(double) cluster->red.right,(double) 00480 cluster->green.left,(double) cluster->green.right,(double) 00481 cluster->blue.left,(double) cluster->blue.right); 00482 } 00483 /* 00484 Print the cluster center values. 00485 */ 00486 (void) FormatLocaleFile(stdout, 00487 "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension); 00488 (void) FormatLocaleFile(stdout,"====================="); 00489 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00490 { 00491 (void) FormatLocaleFile(stdout,"\n\nCluster #%.20g\n\n",(double) 00492 cluster->id); 00493 (void) FormatLocaleFile(stdout,"%g %g %g\n",(double) 00494 cluster->red.center,(double) cluster->green.center,(double) 00495 cluster->blue.center); 00496 } 00497 (void) FormatLocaleFile(stdout,"\n"); 00498 } 00499 if (number_clusters > 256) 00500 ThrowBinaryException(ImageError,"TooManyClusters",image->filename); 00501 /* 00502 Speed up distance calculations. 00503 */ 00504 squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares)); 00505 if (squares == (MagickRealType *) NULL) 00506 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00507 image->filename); 00508 squares+=255; 00509 for (i=(-255); i <= 255; i++) 00510 squares[i]=(MagickRealType) i*(MagickRealType) i; 00511 /* 00512 Allocate image colormap. 00513 */ 00514 if (AcquireImageColormap(image,number_clusters,exception) == MagickFalse) 00515 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00516 image->filename); 00517 i=0; 00518 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00519 { 00520 image->colormap[i].red=ScaleCharToQuantum((unsigned char) 00521 (cluster->red.center+0.5)); 00522 image->colormap[i].green=ScaleCharToQuantum((unsigned char) 00523 (cluster->green.center+0.5)); 00524 image->colormap[i].blue=ScaleCharToQuantum((unsigned char) 00525 (cluster->blue.center+0.5)); 00526 i++; 00527 } 00528 /* 00529 Do course grain classes. 00530 */ 00531 image_view=AcquireCacheView(image); 00532 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00533 #pragma omp parallel for schedule(static,4) shared(progress,status) 00534 #endif 00535 for (y=0; y < (ssize_t) image->rows; y++) 00536 { 00537 Cluster 00538 *cluster; 00539 00540 register const PixelInfo 00541 *restrict p; 00542 00543 register ssize_t 00544 x; 00545 00546 register Quantum 00547 *restrict q; 00548 00549 if (status == MagickFalse) 00550 continue; 00551 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00552 if (q == (Quantum *) NULL) 00553 { 00554 status=MagickFalse; 00555 continue; 00556 } 00557 for (x=0; x < (ssize_t) image->columns; x++) 00558 { 00559 SetPixelIndex(image,0,q); 00560 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 00561 { 00562 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,q)) >= 00563 (cluster->red.left-SafeMargin)) && 00564 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,q)) <= 00565 (cluster->red.right+SafeMargin)) && 00566 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,q)) >= 00567 (cluster->green.left-SafeMargin)) && 00568 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,q)) <= 00569 (cluster->green.right+SafeMargin)) && 00570 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,q)) >= 00571 (cluster->blue.left-SafeMargin)) && 00572 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,q)) <= 00573 (cluster->blue.right+SafeMargin))) 00574 { 00575 /* 00576 Classify this pixel. 00577 */ 00578 SetPixelIndex(image,(Quantum) cluster->id,q); 00579 break; 00580 } 00581 } 00582 if (cluster == (Cluster *) NULL) 00583 { 00584 MagickRealType 00585 distance_squared, 00586 local_minima, 00587 numerator, 00588 ratio, 00589 sum; 00590 00591 register ssize_t 00592 j, 00593 k; 00594 00595 /* 00596 Compute fuzzy membership. 00597 */ 00598 local_minima=0.0; 00599 for (j=0; j < (ssize_t) image->colors; j++) 00600 { 00601 sum=0.0; 00602 p=image->colormap+j; 00603 distance_squared=squares[(ssize_t) ScaleQuantumToChar( 00604 GetPixelRed(image,q))-(ssize_t) 00605 ScaleQuantumToChar(p->red)]+squares[(ssize_t) 00606 ScaleQuantumToChar(GetPixelGreen(image,q))-(ssize_t) 00607 ScaleQuantumToChar(p->green)]+squares[(ssize_t) 00608 ScaleQuantumToChar(GetPixelBlue(image,q))-(ssize_t) 00609 ScaleQuantumToChar(p->blue)]; 00610 numerator=distance_squared; 00611 for (k=0; k < (ssize_t) image->colors; k++) 00612 { 00613 p=image->colormap+k; 00614 distance_squared=squares[(ssize_t) ScaleQuantumToChar( 00615 GetPixelRed(image,q))-(ssize_t) 00616 ScaleQuantumToChar(p->red)]+squares[(ssize_t) 00617 ScaleQuantumToChar(GetPixelGreen(image,q))-(ssize_t) 00618 ScaleQuantumToChar(p->green)]+squares[(ssize_t) 00619 ScaleQuantumToChar(GetPixelBlue(image,q))-(ssize_t) 00620 ScaleQuantumToChar(p->blue)]; 00621 ratio=numerator/distance_squared; 00622 sum+=SegmentPower(ratio); 00623 } 00624 if ((sum != 0.0) && ((1.0/sum) > local_minima)) 00625 { 00626 /* 00627 Classify this pixel. 00628 */ 00629 local_minima=1.0/sum; 00630 SetPixelIndex(image,(Quantum) j,q); 00631 } 00632 } 00633 } 00634 q+=GetPixelChannels(image); 00635 } 00636 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00637 status=MagickFalse; 00638 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00639 { 00640 MagickBooleanType 00641 proceed; 00642 00643 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00644 #pragma omp critical (MagickCore_Classify) 00645 #endif 00646 proceed=SetImageProgress(image,SegmentImageTag,progress++, 00647 2*image->rows); 00648 if (proceed == MagickFalse) 00649 status=MagickFalse; 00650 } 00651 } 00652 image_view=DestroyCacheView(image_view); 00653 status&=SyncImage(image,exception); 00654 /* 00655 Relinquish resources. 00656 */ 00657 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster) 00658 { 00659 next_cluster=cluster->next; 00660 cluster=(Cluster *) RelinquishMagickMemory(cluster); 00661 } 00662 squares-=255; 00663 free_squares=squares; 00664 free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares); 00665 return(MagickTrue); 00666 } 00667 00668 /* 00669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00670 % % 00671 % % 00672 % % 00673 + C o n s o l i d a t e C r o s s i n g s % 00674 % % 00675 % % 00676 % % 00677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00678 % 00679 % ConsolidateCrossings() guarantees that an even number of zero crossings 00680 % always lie between two crossings. 00681 % 00682 % The format of the ConsolidateCrossings method is: 00683 % 00684 % ConsolidateCrossings(ZeroCrossing *zero_crossing, 00685 % const size_t number_crossings) 00686 % 00687 % A description of each parameter follows. 00688 % 00689 % o zero_crossing: Specifies an array of structures of type ZeroCrossing. 00690 % 00691 % o number_crossings: This size_t specifies the number of elements 00692 % in the zero_crossing array. 00693 % 00694 */ 00695 00696 static inline ssize_t MagickAbsoluteValue(const ssize_t x) 00697 { 00698 if (x < 0) 00699 return(-x); 00700 return(x); 00701 } 00702 00703 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y) 00704 { 00705 if (x > y) 00706 return(x); 00707 return(y); 00708 } 00709 00710 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y) 00711 { 00712 if (x < y) 00713 return(x); 00714 return(y); 00715 } 00716 00717 static void ConsolidateCrossings(ZeroCrossing *zero_crossing, 00718 const size_t number_crossings) 00719 { 00720 register ssize_t 00721 i, 00722 j, 00723 k, 00724 l; 00725 00726 ssize_t 00727 center, 00728 correct, 00729 count, 00730 left, 00731 right; 00732 00733 /* 00734 Consolidate zero crossings. 00735 */ 00736 for (i=(ssize_t) number_crossings-1; i >= 0; i--) 00737 for (j=0; j <= 255; j++) 00738 { 00739 if (zero_crossing[i].crossings[j] == 0) 00740 continue; 00741 /* 00742 Find the entry that is closest to j and still preserves the 00743 property that there are an even number of crossings between 00744 intervals. 00745 */ 00746 for (k=j-1; k > 0; k--) 00747 if (zero_crossing[i+1].crossings[k] != 0) 00748 break; 00749 left=MagickMax(k,0); 00750 center=j; 00751 for (k=j+1; k < 255; k++) 00752 if (zero_crossing[i+1].crossings[k] != 0) 00753 break; 00754 right=MagickMin(k,255); 00755 /* 00756 K is the zero crossing just left of j. 00757 */ 00758 for (k=j-1; k > 0; k--) 00759 if (zero_crossing[i].crossings[k] != 0) 00760 break; 00761 if (k < 0) 00762 k=0; 00763 /* 00764 Check center for an even number of crossings between k and j. 00765 */ 00766 correct=(-1); 00767 if (zero_crossing[i+1].crossings[j] != 0) 00768 { 00769 count=0; 00770 for (l=k+1; l < center; l++) 00771 if (zero_crossing[i+1].crossings[l] != 0) 00772 count++; 00773 if (((count % 2) == 0) && (center != k)) 00774 correct=center; 00775 } 00776 /* 00777 Check left for an even number of crossings between k and j. 00778 */ 00779 if (correct == -1) 00780 { 00781 count=0; 00782 for (l=k+1; l < left; l++) 00783 if (zero_crossing[i+1].crossings[l] != 0) 00784 count++; 00785 if (((count % 2) == 0) && (left != k)) 00786 correct=left; 00787 } 00788 /* 00789 Check right for an even number of crossings between k and j. 00790 */ 00791 if (correct == -1) 00792 { 00793 count=0; 00794 for (l=k+1; l < right; l++) 00795 if (zero_crossing[i+1].crossings[l] != 0) 00796 count++; 00797 if (((count % 2) == 0) && (right != k)) 00798 correct=right; 00799 } 00800 l=(ssize_t) zero_crossing[i].crossings[j]; 00801 zero_crossing[i].crossings[j]=0; 00802 if (correct != -1) 00803 zero_crossing[i].crossings[correct]=(short) l; 00804 } 00805 } 00806 00807 /* 00808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00809 % % 00810 % % 00811 % % 00812 + D e f i n e R e g i o n % 00813 % % 00814 % % 00815 % % 00816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00817 % 00818 % DefineRegion() defines the left and right boundaries of a peak region. 00819 % 00820 % The format of the DefineRegion method is: 00821 % 00822 % ssize_t DefineRegion(const short *extrema,ExtentPacket *extents) 00823 % 00824 % A description of each parameter follows. 00825 % 00826 % o extrema: Specifies a pointer to an array of integers. They 00827 % represent the peaks and valleys of the histogram for each color 00828 % component. 00829 % 00830 % o extents: This pointer to an ExtentPacket represent the extends 00831 % of a particular peak or valley of a color component. 00832 % 00833 */ 00834 static ssize_t DefineRegion(const short *extrema,ExtentPacket *extents) 00835 { 00836 /* 00837 Initialize to default values. 00838 */ 00839 extents->left=0; 00840 extents->center=0.0; 00841 extents->right=255; 00842 /* 00843 Find the left side (maxima). 00844 */ 00845 for ( ; extents->index <= 255; extents->index++) 00846 if (extrema[extents->index] > 0) 00847 break; 00848 if (extents->index > 255) 00849 return(MagickFalse); /* no left side - no region exists */ 00850 extents->left=extents->index; 00851 /* 00852 Find the right side (minima). 00853 */ 00854 for ( ; extents->index <= 255; extents->index++) 00855 if (extrema[extents->index] < 0) 00856 break; 00857 extents->right=extents->index-1; 00858 return(MagickTrue); 00859 } 00860 00861 /* 00862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00863 % % 00864 % % 00865 % % 00866 + D e r i v a t i v e H i s t o g r a m % 00867 % % 00868 % % 00869 % % 00870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00871 % 00872 % DerivativeHistogram() determines the derivative of the histogram using 00873 % central differencing. 00874 % 00875 % The format of the DerivativeHistogram method is: 00876 % 00877 % DerivativeHistogram(const MagickRealType *histogram, 00878 % MagickRealType *derivative) 00879 % 00880 % A description of each parameter follows. 00881 % 00882 % o histogram: Specifies an array of MagickRealTypes representing the number 00883 % of pixels for each intensity of a particular color component. 00884 % 00885 % o derivative: This array of MagickRealTypes is initialized by 00886 % DerivativeHistogram to the derivative of the histogram using central 00887 % differencing. 00888 % 00889 */ 00890 static void DerivativeHistogram(const MagickRealType *histogram, 00891 MagickRealType *derivative) 00892 { 00893 register ssize_t 00894 i, 00895 n; 00896 00897 /* 00898 Compute endpoints using second order polynomial interpolation. 00899 */ 00900 n=255; 00901 derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]); 00902 derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]); 00903 /* 00904 Compute derivative using central differencing. 00905 */ 00906 for (i=1; i < n; i++) 00907 derivative[i]=(histogram[i+1]-histogram[i-1])/2.0; 00908 return; 00909 } 00910 00911 /* 00912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00913 % % 00914 % % 00915 % % 00916 + G e t I m a g e D y n a m i c T h r e s h o l d % 00917 % % 00918 % % 00919 % % 00920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00921 % 00922 % GetImageDynamicThreshold() returns the dynamic threshold for an image. 00923 % 00924 % The format of the GetImageDynamicThreshold method is: 00925 % 00926 % MagickBooleanType GetImageDynamicThreshold(const Image *image, 00927 % const double cluster_threshold,const double smooth_threshold, 00928 % PixelInfo *pixel,ExceptionInfo *exception) 00929 % 00930 % A description of each parameter follows. 00931 % 00932 % o image: the image. 00933 % 00934 % o cluster_threshold: This MagickRealType represents the minimum number of 00935 % pixels contained in a hexahedra before it can be considered valid 00936 % (expressed as a percentage). 00937 % 00938 % o smooth_threshold: the smoothing threshold eliminates noise in the second 00939 % derivative of the histogram. As the value is increased, you can expect a 00940 % smoother second derivative. 00941 % 00942 % o pixel: return the dynamic threshold here. 00943 % 00944 % o exception: return any errors or warnings in this structure. 00945 % 00946 */ 00947 MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image, 00948 const double cluster_threshold,const double smooth_threshold, 00949 PixelInfo *pixel,ExceptionInfo *exception) 00950 { 00951 Cluster 00952 *background, 00953 *cluster, 00954 *object, 00955 *head, 00956 *last_cluster, 00957 *next_cluster; 00958 00959 ExtentPacket 00960 blue, 00961 green, 00962 red; 00963 00964 MagickBooleanType 00965 proceed; 00966 00967 MagickRealType 00968 threshold; 00969 00970 register const Quantum 00971 *p; 00972 00973 register ssize_t 00974 i, 00975 x; 00976 00977 short 00978 *extrema[MaxDimension]; 00979 00980 ssize_t 00981 count, 00982 *histogram[MaxDimension], 00983 y; 00984 00985 /* 00986 Allocate histogram and extrema. 00987 */ 00988 assert(image != (Image *) NULL); 00989 assert(image->signature == MagickSignature); 00990 if (image->debug != MagickFalse) 00991 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00992 GetPixelInfo(image,pixel); 00993 for (i=0; i < MaxDimension; i++) 00994 { 00995 histogram[i]=(ssize_t *) AcquireQuantumMemory(256UL,sizeof(**histogram)); 00996 extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram)); 00997 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL)) 00998 { 00999 for (i-- ; i >= 0; i--) 01000 { 01001 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]); 01002 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]); 01003 } 01004 (void) ThrowMagickException(exception,GetMagickModule(), 01005 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 01006 return(MagickFalse); 01007 } 01008 } 01009 /* 01010 Initialize histogram. 01011 */ 01012 InitializeHistogram(image,histogram,exception); 01013 (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau, 01014 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]); 01015 (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau, 01016 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]); 01017 (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau, 01018 (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]); 01019 /* 01020 Form clusters. 01021 */ 01022 cluster=(Cluster *) NULL; 01023 head=(Cluster *) NULL; 01024 (void) ResetMagickMemory(&red,0,sizeof(red)); 01025 (void) ResetMagickMemory(&green,0,sizeof(green)); 01026 (void) ResetMagickMemory(&blue,0,sizeof(blue)); 01027 while (DefineRegion(extrema[Red],&red) != 0) 01028 { 01029 green.index=0; 01030 while (DefineRegion(extrema[Green],&green) != 0) 01031 { 01032 blue.index=0; 01033 while (DefineRegion(extrema[Blue],&blue) != 0) 01034 { 01035 /* 01036 Allocate a new class. 01037 */ 01038 if (head != (Cluster *) NULL) 01039 { 01040 cluster->next=(Cluster *) AcquireMagickMemory( 01041 sizeof(*cluster->next)); 01042 cluster=cluster->next; 01043 } 01044 else 01045 { 01046 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster)); 01047 head=cluster; 01048 } 01049 if (cluster == (Cluster *) NULL) 01050 { 01051 (void) ThrowMagickException(exception,GetMagickModule(), 01052 ResourceLimitError,"MemoryAllocationFailed","`%s'", 01053 image->filename); 01054 return(MagickFalse); 01055 } 01056 /* 01057 Initialize a new class. 01058 */ 01059 cluster->count=0; 01060 cluster->red=red; 01061 cluster->green=green; 01062 cluster->blue=blue; 01063 cluster->next=(Cluster *) NULL; 01064 } 01065 } 01066 } 01067 if (head == (Cluster *) NULL) 01068 { 01069 /* 01070 No classes were identified-- create one. 01071 */ 01072 cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster)); 01073 if (cluster == (Cluster *) NULL) 01074 { 01075 (void) ThrowMagickException(exception,GetMagickModule(), 01076 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 01077 return(MagickFalse); 01078 } 01079 /* 01080 Initialize a new class. 01081 */ 01082 cluster->count=0; 01083 cluster->red=red; 01084 cluster->green=green; 01085 cluster->blue=blue; 01086 cluster->next=(Cluster *) NULL; 01087 head=cluster; 01088 } 01089 /* 01090 Count the pixels for each cluster. 01091 */ 01092 count=0; 01093 for (y=0; y < (ssize_t) image->rows; y++) 01094 { 01095 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 01096 if (p == (const Quantum *) NULL) 01097 break; 01098 for (x=0; x < (ssize_t) image->columns; x++) 01099 { 01100 for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next) 01101 if (((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) >= 01102 (cluster->red.left-SafeMargin)) && 01103 ((ssize_t) ScaleQuantumToChar(GetPixelRed(image,p)) <= 01104 (cluster->red.right+SafeMargin)) && 01105 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) >= 01106 (cluster->green.left-SafeMargin)) && 01107 ((ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p)) <= 01108 (cluster->green.right+SafeMargin)) && 01109 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) >= 01110 (cluster->blue.left-SafeMargin)) && 01111 ((ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p)) <= 01112 (cluster->blue.right+SafeMargin))) 01113 { 01114 /* 01115 Count this pixel. 01116 */ 01117 count++; 01118 cluster->red.center+=(MagickRealType) ScaleQuantumToChar( 01119 GetPixelRed(image,p)); 01120 cluster->green.center+=(MagickRealType) ScaleQuantumToChar( 01121 GetPixelGreen(image,p)); 01122 cluster->blue.center+=(MagickRealType) ScaleQuantumToChar( 01123 GetPixelBlue(image,p)); 01124 cluster->count++; 01125 break; 01126 } 01127 p+=GetPixelChannels(image); 01128 } 01129 proceed=SetImageProgress(image,SegmentImageTag,(MagickOffsetType) y, 01130 2*image->rows); 01131 if (proceed == MagickFalse) 01132 break; 01133 } 01134 /* 01135 Remove clusters that do not meet minimum cluster threshold. 01136 */ 01137 count=0; 01138 last_cluster=head; 01139 next_cluster=head; 01140 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster) 01141 { 01142 next_cluster=cluster->next; 01143 if ((cluster->count > 0) && 01144 (cluster->count >= (count*cluster_threshold/100.0))) 01145 { 01146 /* 01147 Initialize cluster. 01148 */ 01149 cluster->id=count; 01150 cluster->red.center/=cluster->count; 01151 cluster->green.center/=cluster->count; 01152 cluster->blue.center/=cluster->count; 01153 count++; 01154 last_cluster=cluster; 01155 continue; 01156 } 01157 /* 01158 Delete cluster. 01159 */ 01160 if (cluster == head) 01161 head=next_cluster; 01162 else 01163 last_cluster->next=next_cluster; 01164 cluster=(Cluster *) RelinquishMagickMemory(cluster); 01165 } 01166 object=head; 01167 background=head; 01168 if (count > 1) 01169 { 01170 object=head->next; 01171 for (cluster=object; cluster->next != (Cluster *) NULL; ) 01172 { 01173 if (cluster->count < object->count) 01174 object=cluster; 01175 cluster=cluster->next; 01176 } 01177 background=head->next; 01178 for (cluster=background; cluster->next != (Cluster *) NULL; ) 01179 { 01180 if (cluster->count > background->count) 01181 background=cluster; 01182 cluster=cluster->next; 01183 } 01184 } 01185 threshold=(background->red.center+object->red.center)/2.0; 01186 pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char) 01187 (threshold+0.5)); 01188 threshold=(background->green.center+object->green.center)/2.0; 01189 pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char) 01190 (threshold+0.5)); 01191 threshold=(background->blue.center+object->blue.center)/2.0; 01192 pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char) 01193 (threshold+0.5)); 01194 /* 01195 Relinquish resources. 01196 */ 01197 for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster) 01198 { 01199 next_cluster=cluster->next; 01200 cluster=(Cluster *) RelinquishMagickMemory(cluster); 01201 } 01202 for (i=0; i < MaxDimension; i++) 01203 { 01204 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]); 01205 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]); 01206 } 01207 return(MagickTrue); 01208 } 01209 01210 /* 01211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01212 % % 01213 % % 01214 % % 01215 + I n i t i a l i z e H i s t o g r a m % 01216 % % 01217 % % 01218 % % 01219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01220 % 01221 % InitializeHistogram() computes the histogram for an image. 01222 % 01223 % The format of the InitializeHistogram method is: 01224 % 01225 % InitializeHistogram(const Image *image,ssize_t **histogram) 01226 % 01227 % A description of each parameter follows. 01228 % 01229 % o image: Specifies a pointer to an Image structure; returned from 01230 % ReadImage. 01231 % 01232 % o histogram: Specifies an array of integers representing the number 01233 % of pixels for each intensity of a particular color component. 01234 % 01235 */ 01236 static void InitializeHistogram(const Image *image,ssize_t **histogram, 01237 ExceptionInfo *exception) 01238 { 01239 register const Quantum 01240 *p; 01241 01242 register ssize_t 01243 i, 01244 x; 01245 01246 ssize_t 01247 y; 01248 01249 /* 01250 Initialize histogram. 01251 */ 01252 for (i=0; i <= 255; i++) 01253 { 01254 histogram[Red][i]=0; 01255 histogram[Green][i]=0; 01256 histogram[Blue][i]=0; 01257 } 01258 for (y=0; y < (ssize_t) image->rows; y++) 01259 { 01260 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 01261 if (p == (const Quantum *) NULL) 01262 break; 01263 for (x=0; x < (ssize_t) image->columns; x++) 01264 { 01265 histogram[Red][(ssize_t) ScaleQuantumToChar(GetPixelRed(image,p))]++; 01266 histogram[Green][(ssize_t) ScaleQuantumToChar(GetPixelGreen(image,p))]++; 01267 histogram[Blue][(ssize_t) ScaleQuantumToChar(GetPixelBlue(image,p))]++; 01268 p+=GetPixelChannels(image); 01269 } 01270 } 01271 } 01272 01273 /* 01274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01275 % % 01276 % % 01277 % % 01278 + I n i t i a l i z e I n t e r v a l T r e e % 01279 % % 01280 % % 01281 % % 01282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01283 % 01284 % InitializeIntervalTree() initializes an interval tree from the lists of 01285 % zero crossings. 01286 % 01287 % The format of the InitializeIntervalTree method is: 01288 % 01289 % InitializeIntervalTree(IntervalTree **list,ssize_t *number_nodes, 01290 % IntervalTree *node) 01291 % 01292 % A description of each parameter follows. 01293 % 01294 % o zero_crossing: Specifies an array of structures of type ZeroCrossing. 01295 % 01296 % o number_crossings: This size_t specifies the number of elements 01297 % in the zero_crossing array. 01298 % 01299 */ 01300 01301 static void InitializeList(IntervalTree **list,ssize_t *number_nodes, 01302 IntervalTree *node) 01303 { 01304 if (node == (IntervalTree *) NULL) 01305 return; 01306 if (node->child == (IntervalTree *) NULL) 01307 list[(*number_nodes)++]=node; 01308 InitializeList(list,number_nodes,node->sibling); 01309 InitializeList(list,number_nodes,node->child); 01310 } 01311 01312 static void MeanStability(IntervalTree *node) 01313 { 01314 register IntervalTree 01315 *child; 01316 01317 if (node == (IntervalTree *) NULL) 01318 return; 01319 node->mean_stability=0.0; 01320 child=node->child; 01321 if (child != (IntervalTree *) NULL) 01322 { 01323 register ssize_t 01324 count; 01325 01326 register MagickRealType 01327 sum; 01328 01329 sum=0.0; 01330 count=0; 01331 for ( ; child != (IntervalTree *) NULL; child=child->sibling) 01332 { 01333 sum+=child->stability; 01334 count++; 01335 } 01336 node->mean_stability=sum/(MagickRealType) count; 01337 } 01338 MeanStability(node->sibling); 01339 MeanStability(node->child); 01340 } 01341 01342 static void Stability(IntervalTree *node) 01343 { 01344 if (node == (IntervalTree *) NULL) 01345 return; 01346 if (node->child == (IntervalTree *) NULL) 01347 node->stability=0.0; 01348 else 01349 node->stability=node->tau-(node->child)->tau; 01350 Stability(node->sibling); 01351 Stability(node->child); 01352 } 01353 01354 static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing, 01355 const size_t number_crossings) 01356 { 01357 IntervalTree 01358 *head, 01359 **list, 01360 *node, 01361 *root; 01362 01363 register ssize_t 01364 i; 01365 01366 ssize_t 01367 j, 01368 k, 01369 left, 01370 number_nodes; 01371 01372 /* 01373 Allocate interval tree. 01374 */ 01375 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength, 01376 sizeof(*list)); 01377 if (list == (IntervalTree **) NULL) 01378 return((IntervalTree *) NULL); 01379 /* 01380 The root is the entire histogram. 01381 */ 01382 root=(IntervalTree *) AcquireMagickMemory(sizeof(*root)); 01383 root->child=(IntervalTree *) NULL; 01384 root->sibling=(IntervalTree *) NULL; 01385 root->tau=0.0; 01386 root->left=0; 01387 root->right=255; 01388 for (i=(-1); i < (ssize_t) number_crossings; i++) 01389 { 01390 /* 01391 Initialize list with all nodes with no children. 01392 */ 01393 number_nodes=0; 01394 InitializeList(list,&number_nodes,root); 01395 /* 01396 Split list. 01397 */ 01398 for (j=0; j < number_nodes; j++) 01399 { 01400 head=list[j]; 01401 left=head->left; 01402 node=head; 01403 for (k=head->left+1; k < head->right; k++) 01404 { 01405 if (zero_crossing[i+1].crossings[k] != 0) 01406 { 01407 if (node == head) 01408 { 01409 node->child=(IntervalTree *) AcquireMagickMemory( 01410 sizeof(*node->child)); 01411 node=node->child; 01412 } 01413 else 01414 { 01415 node->sibling=(IntervalTree *) AcquireMagickMemory( 01416 sizeof(*node->sibling)); 01417 node=node->sibling; 01418 } 01419 node->tau=zero_crossing[i+1].tau; 01420 node->child=(IntervalTree *) NULL; 01421 node->sibling=(IntervalTree *) NULL; 01422 node->left=left; 01423 node->right=k; 01424 left=k; 01425 } 01426 } 01427 if (left != head->left) 01428 { 01429 node->sibling=(IntervalTree *) AcquireMagickMemory( 01430 sizeof(*node->sibling)); 01431 node=node->sibling; 01432 node->tau=zero_crossing[i+1].tau; 01433 node->child=(IntervalTree *) NULL; 01434 node->sibling=(IntervalTree *) NULL; 01435 node->left=left; 01436 node->right=head->right; 01437 } 01438 } 01439 } 01440 /* 01441 Determine the stability: difference between a nodes tau and its child. 01442 */ 01443 Stability(root->child); 01444 MeanStability(root->child); 01445 list=(IntervalTree **) RelinquishMagickMemory(list); 01446 return(root); 01447 } 01448 01449 /* 01450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01451 % % 01452 % % 01453 % % 01454 + O p t i m a l T a u % 01455 % % 01456 % % 01457 % % 01458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01459 % 01460 % OptimalTau() finds the optimal tau for each band of the histogram. 01461 % 01462 % The format of the OptimalTau method is: 01463 % 01464 % MagickRealType OptimalTau(const ssize_t *histogram,const double max_tau, 01465 % const double min_tau,const double delta_tau, 01466 % const double smooth_threshold,short *extrema) 01467 % 01468 % A description of each parameter follows. 01469 % 01470 % o histogram: Specifies an array of integers representing the number 01471 % of pixels for each intensity of a particular color component. 01472 % 01473 % o extrema: Specifies a pointer to an array of integers. They 01474 % represent the peaks and valleys of the histogram for each color 01475 % component. 01476 % 01477 */ 01478 01479 static void ActiveNodes(IntervalTree **list,ssize_t *number_nodes, 01480 IntervalTree *node) 01481 { 01482 if (node == (IntervalTree *) NULL) 01483 return; 01484 if (node->stability >= node->mean_stability) 01485 { 01486 list[(*number_nodes)++]=node; 01487 ActiveNodes(list,number_nodes,node->sibling); 01488 } 01489 else 01490 { 01491 ActiveNodes(list,number_nodes,node->sibling); 01492 ActiveNodes(list,number_nodes,node->child); 01493 } 01494 } 01495 01496 static void FreeNodes(IntervalTree *node) 01497 { 01498 if (node == (IntervalTree *) NULL) 01499 return; 01500 FreeNodes(node->sibling); 01501 FreeNodes(node->child); 01502 node=(IntervalTree *) RelinquishMagickMemory(node); 01503 } 01504 01505 static MagickRealType OptimalTau(const ssize_t *histogram,const double max_tau, 01506 const double min_tau,const double delta_tau,const double smooth_threshold, 01507 short *extrema) 01508 { 01509 IntervalTree 01510 **list, 01511 *node, 01512 *root; 01513 01514 MagickBooleanType 01515 peak; 01516 01517 MagickRealType 01518 average_tau, 01519 *derivative, 01520 *second_derivative, 01521 tau, 01522 value; 01523 01524 register ssize_t 01525 i, 01526 x; 01527 01528 size_t 01529 count, 01530 number_crossings; 01531 01532 ssize_t 01533 index, 01534 j, 01535 k, 01536 number_nodes; 01537 01538 ZeroCrossing 01539 *zero_crossing; 01540 01541 /* 01542 Allocate interval tree. 01543 */ 01544 list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength, 01545 sizeof(*list)); 01546 if (list == (IntervalTree **) NULL) 01547 return(0.0); 01548 /* 01549 Allocate zero crossing list. 01550 */ 01551 count=(size_t) ((max_tau-min_tau)/delta_tau)+2; 01552 zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count, 01553 sizeof(*zero_crossing)); 01554 if (zero_crossing == (ZeroCrossing *) NULL) 01555 return(0.0); 01556 for (i=0; i < (ssize_t) count; i++) 01557 zero_crossing[i].tau=(-1.0); 01558 /* 01559 Initialize zero crossing list. 01560 */ 01561 derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative)); 01562 second_derivative=(MagickRealType *) AcquireQuantumMemory(256, 01563 sizeof(*second_derivative)); 01564 if ((derivative == (MagickRealType *) NULL) || 01565 (second_derivative == (MagickRealType *) NULL)) 01566 ThrowFatalException(ResourceLimitFatalError, 01567 "UnableToAllocateDerivatives"); 01568 i=0; 01569 for (tau=max_tau; tau >= min_tau; tau-=delta_tau) 01570 { 01571 zero_crossing[i].tau=tau; 01572 ScaleSpace(histogram,tau,zero_crossing[i].histogram); 01573 DerivativeHistogram(zero_crossing[i].histogram,derivative); 01574 DerivativeHistogram(derivative,second_derivative); 01575 ZeroCrossHistogram(second_derivative,smooth_threshold, 01576 zero_crossing[i].crossings); 01577 i++; 01578 } 01579 /* 01580 Add an entry for the original histogram. 01581 */ 01582 zero_crossing[i].tau=0.0; 01583 for (j=0; j <= 255; j++) 01584 zero_crossing[i].histogram[j]=(MagickRealType) histogram[j]; 01585 DerivativeHistogram(zero_crossing[i].histogram,derivative); 01586 DerivativeHistogram(derivative,second_derivative); 01587 ZeroCrossHistogram(second_derivative,smooth_threshold, 01588 zero_crossing[i].crossings); 01589 number_crossings=(size_t) i; 01590 derivative=(MagickRealType *) RelinquishMagickMemory(derivative); 01591 second_derivative=(MagickRealType *) 01592 RelinquishMagickMemory(second_derivative); 01593 /* 01594 Ensure the scale-space fingerprints form lines in scale-space, not loops. 01595 */ 01596 ConsolidateCrossings(zero_crossing,number_crossings); 01597 /* 01598 Force endpoints to be included in the interval. 01599 */ 01600 for (i=0; i <= (ssize_t) number_crossings; i++) 01601 { 01602 for (j=0; j < 255; j++) 01603 if (zero_crossing[i].crossings[j] != 0) 01604 break; 01605 zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]); 01606 for (j=255; j > 0; j--) 01607 if (zero_crossing[i].crossings[j] != 0) 01608 break; 01609 zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]); 01610 } 01611 /* 01612 Initialize interval tree. 01613 */ 01614 root=InitializeIntervalTree(zero_crossing,number_crossings); 01615 if (root == (IntervalTree *) NULL) 01616 return(0.0); 01617 /* 01618 Find active nodes: stability is greater (or equal) to the mean stability of 01619 its children. 01620 */ 01621 number_nodes=0; 01622 ActiveNodes(list,&number_nodes,root->child); 01623 /* 01624 Initialize extrema. 01625 */ 01626 for (i=0; i <= 255; i++) 01627 extrema[i]=0; 01628 for (i=0; i < number_nodes; i++) 01629 { 01630 /* 01631 Find this tau in zero crossings list. 01632 */ 01633 k=0; 01634 node=list[i]; 01635 for (j=0; j <= (ssize_t) number_crossings; j++) 01636 if (zero_crossing[j].tau == node->tau) 01637 k=j; 01638 /* 01639 Find the value of the peak. 01640 */ 01641 peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue : 01642 MagickFalse; 01643 index=node->left; 01644 value=zero_crossing[k].histogram[index]; 01645 for (x=node->left; x <= node->right; x++) 01646 { 01647 if (peak != MagickFalse) 01648 { 01649 if (zero_crossing[k].histogram[x] > value) 01650 { 01651 value=zero_crossing[k].histogram[x]; 01652 index=x; 01653 } 01654 } 01655 else 01656 if (zero_crossing[k].histogram[x] < value) 01657 { 01658 value=zero_crossing[k].histogram[x]; 01659 index=x; 01660 } 01661 } 01662 for (x=node->left; x <= node->right; x++) 01663 { 01664 if (index == 0) 01665 index=256; 01666 if (peak != MagickFalse) 01667 extrema[x]=(short) index; 01668 else 01669 extrema[x]=(short) (-index); 01670 } 01671 } 01672 /* 01673 Determine the average tau. 01674 */ 01675 average_tau=0.0; 01676 for (i=0; i < number_nodes; i++) 01677 average_tau+=list[i]->tau; 01678 average_tau/=(MagickRealType) number_nodes; 01679 /* 01680 Relinquish resources. 01681 */ 01682 FreeNodes(root); 01683 zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing); 01684 list=(IntervalTree **) RelinquishMagickMemory(list); 01685 return(average_tau); 01686 } 01687 01688 /* 01689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01690 % % 01691 % % 01692 % % 01693 + S c a l e S p a c e % 01694 % % 01695 % % 01696 % % 01697 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01698 % 01699 % ScaleSpace() performs a scale-space filter on the 1D histogram. 01700 % 01701 % The format of the ScaleSpace method is: 01702 % 01703 % ScaleSpace(const ssize_t *histogram,const MagickRealType tau, 01704 % MagickRealType *scale_histogram) 01705 % 01706 % A description of each parameter follows. 01707 % 01708 % o histogram: Specifies an array of MagickRealTypes representing the number 01709 % of pixels for each intensity of a particular color component. 01710 % 01711 */ 01712 01713 static void ScaleSpace(const ssize_t *histogram,const MagickRealType tau, 01714 MagickRealType *scale_histogram) 01715 { 01716 MagickRealType 01717 alpha, 01718 beta, 01719 *gamma, 01720 sum; 01721 01722 register ssize_t 01723 u, 01724 x; 01725 01726 gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma)); 01727 if (gamma == (MagickRealType *) NULL) 01728 ThrowFatalException(ResourceLimitFatalError, 01729 "UnableToAllocateGammaMap"); 01730 alpha=1.0/(tau*sqrt(2.0*MagickPI)); 01731 beta=(-1.0/(2.0*tau*tau)); 01732 for (x=0; x <= 255; x++) 01733 gamma[x]=0.0; 01734 for (x=0; x <= 255; x++) 01735 { 01736 gamma[x]=exp((double) beta*x*x); 01737 if (gamma[x] < MagickEpsilon) 01738 break; 01739 } 01740 for (x=0; x <= 255; x++) 01741 { 01742 sum=0.0; 01743 for (u=0; u <= 255; u++) 01744 sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)]; 01745 scale_histogram[x]=alpha*sum; 01746 } 01747 gamma=(MagickRealType *) RelinquishMagickMemory(gamma); 01748 } 01749 01750 /* 01751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01752 % % 01753 % % 01754 % % 01755 % S e g m e n t I m a g e % 01756 % % 01757 % % 01758 % % 01759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01760 % 01761 % SegmentImage() segment an image by analyzing the histograms of the color 01762 % components and identifying units that are homogeneous with the fuzzy 01763 % C-means technique. 01764 % 01765 % The format of the SegmentImage method is: 01766 % 01767 % MagickBooleanType SegmentImage(Image *image, 01768 % const ColorspaceType colorspace,const MagickBooleanType verbose, 01769 % const double cluster_threshold,const double smooth_threshold, 01770 % ExceptionInfo *exception) 01771 % 01772 % A description of each parameter follows. 01773 % 01774 % o image: the image. 01775 % 01776 % o colorspace: Indicate the colorspace. 01777 % 01778 % o verbose: Set to MagickTrue to print detailed information about the 01779 % identified classes. 01780 % 01781 % o cluster_threshold: This represents the minimum number of pixels 01782 % contained in a hexahedra before it can be considered valid (expressed 01783 % as a percentage). 01784 % 01785 % o smooth_threshold: the smoothing threshold eliminates noise in the second 01786 % derivative of the histogram. As the value is increased, you can expect a 01787 % smoother second derivative. 01788 % 01789 % o exception: return any errors or warnings in this structure. 01790 % 01791 */ 01792 MagickExport MagickBooleanType SegmentImage(Image *image, 01793 const ColorspaceType colorspace,const MagickBooleanType verbose, 01794 const double cluster_threshold,const double smooth_threshold, 01795 ExceptionInfo *exception) 01796 { 01797 MagickBooleanType 01798 status; 01799 01800 register ssize_t 01801 i; 01802 01803 short 01804 *extrema[MaxDimension]; 01805 01806 ssize_t 01807 *histogram[MaxDimension]; 01808 01809 /* 01810 Allocate histogram and extrema. 01811 */ 01812 assert(image != (Image *) NULL); 01813 assert(image->signature == MagickSignature); 01814 if (image->debug != MagickFalse) 01815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01816 for (i=0; i < MaxDimension; i++) 01817 { 01818 histogram[i]=(ssize_t *) AcquireQuantumMemory(256,sizeof(**histogram)); 01819 extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema)); 01820 if ((histogram[i] == (ssize_t *) NULL) || (extrema[i] == (short *) NULL)) 01821 { 01822 for (i-- ; i >= 0; i--) 01823 { 01824 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]); 01825 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]); 01826 } 01827 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01828 image->filename) 01829 } 01830 } 01831 if (IsRGBColorspace(colorspace) == MagickFalse) 01832 (void) TransformImageColorspace(image,colorspace,exception); 01833 /* 01834 Initialize histogram. 01835 */ 01836 InitializeHistogram(image,histogram,exception); 01837 (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau, 01838 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]); 01839 (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau, 01840 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]); 01841 (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau, 01842 smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]); 01843 /* 01844 Classify using the fuzzy c-Means technique. 01845 */ 01846 status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose, 01847 exception); 01848 if (IsRGBColorspace(colorspace) == MagickFalse) 01849 (void) TransformImageColorspace(image,colorspace,exception); 01850 /* 01851 Relinquish resources. 01852 */ 01853 for (i=0; i < MaxDimension; i++) 01854 { 01855 extrema[i]=(short *) RelinquishMagickMemory(extrema[i]); 01856 histogram[i]=(ssize_t *) RelinquishMagickMemory(histogram[i]); 01857 } 01858 return(status); 01859 } 01860 01861 /* 01862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01863 % % 01864 % % 01865 % % 01866 + Z e r o C r o s s H i s t o g r a m % 01867 % % 01868 % % 01869 % % 01870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01871 % 01872 % ZeroCrossHistogram() find the zero crossings in a histogram and marks 01873 % directions as: 1 is negative to positive; 0 is zero crossing; and -1 01874 % is positive to negative. 01875 % 01876 % The format of the ZeroCrossHistogram method is: 01877 % 01878 % ZeroCrossHistogram(MagickRealType *second_derivative, 01879 % const MagickRealType smooth_threshold,short *crossings) 01880 % 01881 % A description of each parameter follows. 01882 % 01883 % o second_derivative: Specifies an array of MagickRealTypes representing the 01884 % second derivative of the histogram of a particular color component. 01885 % 01886 % o crossings: This array of integers is initialized with 01887 % -1, 0, or 1 representing the slope of the first derivative of the 01888 % of a particular color component. 01889 % 01890 */ 01891 static void ZeroCrossHistogram(MagickRealType *second_derivative, 01892 const MagickRealType smooth_threshold,short *crossings) 01893 { 01894 register ssize_t 01895 i; 01896 01897 ssize_t 01898 parity; 01899 01900 /* 01901 Merge low numbers to zero to help prevent noise. 01902 */ 01903 for (i=0; i <= 255; i++) 01904 if ((second_derivative[i] < smooth_threshold) && 01905 (second_derivative[i] >= -smooth_threshold)) 01906 second_derivative[i]=0.0; 01907 /* 01908 Mark zero crossings. 01909 */ 01910 parity=0; 01911 for (i=0; i <= 255; i++) 01912 { 01913 crossings[i]=0; 01914 if (second_derivative[i] < 0.0) 01915 { 01916 if (parity > 0) 01917 crossings[i]=(-1); 01918 parity=1; 01919 } 01920 else 01921 if (second_derivative[i] > 0.0) 01922 { 01923 if (parity < 0) 01924 crossings[i]=1; 01925 parity=(-1); 01926 } 01927 } 01928 }