• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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-2016 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"
40 #include "MagickCore/artifact.h"
41 #include "MagickCore/blob.h"
42 #include "MagickCore/cache-view.h"
43 #include "MagickCore/color.h"
44 #include "MagickCore/color-private.h"
45 #include "MagickCore/colormap.h"
46 #include "MagickCore/colorspace.h"
47 #include "MagickCore/constitute.h"
48 #include "MagickCore/decorate.h"
49 #include "MagickCore/distort.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/enhance.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/effect.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
57 #include "MagickCore/image-private.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/log.h"
60 #include "MagickCore/matrix.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/memory-private.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/montage.h"
66 #include "MagickCore/morphology.h"
67 #include "MagickCore/morphology-private.h"
68 #include "MagickCore/opencl-private.h"
69 #include "MagickCore/paint.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/pixel-private.h"
72 #include "MagickCore/property.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/resource_.h"
75 #include "MagickCore/signature-private.h"
76 #include "MagickCore/string_.h"
77 #include "MagickCore/string-private.h"
78 #include "MagickCore/thread-private.h"
79 #include "MagickCore/token.h"
80 #include "MagickCore/vision.h"
81 
82 /*
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 %                                                                             %
85 %                                                                             %
86 %                                                                             %
87 %     C o n n e c t e d C o m p o n e n t s I m a g e                         %
88 %                                                                             %
89 %                                                                             %
90 %                                                                             %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %
93 %  ConnectedComponentsImage() returns the connected-components of the image
94 %  uniquely labeled.  The returned connected components image colors member
95 %  defines the number of unique objects.  Choose from 4 or 8-way connectivity.
96 %
97 %  You are responsible for freeing the connected components objects resources
98 %  with this statement;
99 %
100 %    objects = (CCObjectInfo *) RelinquishMagickMemory(objects);
101 %
102 %  The format of the ConnectedComponentsImage method is:
103 %
104 %      Image *ConnectedComponentsImage(const Image *image,
105 %        const size_t connectivity,CCObjectInfo **objects,
106 %        ExceptionInfo *exception)
107 %
108 %  A description of each parameter follows:
109 %
110 %    o image: the image.
111 %
112 %    o connectivity: how many neighbors to visit, choose from 4 or 8.
113 %
114 %    o objects: return the attributes of each unique object.
115 %
116 %    o exception: return any errors or warnings in this structure.
117 %
118 */
119 
CCObjectInfoCompare(const void * x,const void * y)120 static int CCObjectInfoCompare(const void *x,const void *y)
121 {
122   CCObjectInfo
123     *p,
124     *q;
125 
126   p=(CCObjectInfo *) x;
127   q=(CCObjectInfo *) y;
128   return((int) (q->area-(ssize_t) p->area));
129 }
130 
ConnectedComponentsImage(const Image * image,const size_t connectivity,CCObjectInfo ** objects,ExceptionInfo * exception)131 MagickExport Image *ConnectedComponentsImage(const Image *image,
132   const size_t connectivity,CCObjectInfo **objects,ExceptionInfo *exception)
133 {
134 #define ConnectedComponentsImageTag  "ConnectedComponents/Image"
135 
136   CacheView
137     *image_view,
138     *component_view;
139 
140   CCObjectInfo
141     *object;
142 
143   char
144     *p;
145 
146   const char
147     *artifact;
148 
149   double
150     area_threshold;
151 
152   Image
153     *component_image;
154 
155   MagickBooleanType
156     status;
157 
158   MagickOffsetType
159     progress;
160 
161   MatrixInfo
162     *equivalences;
163 
164   register ssize_t
165     i;
166 
167   size_t
168     size;
169 
170   ssize_t
171     first,
172     last,
173     n,
174     step,
175     y;
176 
177   /*
178     Initialize connected components image attributes.
179   */
180   assert(image != (Image *) NULL);
181   assert(image->signature == MagickCoreSignature);
182   if (image->debug != MagickFalse)
183     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
184   assert(exception != (ExceptionInfo *) NULL);
185   assert(exception->signature == MagickCoreSignature);
186   if (objects != (CCObjectInfo **) NULL)
187     *objects=(CCObjectInfo *) NULL;
188   component_image=CloneImage(image,image->columns,image->rows,MagickTrue,
189     exception);
190   if (component_image == (Image *) NULL)
191     return((Image *) NULL);
192   component_image->depth=MAGICKCORE_QUANTUM_DEPTH;
193   if (AcquireImageColormap(component_image,MaxColormapSize,exception) == MagickFalse)
194     {
195       component_image=DestroyImage(component_image);
196       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
197     }
198   /*
199     Initialize connected components equivalences.
200   */
201   size=image->columns*image->rows;
202   if (image->columns != (size/image->rows))
203     {
204       component_image=DestroyImage(component_image);
205       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
206     }
207   equivalences=AcquireMatrixInfo(size,1,sizeof(ssize_t),exception);
208   if (equivalences == (MatrixInfo *) NULL)
209     {
210       component_image=DestroyImage(component_image);
211       return((Image *) NULL);
212     }
213   for (n=0; n < (ssize_t) (image->columns*image->rows); n++)
214     (void) SetMatrixElement(equivalences,n,0,&n);
215   object=(CCObjectInfo *) AcquireQuantumMemory(MaxColormapSize,sizeof(*object));
216   if (object == (CCObjectInfo *) NULL)
217     {
218       equivalences=DestroyMatrixInfo(equivalences);
219       component_image=DestroyImage(component_image);
220       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
221     }
222   (void) ResetMagickMemory(object,0,MaxColormapSize*sizeof(*object));
223   for (i=0; i < (ssize_t) MaxColormapSize; i++)
224   {
225     object[i].id=i;
226     object[i].bounding_box.x=(ssize_t) image->columns;
227     object[i].bounding_box.y=(ssize_t) image->rows;
228     GetPixelInfo(image,&object[i].color);
229   }
230   /*
231     Find connected components.
232   */
233   status=MagickTrue;
234   progress=0;
235   image_view=AcquireVirtualCacheView(image,exception);
236   for (n=0; n < (ssize_t) (connectivity > 4 ? 4 : 2); n++)
237   {
238     ssize_t
239       connect4[2][2] = { { -1,  0 }, {  0, -1 } },
240       connect8[4][2] = { { -1, -1 }, { -1,  0 }, { -1,  1 }, {  0, -1 } },
241       dx,
242       dy;
243 
244     if (status == MagickFalse)
245       continue;
246     dy=connectivity > 4 ? connect8[n][0] : connect4[n][0];
247     dx=connectivity > 4 ? connect8[n][1] : connect4[n][1];
248     for (y=0; y < (ssize_t) image->rows; y++)
249     {
250       register const Quantum
251         *magick_restrict p;
252 
253       register ssize_t
254         x;
255 
256       if (status == MagickFalse)
257         continue;
258       p=GetCacheViewVirtualPixels(image_view,0,y-1,image->columns,3,exception);
259       if (p == (const Quantum *) NULL)
260         {
261           status=MagickFalse;
262           continue;
263         }
264       p+=GetPixelChannels(image)*image->columns;
265       for (x=0; x < (ssize_t) image->columns; x++)
266       {
267         PixelInfo
268           pixel,
269           target;
270 
271         ssize_t
272           neighbor_offset,
273           object,
274           offset,
275           ox,
276           oy,
277           root;
278 
279         /*
280           Is neighbor an authentic pixel and a different color than the pixel?
281         */
282         GetPixelInfoPixel(image,p,&pixel);
283         neighbor_offset=dy*(GetPixelChannels(image)*image->columns)+dx*
284           GetPixelChannels(image);
285         GetPixelInfoPixel(image,p+neighbor_offset,&target);
286         if (((x+dx) < 0) || ((x+dx) >= (ssize_t) image->columns) ||
287             ((y+dy) < 0) || ((y+dy) >= (ssize_t) image->rows) ||
288             (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse))
289           {
290             p+=GetPixelChannels(image);
291             continue;
292           }
293         /*
294           Resolve this equivalence.
295         */
296         offset=y*image->columns+x;
297         neighbor_offset=dy*image->columns+dx;
298         ox=offset;
299         status=GetMatrixElement(equivalences,ox,0,&object);
300         while (object != ox)
301         {
302           ox=object;
303           status=GetMatrixElement(equivalences,ox,0,&object);
304         }
305         oy=offset+neighbor_offset;
306         status=GetMatrixElement(equivalences,oy,0,&object);
307         while (object != oy)
308         {
309           oy=object;
310           status=GetMatrixElement(equivalences,oy,0,&object);
311         }
312         if (ox < oy)
313           {
314             status=SetMatrixElement(equivalences,oy,0,&ox);
315             root=ox;
316           }
317         else
318           {
319             status=SetMatrixElement(equivalences,ox,0,&oy);
320             root=oy;
321           }
322         ox=offset;
323         status=GetMatrixElement(equivalences,ox,0,&object);
324         while (object != root)
325         {
326           status=GetMatrixElement(equivalences,ox,0,&object);
327           status=SetMatrixElement(equivalences,ox,0,&root);
328         }
329         oy=offset+neighbor_offset;
330         status=GetMatrixElement(equivalences,oy,0,&object);
331         while (object != root)
332         {
333           status=GetMatrixElement(equivalences,oy,0,&object);
334           status=SetMatrixElement(equivalences,oy,0,&root);
335         }
336         status=SetMatrixElement(equivalences,y*image->columns+x,0,&root);
337         p+=GetPixelChannels(image);
338       }
339     }
340   }
341   image_view=DestroyCacheView(image_view);
342   /*
343     Label connected components.
344   */
345   n=0;
346   image_view=AcquireVirtualCacheView(image,exception);
347   component_view=AcquireAuthenticCacheView(component_image,exception);
348   for (y=0; y < (ssize_t) component_image->rows; y++)
349   {
350     register const Quantum
351       *magick_restrict p;
352 
353     register Quantum
354       *magick_restrict q;
355 
356     register ssize_t
357       x;
358 
359     if (status == MagickFalse)
360       continue;
361     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
362     q=QueueCacheViewAuthenticPixels(component_view,0,y,component_image->columns,
363       1,exception);
364     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
365       {
366         status=MagickFalse;
367         continue;
368       }
369     for (x=0; x < (ssize_t) component_image->columns; x++)
370     {
371       ssize_t
372         id,
373         offset;
374 
375       offset=y*image->columns+x;
376       status=GetMatrixElement(equivalences,offset,0,&id);
377       if (id == offset)
378         {
379           id=n++;
380           if (n > (ssize_t) MaxColormapSize)
381             break;
382           status=SetMatrixElement(equivalences,offset,0,&id);
383         }
384       else
385         {
386           status=GetMatrixElement(equivalences,id,0,&id);
387           status=SetMatrixElement(equivalences,offset,0,&id);
388         }
389       if (x < object[id].bounding_box.x)
390         object[id].bounding_box.x=x;
391       if (x > (ssize_t) object[id].bounding_box.width)
392         object[id].bounding_box.width=(size_t) x;
393       if (y < object[id].bounding_box.y)
394         object[id].bounding_box.y=y;
395       if (y > (ssize_t) object[id].bounding_box.height)
396         object[id].bounding_box.height=(size_t) y;
397       object[id].color.red+=GetPixelRed(image,p);
398       object[id].color.green+=GetPixelGreen(image,p);
399       object[id].color.blue+=GetPixelBlue(image,p);
400       object[id].color.black+=GetPixelBlack(image,p);
401       object[id].color.alpha+=GetPixelAlpha(image,p);
402       object[id].centroid.x+=x;
403       object[id].centroid.y+=y;
404       object[id].area++;
405       SetPixelIndex(component_image,(Quantum) id,q);
406       p+=GetPixelChannels(image);
407       q+=GetPixelChannels(component_image);
408     }
409     if (n > (ssize_t) MaxColormapSize)
410       break;
411     if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse)
412       status=MagickFalse;
413     if (image->progress_monitor != (MagickProgressMonitor) NULL)
414       {
415         MagickBooleanType
416           proceed;
417 
418         proceed=SetImageProgress(image,ConnectedComponentsImageTag,progress++,
419           image->rows);
420         if (proceed == MagickFalse)
421           status=MagickFalse;
422       }
423   }
424   component_view=DestroyCacheView(component_view);
425   image_view=DestroyCacheView(image_view);
426   equivalences=DestroyMatrixInfo(equivalences);
427   if (n > (ssize_t) MaxColormapSize)
428     {
429       object=(CCObjectInfo *) RelinquishMagickMemory(object);
430       component_image=DestroyImage(component_image);
431       ThrowImageException(ResourceLimitError,"TooManyObjects");
432     }
433   component_image->colors=(size_t) n;
434   for (i=0; i < (ssize_t) component_image->colors; i++)
435   {
436     object[i].bounding_box.width-=(object[i].bounding_box.x-1);
437     object[i].bounding_box.height-=(object[i].bounding_box.y-1);
438     object[i].color.red=object[i].color.red/object[i].area;
439     object[i].color.green=object[i].color.green/object[i].area;
440     object[i].color.blue=object[i].color.blue/object[i].area;
441     object[i].color.alpha=object[i].color.alpha/object[i].area;
442     object[i].color.black=object[i].color.black/object[i].area;
443     object[i].centroid.x=object[i].centroid.x/object[i].area;
444     object[i].centroid.y=object[i].centroid.y/object[i].area;
445   }
446   artifact=GetImageArtifact(image,"connected-components:area-threshold");
447   area_threshold=0.0;
448   if (artifact != (const char *) NULL)
449     area_threshold=StringToDouble(artifact,(char **) NULL);
450   if (area_threshold > 0.0)
451     {
452       /*
453         Merge object below area threshold.
454       */
455       component_view=AcquireAuthenticCacheView(component_image,exception);
456       for (i=0; i < (ssize_t) component_image->colors; i++)
457       {
458         double
459           census;
460 
461         RectangleInfo
462           bounding_box;
463 
464         register ssize_t
465           j;
466 
467         size_t
468           id;
469 
470         if (status == MagickFalse)
471           continue;
472         if ((double) object[i].area >= area_threshold)
473           continue;
474         for (j=0; j < (ssize_t) component_image->colors; j++)
475           object[j].census=0;
476         bounding_box=object[i].bounding_box;
477         for (y=0; y < (ssize_t) bounding_box.height+2; y++)
478         {
479           register const Quantum
480             *magick_restrict p;
481 
482           register ssize_t
483             x;
484 
485           if (status == MagickFalse)
486             continue;
487           p=GetCacheViewVirtualPixels(component_view,bounding_box.x-1,
488             bounding_box.y+y-1,bounding_box.width+2,1,exception);
489           if (p == (const Quantum *) NULL)
490             {
491               status=MagickFalse;
492               continue;
493             }
494           for (x=0; x < (ssize_t) bounding_box.width+2; x++)
495           {
496             j=(ssize_t) GetPixelIndex(component_image,p);
497             if (j != i)
498               object[j].census++;
499           }
500         }
501         census=0;
502         id=0;
503         for (j=0; j < (ssize_t) component_image->colors; j++)
504           if (census < object[j].census)
505             {
506               census=object[j].census;
507               id=(size_t) j;
508             }
509         object[id].area+=object[i].area;
510         for (y=0; y < (ssize_t) bounding_box.height; y++)
511         {
512           register Quantum
513             *magick_restrict q;
514 
515           register ssize_t
516             x;
517 
518           if (status == MagickFalse)
519             continue;
520           q=GetCacheViewAuthenticPixels(component_view,bounding_box.x,
521             bounding_box.y+y,bounding_box.width,1,exception);
522           if (q == (Quantum *) NULL)
523             {
524               status=MagickFalse;
525               continue;
526             }
527           for (x=0; x < (ssize_t) bounding_box.width; x++)
528           {
529             if ((ssize_t) GetPixelIndex(component_image,q) == i)
530               SetPixelIndex(image,(Quantum) id,q);
531             q+=GetPixelChannels(component_image);
532           }
533           if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse)
534             status=MagickFalse;
535         }
536       }
537       (void) SyncImage(component_image,exception);
538     }
539   artifact=GetImageArtifact(image,"connected-components:mean-color");
540   if (IsStringTrue(artifact) != MagickFalse)
541     {
542       /*
543         Replace object with mean color.
544       */
545       for (i=0; i < (ssize_t) component_image->colors; i++)
546         component_image->colormap[i]=object[i].color;
547     }
548   artifact=GetImageArtifact(image,"connected-components:keep");
549   if (artifact != (const char *) NULL)
550     {
551       /*
552         Keep these object (make others transparent).
553       */
554       for (i=0; i < (ssize_t) component_image->colors; i++)
555         object[i].census=0;
556       for (p=(char *) artifact; *p != '\0';)
557       {
558         while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
559           p++;
560         first=strtol(p,&p,10);
561         if (first < 0)
562           first+=(long) component_image->colors;
563         last=first;
564         while (isspace((int) ((unsigned char) *p)) != 0)
565           p++;
566         if (*p == '-')
567           {
568             last=strtol(p+1,&p,10);
569             if (last < 0)
570               last+=(long) component_image->colors;
571           }
572         for (step=first > last ? -1 : 1; first != (last+step); first+=step)
573           object[first].census++;
574       }
575       for (i=0; i < (ssize_t) component_image->colors; i++)
576       {
577         if (object[i].census != 0)
578           continue;
579         component_image->alpha_trait=BlendPixelTrait;
580         component_image->colormap[i].alpha=TransparentAlpha;
581       }
582     }
583   artifact=GetImageArtifact(image,"connected-components:remove");
584   if (artifact != (const char *) NULL)
585     {
586       /*
587         Remove these object (make them transparent).
588       */
589       for (p=(char *) artifact; *p != '\0';)
590       {
591         while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
592           p++;
593         first=strtol(p,&p,10);
594         if (first < 0)
595           first+=(long) component_image->colors;
596         last=first;
597         while (isspace((int) ((unsigned char) *p)) != 0)
598           p++;
599         if (*p == '-')
600           {
601             last=strtol(p+1,&p,10);
602             if (last < 0)
603               last+=(long) component_image->colors;
604           }
605         for (step=first > last ? -1 : 1; first != (last+step); first+=step)
606         {
607           component_image->alpha_trait=BlendPixelTrait;
608           component_image->colormap[first].alpha=TransparentAlpha;
609         }
610       }
611     }
612   (void) SyncImage(component_image,exception);
613   artifact=GetImageArtifact(image,"connected-components:verbose");
614   if ((IsStringTrue(artifact) != MagickFalse) ||
615       (objects != (CCObjectInfo **) NULL))
616     {
617       /*
618         Report statistics on unique object.
619       */
620       for (i=0; i < (ssize_t) component_image->colors; i++)
621       {
622         object[i].bounding_box.width=0;
623         object[i].bounding_box.height=0;
624         object[i].bounding_box.x=(ssize_t) component_image->columns;
625         object[i].bounding_box.y=(ssize_t) component_image->rows;
626         object[i].centroid.x=0;
627         object[i].centroid.y=0;
628         object[i].area=0;
629       }
630       component_view=AcquireVirtualCacheView(component_image,exception);
631       for (y=0; y < (ssize_t) component_image->rows; y++)
632       {
633         register const Quantum
634           *magick_restrict p;
635 
636         register ssize_t
637           x;
638 
639         if (status == MagickFalse)
640           continue;
641         p=GetCacheViewVirtualPixels(component_view,0,y,
642           component_image->columns,1,exception);
643         if (p == (const Quantum *) NULL)
644           {
645             status=MagickFalse;
646             continue;
647           }
648         for (x=0; x < (ssize_t) component_image->columns; x++)
649         {
650           size_t
651             id;
652 
653           id=GetPixelIndex(component_image,p);
654           if (x < object[id].bounding_box.x)
655             object[id].bounding_box.x=x;
656           if (x > (ssize_t) object[id].bounding_box.width)
657             object[id].bounding_box.width=(size_t) x;
658           if (y < object[id].bounding_box.y)
659             object[id].bounding_box.y=y;
660           if (y > (ssize_t) object[id].bounding_box.height)
661             object[id].bounding_box.height=(size_t) y;
662           object[id].centroid.x+=x;
663           object[id].centroid.y+=y;
664           object[id].area++;
665           p+=GetPixelChannels(component_image);
666         }
667       }
668       for (i=0; i < (ssize_t) component_image->colors; i++)
669       {
670         object[i].bounding_box.width-=(object[i].bounding_box.x-1);
671         object[i].bounding_box.height-=(object[i].bounding_box.y-1);
672         object[i].centroid.x=object[i].centroid.x/object[i].area;
673         object[i].centroid.y=object[i].centroid.y/object[i].area;
674       }
675       component_view=DestroyCacheView(component_view);
676       qsort((void *) object,component_image->colors,sizeof(*object),
677         CCObjectInfoCompare);
678       if (objects == (CCObjectInfo **) NULL)
679         {
680           (void) fprintf(stdout,
681             "Objects (id: bounding-box centroid area mean-color):\n");
682           for (i=0; i < (ssize_t) component_image->colors; i++)
683           {
684             char
685               mean_color[MagickPathExtent];
686 
687             if (status == MagickFalse)
688               break;
689             if (object[i].area < MagickEpsilon)
690               continue;
691             GetColorTuple(&object[i].color,MagickFalse,mean_color);
692             (void) fprintf(stdout,
693               "  %.20g: %.20gx%.20g%+.20g%+.20g %.1f,%.1f %.20g %s\n",(double)
694               object[i].id,(double) object[i].bounding_box.width,(double)
695               object[i].bounding_box.height,(double) object[i].bounding_box.x,
696               (double) object[i].bounding_box.y,object[i].centroid.x,
697               object[i].centroid.y,(double) object[i].area,mean_color);
698         }
699       }
700     }
701   if (objects == (CCObjectInfo **) NULL)
702     object=(CCObjectInfo *) RelinquishMagickMemory(object);
703   else
704     *objects=object;
705   return(component_image);
706 }
707