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