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