blob: b88f0ff7051717e633b791daa09998afe0d7963d [file] [log] [blame]
cristy6e0b3bc2014-10-19 17:51:42 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% V V IIIII SSSSS IIIII OOO N N %
7% V V I SS I O O NN N %
8% V V I SSS I O O N N N %
9% V V I SS I O O N NN %
10% V IIIII SSSSS IIIII OOO N N %
11% %
12% %
13% MagickCore Computer Vision Methods %
14% %
15% Software Design %
16% Cristy %
17% September 2014 %
18% %
19% %
20% Copyright 1999-2014 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% http://www.imagemagick.org/script/license.php %
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%
37*/
38
39#include "MagickCore/studio.h"
cristy016b7642014-10-25 13:09:57 +000040#include "MagickCore/artifact.h"
cristy6e0b3bc2014-10-19 17:51:42 +000041#include "MagickCore/blob.h"
42#include "MagickCore/cache-view.h"
43#include "MagickCore/color.h"
44#include "MagickCore/color-private.h"
45#include "MagickCore/colorspace.h"
46#include "MagickCore/constitute.h"
47#include "MagickCore/decorate.h"
48#include "MagickCore/distort.h"
49#include "MagickCore/draw.h"
50#include "MagickCore/enhance.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/effect.h"
54#include "MagickCore/gem.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/list.h"
58#include "MagickCore/log.h"
59#include "MagickCore/matrix.h"
60#include "MagickCore/memory_.h"
61#include "MagickCore/memory-private.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/montage.h"
65#include "MagickCore/morphology.h"
66#include "MagickCore/morphology-private.h"
67#include "MagickCore/opencl-private.h"
68#include "MagickCore/paint.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/pixel-private.h"
71#include "MagickCore/property.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/resource_.h"
74#include "MagickCore/signature-private.h"
75#include "MagickCore/string_.h"
cristyc6ca5712014-10-26 22:13:07 +000076#include "MagickCore/string-private.h"
cristy6e0b3bc2014-10-19 17:51:42 +000077#include "MagickCore/thread-private.h"
cristy016b7642014-10-25 13:09:57 +000078#include "MagickCore/token.h"
cristy6e0b3bc2014-10-19 17:51:42 +000079#include "MagickCore/vision.h"
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83% %
84% %
85% %
86% C o n n e c t e d C o m p o n e n t s I m a g e %
87% %
88% %
89% %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
92% ConnectedComponentsImage() returns the connected-components of the image
93% uniquely labeled. Choose from 4 or 8-way connectivity.
94%
95% The format of the ConnectedComponentsImage method is:
96%
97% Image *ConnectedComponentsImage(const Image *image,
98% const size_t connectivity,ExceptionInfo *exception)
99%
100% A description of each parameter follows:
101%
102% o image: the image.
103%
cristy016b7642014-10-25 13:09:57 +0000104% o connectivity: how many neighbors to visit, choose from 4 or 8.
cristy6e0b3bc2014-10-19 17:51:42 +0000105%
106% o exception: return any errors or warnings in this structure.
107%
108*/
cristy016b7642014-10-25 13:09:57 +0000109
110typedef struct _CCObject
111{
112 ssize_t
113 id;
114
115 RectangleInfo
116 bounding_box;
117
cristyc6ca5712014-10-26 22:13:07 +0000118 PixelInfo
119 color;
120
cristy016b7642014-10-25 13:09:57 +0000121 PointInfo
122 centroid;
123
124 ssize_t
125 area;
126} CCObject;
127
128static int CCObjectCompare(const void *x,const void *y)
129{
130 CCObject
131 *p,
132 *q;
133
134 p=(CCObject *) x;
135 q=(CCObject *) y;
136 return((int) (q->area-(ssize_t) p->area));
137}
138
139static MagickBooleanType ConnectedComponentsStatistics(const Image *image,
cristyc6ca5712014-10-26 22:13:07 +0000140 const Image *component_image,const size_t number_objects,
141 ExceptionInfo *exception)
cristy016b7642014-10-25 13:09:57 +0000142{
143 CacheView
cristyc6ca5712014-10-26 22:13:07 +0000144 *component_view,
cristy016b7642014-10-25 13:09:57 +0000145 *image_view;
146
147 CCObject
148 *object;
149
150 MagickBooleanType
151 status;
152
153 register ssize_t
154 i;
155
156 ssize_t
157 y;
158
159 object=(CCObject *) AcquireQuantumMemory(number_objects,sizeof(*object));
160 if (object == (CCObject *) NULL)
161 {
162 (void) ThrowMagickException(exception,GetMagickModule(),
163 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
164 return(MagickFalse);
165 }
166 (void) ResetMagickMemory(object,0,number_objects*sizeof(*object));
167 for (i=0; i < (ssize_t) number_objects; i++)
168 {
169 object[i].id=i;
cristyc6ca5712014-10-26 22:13:07 +0000170 object[i].bounding_box.x=(ssize_t) component_image->columns;
171 object[i].bounding_box.y=(ssize_t) component_image->rows;
172 GetPixelInfo(image,&object[i].color);
cristy016b7642014-10-25 13:09:57 +0000173 }
cristy016b7642014-10-25 13:09:57 +0000174 status=MagickTrue;
175 image_view=AcquireVirtualCacheView(image,exception);
cristyc6ca5712014-10-26 22:13:07 +0000176 component_view=AcquireVirtualCacheView(component_image,exception);
cristy016b7642014-10-25 13:09:57 +0000177 for (y=0; y < (ssize_t) image->rows; y++)
178 {
179 register const Quantum
cristyc6ca5712014-10-26 22:13:07 +0000180 *restrict p,
181 *restrict q;
cristy016b7642014-10-25 13:09:57 +0000182
183 register ssize_t
184 x;
185
186 if (status == MagickFalse)
187 continue;
188 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristyc6ca5712014-10-26 22:13:07 +0000189 q=GetCacheViewVirtualPixels(component_view,0,y,component_image->columns,1,
190 exception);
191 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
cristy016b7642014-10-25 13:09:57 +0000192 {
193 status=MagickFalse;
194 continue;
195 }
196 for (x=0; x < (ssize_t) image->columns; x++)
197 {
cristyc6ca5712014-10-26 22:13:07 +0000198 i=(ssize_t) *q;
cristy016b7642014-10-25 13:09:57 +0000199 if (x < object[i].bounding_box.x)
200 object[i].bounding_box.x=x;
201 if (x > (ssize_t) object[i].bounding_box.width)
202 object[i].bounding_box.width=(size_t) x;
203 if (y < object[i].bounding_box.y)
204 object[i].bounding_box.y=y;
205 if (y > (ssize_t) object[i].bounding_box.height)
206 object[i].bounding_box.height=(size_t) y;
cristyc6ca5712014-10-26 22:13:07 +0000207 object[i].color.red+=GetPixelRed(image,p);
208 object[i].color.green+=GetPixelGreen(image,p);
209 object[i].color.blue+=GetPixelBlue(image,p);
210 object[i].color.alpha+=GetPixelAlpha(image,p);
211 object[i].color.black+=GetPixelBlack(image,p);
cristyc75924c2014-10-25 14:58:34 +0000212 object[i].centroid.x+=x;
213 object[i].centroid.y+=y;
cristy016b7642014-10-25 13:09:57 +0000214 object[i].area++;
215 p+=GetPixelChannels(image);
cristyc6ca5712014-10-26 22:13:07 +0000216 q+=GetPixelChannels(component_image);
cristy016b7642014-10-25 13:09:57 +0000217 }
218 }
219 for (i=0; i < (ssize_t) number_objects; i++)
220 {
221 object[i].bounding_box.width-=(object[i].bounding_box.x-1);
222 object[i].bounding_box.height-=(object[i].bounding_box.y-1);
cristy40898402014-10-26 22:22:02 +0000223 object[i].color.red=(MagickRealType) (object[i].color.red/
224 object[i].area);
225 object[i].color.green=(MagickRealType) (object[i].color.green/
226 object[i].area);
227 object[i].color.blue=(MagickRealType) (object[i].color.blue/
228 object[i].area);
229 object[i].color.alpha=(MagickRealType) (object[i].color.alpha/
230 object[i].area);
231 object[i].color.black=(MagickRealType) (object[i].color.black/
232 object[i].area);
cristyc75924c2014-10-25 14:58:34 +0000233 object[i].centroid.x=object[i].centroid.x/object[i].area;
234 object[i].centroid.y=object[i].centroid.y/object[i].area;
cristy016b7642014-10-25 13:09:57 +0000235 }
cristyc6ca5712014-10-26 22:13:07 +0000236 component_view=DestroyCacheView(component_view);
cristy016b7642014-10-25 13:09:57 +0000237 image_view=DestroyCacheView(image_view);
238 qsort((void *) object,number_objects,sizeof(*object),CCObjectCompare);
cristyc6ca5712014-10-26 22:13:07 +0000239 (void) fprintf(stdout,
240 "Objects (id: bounding-box centroid area mean-color):\n");
cristy016b7642014-10-25 13:09:57 +0000241 for (i=0; i < (ssize_t) number_objects; i++)
242 {
cristyc6ca5712014-10-26 22:13:07 +0000243 char
244 mean_color[MaxTextExtent];
245
cristy016b7642014-10-25 13:09:57 +0000246 if (status == MagickFalse)
247 break;
cristyc6ca5712014-10-26 22:13:07 +0000248 GetColorTuple(&object[i].color,MagickFalse,mean_color);
cristy016b7642014-10-25 13:09:57 +0000249 (void) fprintf(stdout,
cristyc6ca5712014-10-26 22:13:07 +0000250 " %.20g: %.20gx%.20g%+.20g%+.20g %+.1f,%+.1f %.20g %s\n",(double)
cristy016b7642014-10-25 13:09:57 +0000251 object[i].id,(double) object[i].bounding_box.width,(double)
252 object[i].bounding_box.height,(double) object[i].bounding_box.x,
253 (double) object[i].bounding_box.y,object[i].centroid.x,
cristyc6ca5712014-10-26 22:13:07 +0000254 object[i].centroid.y,(double) object[i].area,mean_color);
255 }
256 object=(CCObject *) RelinquishMagickMemory(object);
257 return(status);
258}
259
260static MagickBooleanType MergeConnectedComponents(const Image *image,
261 const Image *component_image,const size_t number_objects,
262 const double area_threshold,ExceptionInfo *exception)
263{
264 CacheView
265 *component_view,
266 *image_view;
267
268 CCObject
269 *object;
270
271 MagickBooleanType
272 status;
273
274 register ssize_t
275 i;
276
277 ssize_t
278 y;
279
280 object=(CCObject *) AcquireQuantumMemory(number_objects,sizeof(*object));
281 if (object == (CCObject *) NULL)
282 {
283 (void) ThrowMagickException(exception,GetMagickModule(),
284 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
285 return(MagickFalse);
286 }
287 (void) ResetMagickMemory(object,0,number_objects*sizeof(*object));
288 for (i=0; i < (ssize_t) number_objects; i++)
289 {
290 object[i].id=i;
291 object[i].bounding_box.x=(ssize_t) component_image->columns;
292 object[i].bounding_box.y=(ssize_t) component_image->rows;
293 GetPixelInfo(image,&object[i].color);
294 }
295 status=MagickTrue;
296 image_view=AcquireVirtualCacheView(image,exception);
297 component_view=AcquireVirtualCacheView(component_image,exception);
298 for (y=0; y < (ssize_t) image->rows; y++)
299 {
300 register const Quantum
301 *restrict p,
302 *restrict q;
303
304 register ssize_t
305 x;
306
307 if (status == MagickFalse)
308 continue;
309 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
310 q=GetCacheViewVirtualPixels(component_view,0,y,component_image->columns,1,
311 exception);
312 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
313 {
314 status=MagickFalse;
315 continue;
316 }
317 for (x=0; x < (ssize_t) image->columns; x++)
318 {
319 i=(ssize_t) *q;
320 if (x < object[i].bounding_box.x)
321 object[i].bounding_box.x=x;
322 if (x > (ssize_t) object[i].bounding_box.width)
323 object[i].bounding_box.width=(size_t) x;
324 if (y < object[i].bounding_box.y)
325 object[i].bounding_box.y=y;
326 if (y > (ssize_t) object[i].bounding_box.height)
327 object[i].bounding_box.height=(size_t) y;
328 object[i].color.red+=GetPixelRed(image,p);
329 object[i].color.green+=GetPixelGreen(image,p);
330 object[i].color.blue+=GetPixelBlue(image,p);
331 object[i].color.alpha+=GetPixelAlpha(image,p);
332 object[i].color.black+=GetPixelBlack(image,p);
333 object[i].centroid.x+=x;
334 object[i].centroid.y+=y;
335 object[i].area++;
336 p+=GetPixelChannels(image);
337 q+=GetPixelChannels(component_image);
338 }
339 }
340 for (i=0; i < (ssize_t) number_objects; i++)
341 {
342 object[i].bounding_box.width-=(object[i].bounding_box.x-1);
343 object[i].bounding_box.height-=(object[i].bounding_box.y-1);
cristy40898402014-10-26 22:22:02 +0000344 object[i].color.red=(MagickRealType) (object[i].color.red/
345 object[i].area);
346 object[i].color.green=(MagickRealType) (object[i].color.green/
347 object[i].area);
348 object[i].color.blue=(MagickRealType) (object[i].color.blue/
349 object[i].area);
350 object[i].color.alpha=(MagickRealType) (object[i].color.alpha/
351 object[i].area);
352 object[i].color.black=(MagickRealType) (object[i].color.black/
353 object[i].area);
cristyc6ca5712014-10-26 22:13:07 +0000354 object[i].centroid.x=object[i].centroid.x/object[i].area;
355 object[i].centroid.y=object[i].centroid.y/object[i].area;
356 }
357 component_view=DestroyCacheView(component_view);
358 image_view=DestroyCacheView(image_view);
359 qsort((void *) object,number_objects,sizeof(*object),CCObjectCompare);
360 (void) fprintf(stdout,
361 "Objects (id: bounding-box centroid area mean-color):\n");
362 for (i=0; i < (ssize_t) number_objects; i++)
363 {
364 char
365 mean_color[MaxTextExtent];
366
367 if (status == MagickFalse)
368 break;
369 GetColorTuple(&object[i].color,MagickFalse,mean_color);
370 (void) fprintf(stdout,
371 " %.20g: %.20gx%.20g%+.20g%+.20g %+.1f,%+.1f %.20g %s\n",(double)
372 object[i].id,(double) object[i].bounding_box.width,(double)
373 object[i].bounding_box.height,(double) object[i].bounding_box.x,
374 (double) object[i].bounding_box.y,object[i].centroid.x,
375 object[i].centroid.y,(double) object[i].area,mean_color);
cristy016b7642014-10-25 13:09:57 +0000376 }
377 object=(CCObject *) RelinquishMagickMemory(object);
378 return(status);
379}
380
cristy6e0b3bc2014-10-19 17:51:42 +0000381MagickExport Image *ConnectedComponentsImage(const Image *image,
382 const size_t connectivity,ExceptionInfo *exception)
383{
384#define ConnectedComponentsImageTag "ConnectedComponents/Image"
385
386 CacheView
387 *image_view,
388 *component_view;
389
cristy016b7642014-10-25 13:09:57 +0000390 const char
391 *artifact;
392
cristyc6ca5712014-10-26 22:13:07 +0000393 double
394 area_threshold;
395
cristy6e0b3bc2014-10-19 17:51:42 +0000396 Image
397 *component_image;
398
399 MagickBooleanType
400 status;
401
402 MagickOffsetType
403 progress;
404
cristy016b7642014-10-25 13:09:57 +0000405 MatrixInfo
406 *equivalences;
407
408 size_t
409 size;
410
cristy6e0b3bc2014-10-19 17:51:42 +0000411 ssize_t
cristy016b7642014-10-25 13:09:57 +0000412 n,
cristy6e0b3bc2014-10-19 17:51:42 +0000413 y;
414
415 /*
416 Initialize connected components image attributes.
417 */
418 assert(image != (Image *) NULL);
419 assert(image->signature == MagickSignature);
420 if (image->debug != MagickFalse)
421 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
422 assert(exception != (ExceptionInfo *) NULL);
423 assert(exception->signature == MagickSignature);
424 component_image=CloneImage(image,image->columns,image->rows,MagickTrue,
425 exception);
426 if (component_image == (Image *) NULL)
427 return((Image *) NULL);
cristy016b7642014-10-25 13:09:57 +0000428 component_image->depth=MAGICKCORE_QUANTUM_DEPTH;
429 component_image->colorspace=GRAYColorspace;
cristy6e0b3bc2014-10-19 17:51:42 +0000430 if (SetImageStorageClass(component_image,DirectClass,exception) == MagickFalse)
431 {
432 component_image=DestroyImage(component_image);
433 return((Image *) NULL);
434 }
435 /*
cristy016b7642014-10-25 13:09:57 +0000436 Initialize connected components equivalences.
437 */
438 size=image->columns*image->rows;
439 if (image->columns != (size/image->rows))
440 {
441 component_image=DestroyImage(component_image);
442 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
443 }
444 equivalences=AcquireMatrixInfo(size,1,sizeof(ssize_t),exception);
445 if (equivalences == (MatrixInfo *) NULL)
446 {
447 component_image=DestroyImage(component_image);
448 return((Image *) NULL);
449 }
450 for (n=0; n < (ssize_t) (image->columns*image->rows); n++)
451 status=SetMatrixElement(equivalences,n,0,&n);
452 /*
453 Find connected components.
cristy6e0b3bc2014-10-19 17:51:42 +0000454 */
455 status=MagickTrue;
456 progress=0;
457 image_view=AcquireVirtualCacheView(image,exception);
cristy016b7642014-10-25 13:09:57 +0000458 for (n=0; n < (ssize_t) (connectivity > 4 ? 4 : 2); n++)
459 {
460 ssize_t
461 connect4[2][2] = { { -1, 0 }, { 0, -1 } },
462 connect8[4][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 } },
463 dx,
464 dy;
465
466 if (status == MagickFalse)
467 continue;
468 dy=connectivity > 4 ? connect8[n][0] : connect4[n][0];
469 dx=connectivity > 4 ? connect8[n][1] : connect4[n][1];
470 for (y=0; y < (ssize_t) image->rows; y++)
471 {
472 register const Quantum
473 *restrict p;
474
475 register ssize_t
476 x;
477
478 if (status == MagickFalse)
479 continue;
480 p=GetCacheViewVirtualPixels(image_view,0,y-1,image->columns,3,exception);
481 if (p == (const Quantum *) NULL)
482 {
483 status=MagickFalse;
484 continue;
485 }
cristy00f8eb42014-10-25 13:46:10 +0000486 p+=image->columns*GetPixelChannels(image);
cristy016b7642014-10-25 13:09:57 +0000487 for (x=0; x < (ssize_t) image->columns; x++)
488 {
489 PixelInfo
490 pixel,
491 target;
492
493 ssize_t
494 neighbor_offset,
495 object,
cristy00f8eb42014-10-25 13:46:10 +0000496 offset,
cristy016b7642014-10-25 13:09:57 +0000497 ox,
498 oy,
cristy016b7642014-10-25 13:09:57 +0000499 root;
500
501 /*
502 Is neighbor an authentic pixel and a different color than the pixel?
503 */
cristy00f8eb42014-10-25 13:46:10 +0000504 GetPixelInfoPixel(image,p,&pixel);
cristy016b7642014-10-25 13:09:57 +0000505 neighbor_offset=dy*(image->columns*GetPixelChannels(image))+dx*
506 GetPixelChannels(image);
cristy016b7642014-10-25 13:09:57 +0000507 GetPixelInfoPixel(image,p+neighbor_offset,&target);
508 if (((x+dx) < 0) || ((x+dx) >= (ssize_t) image->columns) ||
509 ((y+dy) < 0) || ((y+dy) >= (ssize_t) image->rows) ||
510 (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse))
511 {
512 p+=GetPixelChannels(image);
513 continue;
514 }
515 /*
516 Resolve this equivalence.
517 */
cristy00f8eb42014-10-25 13:46:10 +0000518 offset=y*image->columns+x;
519 neighbor_offset=dy*image->columns+dx;
520 ox=offset;
cristy016b7642014-10-25 13:09:57 +0000521 status=GetMatrixElement(equivalences,ox,0,&object);
522 while (object != ox) {
523 ox=object;
524 status=GetMatrixElement(equivalences,ox,0,&object);
525 }
cristy00f8eb42014-10-25 13:46:10 +0000526 oy=offset+neighbor_offset;
cristy016b7642014-10-25 13:09:57 +0000527 status=GetMatrixElement(equivalences,oy,0,&object);
528 while (object != oy) {
529 oy=object;
530 status=GetMatrixElement(equivalences,oy,0,&object);
531 }
532 if (ox < oy)
533 {
534 status=SetMatrixElement(equivalences,oy,0,&ox);
535 root=ox;
536 }
537 else
538 {
539 status=SetMatrixElement(equivalences,ox,0,&oy);
540 root=oy;
541 }
cristy00f8eb42014-10-25 13:46:10 +0000542 ox=offset;
cristy016b7642014-10-25 13:09:57 +0000543 status=GetMatrixElement(equivalences,ox,0,&object);
544 while (object != root) {
545 status=GetMatrixElement(equivalences,ox,0,&object);
546 status=SetMatrixElement(equivalences,ox,0,&root);
547 }
cristy00f8eb42014-10-25 13:46:10 +0000548 oy=offset+neighbor_offset;
cristy016b7642014-10-25 13:09:57 +0000549 status=GetMatrixElement(equivalences,oy,0,&object);
550 while (object != root) {
551 status=GetMatrixElement(equivalences,oy,0,&object);
552 status=SetMatrixElement(equivalences,oy,0,&root);
553 }
554 status=SetMatrixElement(equivalences,y*image->columns+x,0,&root);
555 p+=GetPixelChannels(image);
556 }
557 }
558 }
559 image_view=DestroyCacheView(image_view);
560 /*
561 Label connected components.
562 */
563 n=0;
cristy6e0b3bc2014-10-19 17:51:42 +0000564 component_view=AcquireAuthenticCacheView(component_image,exception);
cristy6e0b3bc2014-10-19 17:51:42 +0000565 for (y=0; y < (ssize_t) component_image->rows; y++)
566 {
567 register Quantum
568 *restrict q;
569
570 register ssize_t
571 x;
572
573 if (status == MagickFalse)
574 continue;
575 q=QueueCacheViewAuthenticPixels(component_view,0,y,component_image->columns,
576 1,exception);
577 if (q == (Quantum *) NULL)
578 {
579 status=MagickFalse;
580 continue;
581 }
582 for (x=0; x < (ssize_t) component_image->columns; x++)
583 {
cristy016b7642014-10-25 13:09:57 +0000584 ssize_t
585 object,
586 offset;
587
588 offset=y*image->columns+x;
589 status=GetMatrixElement(equivalences,offset,0,&object);
590 if (object == offset)
591 {
592 object=n++;
593 status=SetMatrixElement(equivalences,offset,0,&object);
594 }
595 else
596 {
597 status=GetMatrixElement(equivalences,object,0,&object);
598 status=SetMatrixElement(equivalences,offset,0,&object);
599 }
600 *q=(Quantum) (object > (ssize_t) QuantumRange ? (ssize_t) QuantumRange :
601 object);
602 q+=GetPixelChannels(component_image);
cristy6e0b3bc2014-10-19 17:51:42 +0000603 }
604 if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse)
605 status=MagickFalse;
606 if (image->progress_monitor != (MagickProgressMonitor) NULL)
607 {
608 MagickBooleanType
609 proceed;
610
cristy6e0b3bc2014-10-19 17:51:42 +0000611 proceed=SetImageProgress(image,ConnectedComponentsImageTag,progress++,
612 image->rows);
613 if (proceed == MagickFalse)
614 status=MagickFalse;
615 }
616 }
617 component_view=DestroyCacheView(component_view);
cristy016b7642014-10-25 13:09:57 +0000618 equivalences=DestroyMatrixInfo(equivalences);
619 artifact=GetImageArtifact(image,"connected-components:verbose");
cristyc6ca5712014-10-26 22:13:07 +0000620 if (IsStringTrue(artifact) != MagickFalse)
621 status=ConnectedComponentsStatistics(image,component_image,(size_t) n,
622 exception);
623 artifact=GetImageArtifact(image,"connected-components:area-threshold");
624 if (artifact != (const char *) NULL)
625 area_threshold=StringToDouble(artifact,(char **) NULL);
cristy40898402014-10-26 22:22:02 +0000626 if (area_threshold > 0.0)
cristyc6ca5712014-10-26 22:13:07 +0000627 status=MergeConnectedComponents(image,component_image,(size_t) n,
628 area_threshold,exception);
cristy6e0b3bc2014-10-19 17:51:42 +0000629 if (status == MagickFalse)
630 component_image=DestroyImage(component_image);
631 return(component_image);
632}