MagickCore  7.0.9
vision.c
Go to the documentation of this file.
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-2020 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/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"
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"
54 #include "MagickCore/effect.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/log.h"
60 #include "MagickCore/matrix.h"
61 #include "MagickCore/memory_.h"
63 #include "MagickCore/monitor.h"
65 #include "MagickCore/montage.h"
66 #include "MagickCore/morphology.h"
69 #include "MagickCore/paint.h"
72 #include "MagickCore/property.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/resource_.h"
76 #include "MagickCore/string_.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 
120 static int CCObjectInfoCompare(const void *x,const void *y)
121 {
123  *p,
124  *q;
125 
126  p=(CCObjectInfo *) x;
127  q=(CCObjectInfo *) y;
128  return((int) (q->area-(ssize_t) p->area));
129 }
130 
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 
141  *object;
142 
143  char
144  *c;
145 
146  const char
147  *artifact;
148 
149  double
150  max_threshold,
151  min_threshold;
152 
153  Image
154  *component_image;
155 
157  status;
158 
160  progress;
161 
162  MatrixInfo
163  *equivalences;
164 
165  register ssize_t
166  i;
167 
168  size_t
169  size;
170 
171  ssize_t
172  first,
173  last,
174  n,
175  step,
176  y;
177 
178  /*
179  Initialize connected components image attributes.
180  */
181  assert(image != (Image *) NULL);
182  assert(image->signature == MagickCoreSignature);
183  if (image->debug != MagickFalse)
184  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185  assert(exception != (ExceptionInfo *) NULL);
186  assert(exception->signature == MagickCoreSignature);
187  if (objects != (CCObjectInfo **) NULL)
188  *objects=(CCObjectInfo *) NULL;
189  component_image=CloneImage(image,0,0,MagickTrue,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) memset(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  obj,
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  if (((x+dx) < 0) || ((x+dx) >= (ssize_t) image->columns) ||
284  ((y+dy) < 0) || ((y+dy) >= (ssize_t) image->rows))
285  {
286  p+=GetPixelChannels(image);
287  continue;
288  }
289  neighbor_offset=dy*(GetPixelChannels(image)*image->columns)+dx*
290  GetPixelChannels(image);
291  GetPixelInfoPixel(image,p+neighbor_offset,&target);
292  if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
293  {
294  p+=GetPixelChannels(image);
295  continue;
296  }
297  /*
298  Resolve this equivalence.
299  */
300  offset=y*image->columns+x;
301  neighbor_offset=dy*image->columns+dx;
302  ox=offset;
303  status=GetMatrixElement(equivalences,ox,0,&obj);
304  while (obj != ox)
305  {
306  ox=obj;
307  status=GetMatrixElement(equivalences,ox,0,&obj);
308  }
309  oy=offset+neighbor_offset;
310  status=GetMatrixElement(equivalences,oy,0,&obj);
311  while (obj != oy)
312  {
313  oy=obj;
314  status=GetMatrixElement(equivalences,oy,0,&obj);
315  }
316  if (ox < oy)
317  {
318  status=SetMatrixElement(equivalences,oy,0,&ox);
319  root=ox;
320  }
321  else
322  {
323  status=SetMatrixElement(equivalences,ox,0,&oy);
324  root=oy;
325  }
326  ox=offset;
327  status=GetMatrixElement(equivalences,ox,0,&obj);
328  while (obj != root)
329  {
330  status=GetMatrixElement(equivalences,ox,0,&obj);
331  status=SetMatrixElement(equivalences,ox,0,&root);
332  }
333  oy=offset+neighbor_offset;
334  status=GetMatrixElement(equivalences,oy,0,&obj);
335  while (obj != root)
336  {
337  status=GetMatrixElement(equivalences,oy,0,&obj);
338  status=SetMatrixElement(equivalences,oy,0,&root);
339  }
340  status=SetMatrixElement(equivalences,y*image->columns+x,0,&root);
341  p+=GetPixelChannels(image);
342  }
343  }
344  }
345  image_view=DestroyCacheView(image_view);
346  /*
347  Label connected components.
348  */
349  n=0;
350  image_view=AcquireVirtualCacheView(image,exception);
351  component_view=AcquireAuthenticCacheView(component_image,exception);
352  for (y=0; y < (ssize_t) component_image->rows; y++)
353  {
354  register const Quantum
355  *magick_restrict p;
356 
357  register Quantum
358  *magick_restrict q;
359 
360  register ssize_t
361  x;
362 
363  if (status == MagickFalse)
364  continue;
365  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
366  q=QueueCacheViewAuthenticPixels(component_view,0,y,component_image->columns,
367  1,exception);
368  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
369  {
370  status=MagickFalse;
371  continue;
372  }
373  for (x=0; x < (ssize_t) component_image->columns; x++)
374  {
375  ssize_t
376  id,
377  offset;
378 
379  offset=y*image->columns+x;
380  status=GetMatrixElement(equivalences,offset,0,&id);
381  if (id != offset)
382  status=GetMatrixElement(equivalences,id,0,&id);
383  else
384  {
385  id=n++;
386  if (id >= (ssize_t) MaxColormapSize)
387  break;
388  }
389  status=SetMatrixElement(equivalences,offset,0,&id);
390  if (x < object[id].bounding_box.x)
391  object[id].bounding_box.x=x;
392  if (x >= (ssize_t) object[id].bounding_box.width)
393  object[id].bounding_box.width=(size_t) x;
394  if (y < object[id].bounding_box.y)
395  object[id].bounding_box.y=y;
396  if (y >= (ssize_t) object[id].bounding_box.height)
397  object[id].bounding_box.height=(size_t) y;
398  object[id].color.red+=QuantumScale*GetPixelRed(image,p);
399  object[id].color.green+=QuantumScale*GetPixelGreen(image,p);
400  object[id].color.blue+=QuantumScale*GetPixelBlue(image,p);
401  if (image->alpha_trait != UndefinedPixelTrait)
402  object[id].color.alpha+=QuantumScale*GetPixelAlpha(image,p);
403  if (image->colorspace == CMYKColorspace)
404  object[id].color.black+=QuantumScale*GetPixelBlack(image,p);
405  object[id].centroid.x+=x;
406  object[id].centroid.y+=y;
407  object[id].area++;
408  SetPixelIndex(component_image,(Quantum) id,q);
409  p+=GetPixelChannels(image);
410  q+=GetPixelChannels(component_image);
411  }
412  if (n > (ssize_t) MaxColormapSize)
413  break;
414  if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse)
415  status=MagickFalse;
416  if (image->progress_monitor != (MagickProgressMonitor) NULL)
417  {
419  proceed;
420 
421  progress++;
422  proceed=SetImageProgress(image,ConnectedComponentsImageTag,progress,
423  image->rows);
424  if (proceed == MagickFalse)
425  status=MagickFalse;
426  }
427  }
428  component_view=DestroyCacheView(component_view);
429  image_view=DestroyCacheView(image_view);
430  equivalences=DestroyMatrixInfo(equivalences);
431  if (n > (ssize_t) MaxColormapSize)
432  {
433  object=(CCObjectInfo *) RelinquishMagickMemory(object);
434  component_image=DestroyImage(component_image);
435  ThrowImageException(ResourceLimitError,"TooManyObjects");
436  }
437  min_threshold=0.0;
438  max_threshold=0.0;
439  component_image->colors=(size_t) n;
440  for (i=0; i < (ssize_t) component_image->colors; i++)
441  {
442  object[i].bounding_box.width-=(object[i].bounding_box.x-1);
443  object[i].bounding_box.height-=(object[i].bounding_box.y-1);
444  object[i].color.red/=(object[i].area/QuantumRange);
445  object[i].color.green/=(object[i].area/QuantumRange);
446  object[i].color.blue/=(object[i].area/QuantumRange);
447  if (image->alpha_trait != UndefinedPixelTrait)
448  object[i].color.alpha/=(object[i].area/QuantumRange);
449  if (image->colorspace == CMYKColorspace)
450  object[i].color.black/=(object[i].area/QuantumRange);
451  object[i].centroid.x/=object[i].area;
452  object[i].centroid.y/=object[i].area;
453  max_threshold+=object[i].area;
454  }
455  max_threshold+=MagickEpsilon;
456  artifact=GetImageArtifact(image,"connected-components:area-threshold");
457  if (artifact != (const char *) NULL)
458  {
459  /*
460  Merge object below area threshold.
461  */
462  (void) sscanf(artifact,"%lf%*[ -]%lf",&min_threshold,&max_threshold);
463  component_view=AcquireAuthenticCacheView(component_image,exception);
464  for (i=0; i < (ssize_t) component_image->colors; i++)
465  {
466  double
467  census;
468 
470  bounding_box;
471 
472  register ssize_t
473  j;
474 
475  size_t
476  id;
477 
478  if (status == MagickFalse)
479  continue;
480  if (((double) object[i].area >= min_threshold) &&
481  ((double) object[i].area < max_threshold))
482  continue;
483  for (j=0; j < (ssize_t) component_image->colors; j++)
484  object[j].census=0;
485  bounding_box=object[i].bounding_box;
486  for (y=0; y < (ssize_t) bounding_box.height+2; y++)
487  {
488  register const Quantum
489  *magick_restrict p;
490 
491  register ssize_t
492  x;
493 
494  if (status == MagickFalse)
495  continue;
496  p=GetCacheViewVirtualPixels(component_view,bounding_box.x-1,
497  bounding_box.y+y-1,bounding_box.width+2,1,exception);
498  if (p == (const Quantum *) NULL)
499  {
500  status=MagickFalse;
501  continue;
502  }
503  for (x=0; x < (ssize_t) bounding_box.width+2; x++)
504  {
505  j=(ssize_t) GetPixelIndex(component_image,p);
506  if (j != i)
507  object[j].census++;
508  p+=GetPixelChannels(component_image);
509  }
510  }
511  census=0;
512  id=0;
513  for (j=0; j < (ssize_t) component_image->colors; j++)
514  if (census < object[j].census)
515  {
516  census=object[j].census;
517  id=(size_t) j;
518  }
519  object[id].area+=object[i].area;
520  for (y=0; y < (ssize_t) bounding_box.height; y++)
521  {
522  register Quantum
523  *magick_restrict q;
524 
525  register ssize_t
526  x;
527 
528  if (status == MagickFalse)
529  continue;
530  q=GetCacheViewAuthenticPixels(component_view,bounding_box.x,
531  bounding_box.y+y,bounding_box.width,1,exception);
532  if (q == (Quantum *) NULL)
533  {
534  status=MagickFalse;
535  continue;
536  }
537  for (x=0; x < (ssize_t) bounding_box.width; x++)
538  {
539  if ((ssize_t) GetPixelIndex(component_image,q) == i)
540  SetPixelIndex(component_image,(Quantum) id,q);
541  q+=GetPixelChannels(component_image);
542  }
543  if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse)
544  status=MagickFalse;
545  }
546  }
547  component_view=DestroyCacheView(component_view);
548  (void) SyncImage(component_image,exception);
549  }
550  artifact=GetImageArtifact(image,"connected-components:mean-color");
551  if (IsStringTrue(artifact) != MagickFalse)
552  {
553  /*
554  Replace object with mean color.
555  */
556  for (i=0; i < (ssize_t) component_image->colors; i++)
557  component_image->colormap[i]=object[i].color;
558  }
559  artifact=GetImageArtifact(image,"connected-components:keep-colors");
560  if (artifact != (const char *) NULL)
561  {
562  register const char
563  *p;
564 
565  /*
566  Keep selected objects based on color (make others transparent).
567  */
568  for (i=0; i < (ssize_t) component_image->colors; i++)
569  object[i].census=0;
570  for (p=artifact; ; )
571  {
572  char
573  color[MagickPathExtent];
574 
575  PixelInfo
576  pixel;
577 
578  register const char
579  *q;
580 
581  for (q=p; *q != '\0'; q++)
582  if (*q == ';')
583  break;
584  (void) CopyMagickString(color,p,(size_t) MagickMin(q-p+1,
586  (void) QueryColorCompliance(color,AllCompliance,&pixel,exception);
587  for (i=0; i < (ssize_t) component_image->colors; i++)
588  if (IsFuzzyEquivalencePixelInfo(&object[i].color,&pixel) != MagickFalse)
589  object[i].census++;
590  if (*q == '\0')
591  break;
592  p=q+1;
593  }
594  for (i=0; i < (ssize_t) component_image->colors; i++)
595  {
596  if (object[i].census == 0)
597  continue;
598  object[i].color.alpha_trait=BlendPixelTrait;
599  component_image->alpha_trait=BlendPixelTrait;
600  component_image->colormap[i].alpha_trait=BlendPixelTrait;
601  component_image->colormap[i].alpha=(MagickRealType) TransparentAlpha;
602  }
603  }
604  artifact=GetImageArtifact(image,"connected-components:keep-ids");
605  if (artifact == (const char *) NULL)
606  artifact=GetImageArtifact(image,"connected-components:keep");
607  if (artifact != (const char *) NULL)
608  {
609  /*
610  Keep selected objects based on ID (make others transparent).
611  */
612  for (i=0; i < (ssize_t) component_image->colors; i++)
613  object[i].census=0;
614  for (c=(char *) artifact; *c != '\0';)
615  {
616  while ((isspace((int) ((unsigned char) *c)) != 0) || (*c == ','))
617  c++;
618  first=(ssize_t) strtol(c,&c,10);
619  if (first < 0)
620  first+=(ssize_t) component_image->colors;
621  last=first;
622  while (isspace((int) ((unsigned char) *c)) != 0)
623  c++;
624  if (*c == '-')
625  {
626  last=(ssize_t) strtol(c+1,&c,10);
627  if (last < 0)
628  last+=(ssize_t) component_image->colors;
629  }
630  step=(ssize_t) (first > last ? -1 : 1);
631  for ( ; first != (last+step); first+=step)
632  object[first].census++;
633  }
634  for (i=0; i < (ssize_t) component_image->colors; i++)
635  {
636  if (object[i].census != 0)
637  continue;
638  object[i].color.alpha_trait=BlendPixelTrait;
639  component_image->alpha_trait=BlendPixelTrait;
640  component_image->colormap[i].alpha_trait=BlendPixelTrait;
641  component_image->colormap[i].alpha=(MagickRealType) TransparentAlpha;
642  }
643  }
644  artifact=GetImageArtifact(image,"connected-components:keep-top");
645  if (artifact != (const char *) NULL)
646  {
647  double
648  top;
649 
650  /*
651  Keep top objects with most area (make others transparent).
652  */
653  top=StringToDouble(artifact,(char **) NULL);
654  qsort((void *) object,component_image->colors,sizeof(*object),
656  for (i=(ssize_t) top; i < (ssize_t) component_image->colors; i++)
657  {
658  object[object[i].id].color.alpha_trait=BlendPixelTrait;
659  component_image->alpha_trait=BlendPixelTrait;
660  component_image->colormap[object[i].id].alpha_trait=BlendPixelTrait;
661  component_image->colormap[object[i].id].alpha=(MagickRealType)
663  }
664  }
665  artifact=GetImageArtifact(image,"connected-components:remove-colors");
666  if (artifact != (const char *) NULL)
667  {
668  register const char
669  *p;
670 
671  /*
672  Keep selected objects based on color (make others transparent).
673  */
674  for (i=0; i < (ssize_t) component_image->colors; i++)
675  object[i].census=0;
676  for (p=artifact; ; )
677  {
678  char
679  color[MagickPathExtent];
680 
681  PixelInfo
682  pixel;
683 
684  register const char
685  *q;
686 
687  for (q=p; *q != '\0'; q++)
688  if (*q == ';')
689  break;
690  (void) CopyMagickString(color,p,(size_t) MagickMin(q-p+1,
692  (void) QueryColorCompliance(color,AllCompliance,&pixel,exception);
693  for (i=0; i < (ssize_t) component_image->colors; i++)
694  if (IsFuzzyEquivalencePixelInfo(&object[i].color,&pixel) != MagickFalse)
695  object[i].census++;
696  if (*q == '\0')
697  break;
698  p=q+1;
699  }
700  for (i=0; i < (ssize_t) component_image->colors; i++)
701  {
702  if (object[i].census != 0)
703  continue;
704  object[i].color.alpha_trait=BlendPixelTrait;
705  component_image->alpha_trait=BlendPixelTrait;
706  component_image->colormap[i].alpha_trait=BlendPixelTrait;
707  component_image->colormap[i].alpha=(MagickRealType) TransparentAlpha;
708  }
709  }
710  artifact=GetImageArtifact(image,"connected-components:remove-ids");
711  if (artifact == (const char *) NULL)
712  artifact=GetImageArtifact(image,"connected-components:remove");
713  if (artifact != (const char *) NULL)
714  {
715  /*
716  Remove these object (make them transparent).
717  */
718  for (c=(char *) artifact; *c != '\0';)
719  {
720  while ((isspace((int) ((unsigned char) *c)) != 0) || (*c == ','))
721  c++;
722  first=(ssize_t) strtol(c,&c,10);
723  if (first < 0)
724  first+=(ssize_t) component_image->colors;
725  last=first;
726  while (isspace((int) ((unsigned char) *c)) != 0)
727  c++;
728  if (*c == '-')
729  {
730  last=(ssize_t) strtol(c+1,&c,10);
731  if (last < 0)
732  last+=(ssize_t) component_image->colors;
733  }
734  step=(ssize_t) (first > last ? -1 : 1);
735  for ( ; first != (last+step); first+=step)
736  {
737  component_image->alpha_trait=BlendPixelTrait;
738  component_image->colormap[first].alpha_trait=BlendPixelTrait;
739  component_image->colormap[first].alpha=(MagickRealType)
741  }
742  }
743  }
744  (void) SyncImage(component_image,exception);
745  artifact=GetImageArtifact(image,"connected-components:verbose");
746  if ((IsStringTrue(artifact) != MagickFalse) ||
747  (objects != (CCObjectInfo **) NULL))
748  {
749  /*
750  Report statistics on unique object.
751  */
752  for (i=0; i < (ssize_t) component_image->colors; i++)
753  {
754  object[i].bounding_box.width=0;
755  object[i].bounding_box.height=0;
756  object[i].bounding_box.x=(ssize_t) component_image->columns;
757  object[i].bounding_box.y=(ssize_t) component_image->rows;
758  object[i].centroid.x=0;
759  object[i].centroid.y=0;
760  object[i].area=0;
761  }
762  component_view=AcquireVirtualCacheView(component_image,exception);
763  for (y=0; y < (ssize_t) component_image->rows; y++)
764  {
765  register const Quantum
766  *magick_restrict p;
767 
768  register ssize_t
769  x;
770 
771  if (status == MagickFalse)
772  continue;
773  p=GetCacheViewVirtualPixels(component_view,0,y,component_image->columns,
774  1,exception);
775  if (p == (const Quantum *) NULL)
776  {
777  status=MagickFalse;
778  continue;
779  }
780  for (x=0; x < (ssize_t) component_image->columns; x++)
781  {
782  size_t
783  id;
784 
785  id=(size_t) GetPixelIndex(component_image,p);
786  if (x < object[id].bounding_box.x)
787  object[id].bounding_box.x=x;
788  if (x > (ssize_t) object[id].bounding_box.width)
789  object[id].bounding_box.width=(size_t) x;
790  if (y < object[id].bounding_box.y)
791  object[id].bounding_box.y=y;
792  if (y > (ssize_t) object[id].bounding_box.height)
793  object[id].bounding_box.height=(size_t) y;
794  object[id].centroid.x+=x;
795  object[id].centroid.y+=y;
796  object[id].area++;
797  p+=GetPixelChannels(component_image);
798  }
799  }
800  for (i=0; i < (ssize_t) component_image->colors; i++)
801  {
802  object[i].bounding_box.width-=(object[i].bounding_box.x-1);
803  object[i].bounding_box.height-=(object[i].bounding_box.y-1);
804  object[i].centroid.x=object[i].centroid.x/object[i].area;
805  object[i].centroid.y=object[i].centroid.y/object[i].area;
806  }
807  component_view=DestroyCacheView(component_view);
808  qsort((void *) object,component_image->colors,sizeof(*object),
810  if (objects == (CCObjectInfo **) NULL)
811  {
812  artifact=GetImageArtifact(image,
813  "connected-components:exclude-header");
814  if (IsStringTrue(artifact) == MagickFalse)
815  (void) fprintf(stdout,
816  "Objects (id: bounding-box centroid area mean-color):\n");
817  for (i=0; i < (ssize_t) component_image->colors; i++)
818  {
819  char
820  mean_color[MagickPathExtent];
821 
822  if (status == MagickFalse)
823  break;
824  if (((double) object[i].area < min_threshold) ||
825  ((double) object[i].area >= max_threshold))
826  continue;
827  GetColorTuple(&object[i].color,MagickFalse,mean_color);
828  (void) fprintf(stdout,
829  " %.20g: %.20gx%.20g%+.20g%+.20g %.1f,%.1f %.20g %s\n",(double)
830  object[i].id,(double) object[i].bounding_box.width,(double)
831  object[i].bounding_box.height,(double) object[i].bounding_box.x,
832  (double) object[i].bounding_box.y,object[i].centroid.x,
833  object[i].centroid.y,(double) object[i].area,mean_color);
834  }
835  }
836  }
837  if (objects == (CCObjectInfo **) NULL)
838  object=(CCObjectInfo *) RelinquishMagickMemory(object);
839  else
840  *objects=object;
841  return(component_image);
842 }
size_t rows
Definition: image.h:172
#define magick_restrict
Definition: MagickCore.h:41
MagickDoubleType MagickRealType
Definition: magick-type.h:124
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
MagickExport Image * ConnectedComponentsImage(const Image *image, const size_t connectivity, CCObjectInfo **objects, ExceptionInfo *exception)
Definition: vision.c:131
#define TransparentAlpha
Definition: image.h:26
PixelInfo * colormap
Definition: image.h:179
MagickProgressMonitor progress_monitor
Definition: image.h:303
MagickExport MagickBooleanType SyncImage(Image *image, ExceptionInfo *exception)
Definition: image.c:3888
static Quantum GetPixelAlpha(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
PixelTrait alpha_trait
Definition: pixel.h:178
static Quantum GetPixelRed(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
size_t signature
Definition: exception.h:123
MagickExport const char * GetImageArtifact(const Image *image, const char *artifact)
Definition: artifact.c:273
#define MaxColormapSize
Definition: magick-type.h:78
static double StringToDouble(const char *magick_restrict string, char **magick_restrict sentinal)
MagickExport size_t CopyMagickString(char *magick_restrict destination, const char *magick_restrict source, const size_t length)
Definition: string.c:755
#define MAGICKCORE_QUANTUM_DEPTH
Definition: magick-type.h:32
MagickExport const Quantum * GetCacheViewVirtualPixels(const CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:651
MagickRealType alpha
Definition: pixel.h:190
#define MagickEpsilon
Definition: magick-type.h:114
MagickExport MagickBooleanType GetMatrixElement(const MatrixInfo *matrix_info, const ssize_t x, const ssize_t y, void *value)
Definition: matrix.c:705
size_t width
Definition: geometry.h:130
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:133
MagickExport void GetPixelInfo(const Image *image, PixelInfo *pixel)
Definition: pixel.c:2170
Definition: image.h:151
MagickExport MagickBooleanType SetMatrixElement(const MatrixInfo *matrix_info, const ssize_t x, const ssize_t y, const void *value)
Definition: matrix.c:1110
#define MagickCoreSignature
MagickExport Quantum * GetCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:299
MagickBooleanType
Definition: magick-type.h:169
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:634
MagickExport void GetColorTuple(const PixelInfo *pixel, const MagickBooleanType hex, char *tuple)
Definition: color.c:1515
#define MagickPathExtent
static Quantum GetPixelGreen(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport MagickBooleanType IsStringTrue(const char *value)
Definition: string.c:1425
static void GetPixelInfoPixel(const Image *magick_restrict image, const Quantum *magick_restrict pixel, PixelInfo *magick_restrict pixel_info)
PixelTrait alpha_trait
Definition: image.h:280
static Quantum GetPixelIndex(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
static Quantum GetPixelBlack(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport Quantum * QueueCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:977
MagickExport MatrixInfo * AcquireMatrixInfo(const size_t columns, const size_t rows, const size_t stride, ExceptionInfo *exception)
Definition: matrix.c:200
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1660
size_t signature
Definition: image.h:354
#define QuantumScale
Definition: magick-type.h:119
size_t columns
Definition: image.h:172
ssize_t x
Definition: geometry.h:134
size_t height
Definition: geometry.h:130
MagickExport MagickBooleanType QueryColorCompliance(const char *name, const ComplianceType compliance, PixelInfo *color, ExceptionInfo *exception)
Definition: color.c:2177
size_t colors
Definition: image.h:172
static size_t GetPixelChannels(const Image *magick_restrict image)
MagickExport MagickBooleanType AcquireImageColormap(Image *image, const size_t colors, ExceptionInfo *exception)
Definition: colormap.c:104
char filename[MagickPathExtent]
Definition: image.h:319
#define GetMagickModule()
Definition: log.h:28
#define ThrowImageException(severity, tag)
MagickExport CacheView * AcquireVirtualCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:149
double area
Definition: vision.h:40
unsigned short Quantum
Definition: magick-type.h:86
MagickExport MagickBooleanType IsFuzzyEquivalencePixelInfo(const PixelInfo *p, const PixelInfo *q)
Definition: pixel.c:6063
static void SetPixelIndex(const Image *magick_restrict image, const Quantum index, Quantum *magick_restrict pixel)
#define MagickMin(x, y)
Definition: image-private.h:27
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1123
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
static int CCObjectInfoCompare(const void *x, const void *y)
Definition: vision.c:120
#define MagickExport
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
ssize_t y
Definition: geometry.h:134
MagickExport CacheView * AcquireAuthenticCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:112
static Quantum GetPixelBlue(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
#define ConnectedComponentsImageTag
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1181
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:796
ColorspaceType colorspace
Definition: image.h:157
#define QuantumRange
Definition: magick-type.h:87
MagickExport MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
Definition: monitor.c:136
MagickBooleanType debug
Definition: image.h:334
MagickExport MatrixInfo * DestroyMatrixInfo(MatrixInfo *matrix_info)
Definition: matrix.c:369
size_t depth
Definition: image.h:172