|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % H H IIIII SSSSS TTTTT OOO GGGG RRRR AAA M M % 00007 % H H I SS T O O G R R A A MM MM % 00008 % HHHHH I SSS T O O G GG RRRR AAAAA M M M % 00009 % H H I SS T O O G G R R A A M M % 00010 % H H IIIII SSSSS T OOO GGG R R A A M M % 00011 % % 00012 % % 00013 % MagickCore Histogram Methods % 00014 % % 00015 % Software Design % 00016 % Anthony Thyssen % 00017 % Fred Weinhaus % 00018 % August 2009 % 00019 % % 00020 % % 00021 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00022 % dedicated to making software imaging solutions freely available. % 00023 % % 00024 % You may not use this file except in compliance with the License. You may % 00025 % obtain a copy of the License at % 00026 % % 00027 % http://www.imagemagick.org/script/license.php % 00028 % % 00029 % Unless required by applicable law or agreed to in writing, software % 00030 % distributed under the License is distributed on an "AS IS" BASIS, % 00031 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00032 % See the License for the specific language governing permissions and % 00033 % limitations under the License. % 00034 % % 00035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00036 % 00037 % 00038 */ 00039 00040 /* 00041 Include declarations. 00042 */ 00043 #include "MagickCore/studio.h" 00044 #include "MagickCore/cache-view.h" 00045 #include "MagickCore/color-private.h" 00046 #include "MagickCore/enhance.h" 00047 #include "MagickCore/exception.h" 00048 #include "MagickCore/exception-private.h" 00049 #include "MagickCore/hashmap.h" 00050 #include "MagickCore/histogram.h" 00051 #include "MagickCore/image.h" 00052 #include "MagickCore/list.h" 00053 #include "MagickCore/memory_.h" 00054 #include "MagickCore/monitor-private.h" 00055 #include "MagickCore/pixel-accessor.h" 00056 #include "MagickCore/prepress.h" 00057 #include "MagickCore/quantize.h" 00058 #include "MagickCore/registry.h" 00059 #include "MagickCore/semaphore.h" 00060 #include "MagickCore/splay-tree.h" 00061 #include "MagickCore/statistic.h" 00062 #include "MagickCore/string_.h" 00063 00064 /* 00065 Define declarations. 00066 */ 00067 #define MaxTreeDepth 8 00068 #define NodesInAList 1536 00069 00070 /* 00071 Typedef declarations. 00072 */ 00073 typedef struct _NodeInfo 00074 { 00075 struct _NodeInfo 00076 *child[16]; 00077 00078 PixelInfo 00079 *list; 00080 00081 MagickSizeType 00082 number_unique; 00083 00084 size_t 00085 level; 00086 } NodeInfo; 00087 00088 typedef struct _Nodes 00089 { 00090 NodeInfo 00091 nodes[NodesInAList]; 00092 00093 struct _Nodes 00094 *next; 00095 } Nodes; 00096 00097 typedef struct _CubeInfo 00098 { 00099 NodeInfo 00100 *root; 00101 00102 ssize_t 00103 x; 00104 00105 MagickOffsetType 00106 progress; 00107 00108 size_t 00109 colors, 00110 free_nodes; 00111 00112 NodeInfo 00113 *node_info; 00114 00115 Nodes 00116 *node_queue; 00117 } CubeInfo; 00118 00119 /* 00120 Forward declarations. 00121 */ 00122 static CubeInfo 00123 *GetCubeInfo(void); 00124 00125 static NodeInfo 00126 *GetNodeInfo(CubeInfo *,const size_t); 00127 00128 static void 00129 DestroyColorCube(const Image *,NodeInfo *); 00130 00131 /* 00132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00133 % % 00134 % % 00135 % % 00136 + C l a s s i f y I m a g e C o l o r s % 00137 % % 00138 % % 00139 % % 00140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00141 % 00142 % ClassifyImageColors() builds a populated CubeInfo tree for the specified 00143 % image. The returned tree should be deallocated using DestroyCubeInfo() 00144 % once it is no longer needed. 00145 % 00146 % The format of the ClassifyImageColors() method is: 00147 % 00148 % CubeInfo *ClassifyImageColors(const Image *image, 00149 % ExceptionInfo *exception) 00150 % 00151 % A description of each parameter follows. 00152 % 00153 % o image: the image. 00154 % 00155 % o exception: return any errors or warnings in this structure. 00156 % 00157 */ 00158 00159 static inline size_t ColorToNodeId(const Image *image, 00160 const PixelInfo *pixel,size_t index) 00161 { 00162 size_t 00163 id; 00164 00165 id=(size_t) ( 00166 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) | 00167 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 | 00168 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2); 00169 if (image->matte != MagickFalse) 00170 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->alpha)) >> index) & 00171 0x01) << 3; 00172 return(id); 00173 } 00174 00175 static CubeInfo *ClassifyImageColors(const Image *image, 00176 ExceptionInfo *exception) 00177 { 00178 #define EvaluateImageTag " Compute image colors... " 00179 00180 CacheView 00181 *image_view; 00182 00183 CubeInfo 00184 *cube_info; 00185 00186 MagickBooleanType 00187 proceed; 00188 00189 PixelInfo 00190 pixel, 00191 target; 00192 00193 NodeInfo 00194 *node_info; 00195 00196 register const Quantum 00197 *p; 00198 00199 register size_t 00200 id, 00201 index, 00202 level; 00203 00204 register ssize_t 00205 i, 00206 x; 00207 00208 ssize_t 00209 y; 00210 00211 /* 00212 Initialize color description tree. 00213 */ 00214 assert(image != (const Image *) NULL); 00215 assert(image->signature == MagickSignature); 00216 if (image->debug != MagickFalse) 00217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00218 cube_info=GetCubeInfo(); 00219 if (cube_info == (CubeInfo *) NULL) 00220 { 00221 (void) ThrowMagickException(exception,GetMagickModule(), 00222 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 00223 return(cube_info); 00224 } 00225 GetPixelInfo(image,&pixel); 00226 GetPixelInfo(image,&target); 00227 image_view=AcquireCacheView(image); 00228 for (y=0; y < (ssize_t) image->rows; y++) 00229 { 00230 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00231 if (p == (const Quantum *) NULL) 00232 break; 00233 for (x=0; x < (ssize_t) image->columns; x++) 00234 { 00235 /* 00236 Start at the root and proceed level by level. 00237 */ 00238 node_info=cube_info->root; 00239 index=MaxTreeDepth-1; 00240 for (level=1; level < MaxTreeDepth; level++) 00241 { 00242 GetPixelInfoPixel(image,p,&pixel); 00243 id=ColorToNodeId(image,&pixel,index); 00244 if (node_info->child[id] == (NodeInfo *) NULL) 00245 { 00246 node_info->child[id]=GetNodeInfo(cube_info,level); 00247 if (node_info->child[id] == (NodeInfo *) NULL) 00248 { 00249 (void) ThrowMagickException(exception,GetMagickModule(), 00250 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00251 image->filename); 00252 return(0); 00253 } 00254 } 00255 node_info=node_info->child[id]; 00256 index--; 00257 } 00258 for (i=0; i < (ssize_t) node_info->number_unique; i++) 00259 { 00260 target=node_info->list[i]; 00261 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse) 00262 break; 00263 } 00264 if (i < (ssize_t) node_info->number_unique) 00265 node_info->list[i].count++; 00266 else 00267 { 00268 if (node_info->number_unique == 0) 00269 node_info->list=(PixelInfo *) AcquireMagickMemory( 00270 sizeof(*node_info->list)); 00271 else 00272 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list, 00273 (size_t) (i+1),sizeof(*node_info->list)); 00274 if (node_info->list == (PixelInfo *) NULL) 00275 { 00276 (void) ThrowMagickException(exception,GetMagickModule(), 00277 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00278 image->filename); 00279 return(0); 00280 } 00281 node_info->list[i]=pixel; 00282 node_info->list[i].red=GetPixelRed(image,p); 00283 node_info->list[i].green=GetPixelGreen(image,p); 00284 node_info->list[i].blue=GetPixelBlue(image,p); 00285 if (image->colorspace == CMYKColorspace) 00286 node_info->list[i].black=GetPixelBlack(image,p); 00287 node_info->list[i].alpha=GetPixelAlpha(image,p); 00288 node_info->list[i].count=1; 00289 node_info->number_unique++; 00290 cube_info->colors++; 00291 } 00292 p+=GetPixelChannels(image); 00293 } 00294 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y, 00295 image->rows); 00296 if (proceed == MagickFalse) 00297 break; 00298 } 00299 image_view=DestroyCacheView(image_view); 00300 return(cube_info); 00301 } 00302 00303 /* 00304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00305 % % 00306 % % 00307 % % 00308 + D e f i n e I m a g e H i s t o g r a m % 00309 % % 00310 % % 00311 % % 00312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00313 % 00314 % DefineImageHistogram() traverses the color cube tree and notes each colormap 00315 % entry. A colormap entry is any node in the color cube tree where the 00316 % of unique colors is not zero. 00317 % 00318 % The format of the DefineImageHistogram method is: 00319 % 00320 % DefineImageHistogram(const Image *image,NodeInfo *node_info, 00321 % PixelInfo **unique_colors) 00322 % 00323 % A description of each parameter follows. 00324 % 00325 % o image: the image. 00326 % 00327 % o node_info: the address of a structure of type NodeInfo which points to a 00328 % node in the color cube tree that is to be pruned. 00329 % 00330 % o histogram: the image histogram. 00331 % 00332 */ 00333 static void DefineImageHistogram(const Image *image,NodeInfo *node_info, 00334 PixelInfo **histogram) 00335 { 00336 register ssize_t 00337 i; 00338 00339 size_t 00340 number_children; 00341 00342 /* 00343 Traverse any children. 00344 */ 00345 number_children=image->matte == MagickFalse ? 8UL : 16UL; 00346 for (i=0; i < (ssize_t) number_children; i++) 00347 if (node_info->child[i] != (NodeInfo *) NULL) 00348 DefineImageHistogram(image,node_info->child[i],histogram); 00349 if (node_info->level == (MaxTreeDepth-1)) 00350 { 00351 register PixelInfo 00352 *p; 00353 00354 p=node_info->list; 00355 for (i=0; i < (ssize_t) node_info->number_unique; i++) 00356 { 00357 **histogram=(*p); 00358 (*histogram)++; 00359 p++; 00360 } 00361 } 00362 } 00363 00364 /* 00365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00366 % % 00367 % % 00368 % % 00369 + D e s t r o y C u b e I n f o % 00370 % % 00371 % % 00372 % % 00373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00374 % 00375 % DestroyCubeInfo() deallocates memory associated with a CubeInfo structure. 00376 % 00377 % The format of the DestroyCubeInfo method is: 00378 % 00379 % DestroyCubeInfo(const Image *image,CubeInfo *cube_info) 00380 % 00381 % A description of each parameter follows: 00382 % 00383 % o image: the image. 00384 % 00385 % o cube_info: the address of a structure of type CubeInfo. 00386 % 00387 */ 00388 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info) 00389 { 00390 register Nodes 00391 *nodes; 00392 00393 /* 00394 Release color cube tree storage. 00395 */ 00396 DestroyColorCube(image,cube_info->root); 00397 do 00398 { 00399 nodes=cube_info->node_queue->next; 00400 cube_info->node_queue=(Nodes *) 00401 RelinquishMagickMemory(cube_info->node_queue); 00402 cube_info->node_queue=nodes; 00403 } while (cube_info->node_queue != (Nodes *) NULL); 00404 return((CubeInfo *) RelinquishMagickMemory(cube_info)); 00405 } 00406 00407 /* 00408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00409 % % 00410 % % 00411 % % 00412 + D e s t r o y C o l o r C u b e % 00413 % % 00414 % % 00415 % % 00416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00417 % 00418 % DestroyColorCube() traverses the color cube tree and frees the list of 00419 % unique colors. 00420 % 00421 % The format of the DestroyColorCube method is: 00422 % 00423 % void DestroyColorCube(const Image *image,const NodeInfo *node_info) 00424 % 00425 % A description of each parameter follows. 00426 % 00427 % o image: the image. 00428 % 00429 % o node_info: the address of a structure of type NodeInfo which points to a 00430 % node in the color cube tree that is to be pruned. 00431 % 00432 */ 00433 static void DestroyColorCube(const Image *image,NodeInfo *node_info) 00434 { 00435 register ssize_t 00436 i; 00437 00438 size_t 00439 number_children; 00440 00441 /* 00442 Traverse any children. 00443 */ 00444 number_children=image->matte == MagickFalse ? 8UL : 16UL; 00445 for (i=0; i < (ssize_t) number_children; i++) 00446 if (node_info->child[i] != (NodeInfo *) NULL) 00447 DestroyColorCube(image,node_info->child[i]); 00448 if (node_info->list != (PixelInfo *) NULL) 00449 node_info->list=(PixelInfo *) RelinquishMagickMemory(node_info->list); 00450 } 00451 00452 /* 00453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00454 % % 00455 % % 00456 % % 00457 + G e t C u b e I n f o % 00458 % % 00459 % % 00460 % % 00461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00462 % 00463 % GetCubeInfo() initializes the CubeInfo data structure. 00464 % 00465 % The format of the GetCubeInfo method is: 00466 % 00467 % cube_info=GetCubeInfo() 00468 % 00469 % A description of each parameter follows. 00470 % 00471 % o cube_info: A pointer to the Cube structure. 00472 % 00473 */ 00474 static CubeInfo *GetCubeInfo(void) 00475 { 00476 CubeInfo 00477 *cube_info; 00478 00479 /* 00480 Initialize tree to describe color cube. 00481 */ 00482 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info)); 00483 if (cube_info == (CubeInfo *) NULL) 00484 return((CubeInfo *) NULL); 00485 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info)); 00486 /* 00487 Initialize root node. 00488 */ 00489 cube_info->root=GetNodeInfo(cube_info,0); 00490 if (cube_info->root == (NodeInfo *) NULL) 00491 return((CubeInfo *) NULL); 00492 return(cube_info); 00493 } 00494 00495 /* 00496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00497 % % 00498 % % 00499 % % 00500 % G e t I m a g e H i s t o g r a m % 00501 % % 00502 % % 00503 % % 00504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00505 % 00506 % GetImageHistogram() returns the unique colors in an image. 00507 % 00508 % The format of the GetImageHistogram method is: 00509 % 00510 % size_t GetImageHistogram(const Image *image, 00511 % size_t *number_colors,ExceptionInfo *exception) 00512 % 00513 % A description of each parameter follows. 00514 % 00515 % o image: the image. 00516 % 00517 % o file: Write a histogram of the color distribution to this file handle. 00518 % 00519 % o exception: return any errors or warnings in this structure. 00520 % 00521 */ 00522 MagickExport PixelInfo *GetImageHistogram(const Image *image, 00523 size_t *number_colors,ExceptionInfo *exception) 00524 { 00525 PixelInfo 00526 *histogram; 00527 00528 CubeInfo 00529 *cube_info; 00530 00531 *number_colors=0; 00532 histogram=(PixelInfo *) NULL; 00533 cube_info=ClassifyImageColors(image,exception); 00534 if (cube_info != (CubeInfo *) NULL) 00535 { 00536 histogram=(PixelInfo *) AcquireQuantumMemory((size_t) cube_info->colors, 00537 sizeof(*histogram)); 00538 if (histogram == (PixelInfo *) NULL) 00539 (void) ThrowMagickException(exception,GetMagickModule(), 00540 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 00541 else 00542 { 00543 PixelInfo 00544 *root; 00545 00546 *number_colors=cube_info->colors; 00547 root=histogram; 00548 DefineImageHistogram(image,cube_info->root,&root); 00549 } 00550 } 00551 cube_info=DestroyCubeInfo(image,cube_info); 00552 return(histogram); 00553 } 00554 00555 /* 00556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00557 % % 00558 % % 00559 % % 00560 + G e t N o d e I n f o % 00561 % % 00562 % % 00563 % % 00564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00565 % 00566 % GetNodeInfo() allocates memory for a new node in the color cube tree and 00567 % presets all fields to zero. 00568 % 00569 % The format of the GetNodeInfo method is: 00570 % 00571 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level) 00572 % 00573 % A description of each parameter follows. 00574 % 00575 % o cube_info: A pointer to the CubeInfo structure. 00576 % 00577 % o level: Specifies the level in the storage_class the node resides. 00578 % 00579 */ 00580 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level) 00581 { 00582 NodeInfo 00583 *node_info; 00584 00585 if (cube_info->free_nodes == 0) 00586 { 00587 Nodes 00588 *nodes; 00589 00590 /* 00591 Allocate a new nodes of nodes. 00592 */ 00593 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes)); 00594 if (nodes == (Nodes *) NULL) 00595 return((NodeInfo *) NULL); 00596 nodes->next=cube_info->node_queue; 00597 cube_info->node_queue=nodes; 00598 cube_info->node_info=nodes->nodes; 00599 cube_info->free_nodes=NodesInAList; 00600 } 00601 cube_info->free_nodes--; 00602 node_info=cube_info->node_info++; 00603 (void) ResetMagickMemory(node_info,0,sizeof(*node_info)); 00604 node_info->level=level; 00605 return(node_info); 00606 } 00607 00608 /* 00609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00610 % % 00611 % % 00612 % % 00613 % I s H i s t o g r a m I m a g e % 00614 % % 00615 % % 00616 % % 00617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00618 % 00619 % IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or 00620 % less. 00621 % 00622 % The format of the IsHistogramImage method is: 00623 % 00624 % MagickBooleanType IsHistogramImage(const Image *image, 00625 % ExceptionInfo *exception) 00626 % 00627 % A description of each parameter follows. 00628 % 00629 % o image: the image. 00630 % 00631 % o exception: return any errors or warnings in this structure. 00632 % 00633 */ 00634 MagickExport MagickBooleanType IsHistogramImage(const Image *image, 00635 ExceptionInfo *exception) 00636 { 00637 #define MaximumUniqueColors 1024 00638 00639 CacheView 00640 *image_view; 00641 00642 CubeInfo 00643 *cube_info; 00644 00645 PixelInfo 00646 pixel, 00647 target; 00648 00649 register const Quantum 00650 *p; 00651 00652 register ssize_t 00653 x; 00654 00655 register NodeInfo 00656 *node_info; 00657 00658 register ssize_t 00659 i; 00660 00661 size_t 00662 id, 00663 index, 00664 level; 00665 00666 ssize_t 00667 y; 00668 00669 assert(image != (Image *) NULL); 00670 assert(image->signature == MagickSignature); 00671 if (image->debug != MagickFalse) 00672 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00673 if ((image->storage_class == PseudoClass) && (image->colors <= 256)) 00674 return(MagickTrue); 00675 if (image->storage_class == PseudoClass) 00676 return(MagickFalse); 00677 /* 00678 Initialize color description tree. 00679 */ 00680 cube_info=GetCubeInfo(); 00681 if (cube_info == (CubeInfo *) NULL) 00682 { 00683 (void) ThrowMagickException(exception,GetMagickModule(), 00684 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 00685 return(MagickFalse); 00686 } 00687 GetPixelInfo(image,&pixel); 00688 GetPixelInfo(image,&target); 00689 image_view=AcquireCacheView(image); 00690 for (y=0; y < (ssize_t) image->rows; y++) 00691 { 00692 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00693 if (p == (const Quantum *) NULL) 00694 break; 00695 for (x=0; x < (ssize_t) image->columns; x++) 00696 { 00697 /* 00698 Start at the root and proceed level by level. 00699 */ 00700 node_info=cube_info->root; 00701 index=MaxTreeDepth-1; 00702 for (level=1; level < MaxTreeDepth; level++) 00703 { 00704 GetPixelInfoPixel(image,p,&pixel); 00705 id=ColorToNodeId(image,&pixel,index); 00706 if (node_info->child[id] == (NodeInfo *) NULL) 00707 { 00708 node_info->child[id]=GetNodeInfo(cube_info,level); 00709 if (node_info->child[id] == (NodeInfo *) NULL) 00710 { 00711 (void) ThrowMagickException(exception,GetMagickModule(), 00712 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00713 image->filename); 00714 break; 00715 } 00716 } 00717 node_info=node_info->child[id]; 00718 index--; 00719 } 00720 if (level < MaxTreeDepth) 00721 break; 00722 for (i=0; i < (ssize_t) node_info->number_unique; i++) 00723 { 00724 target=node_info->list[i]; 00725 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse) 00726 break; 00727 } 00728 if (i < (ssize_t) node_info->number_unique) 00729 node_info->list[i].count++; 00730 else 00731 { 00732 /* 00733 Add this unique color to the color list. 00734 */ 00735 if (node_info->number_unique == 0) 00736 node_info->list=(PixelInfo *) AcquireMagickMemory( 00737 sizeof(*node_info->list)); 00738 else 00739 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list, 00740 (size_t) (i+1),sizeof(*node_info->list)); 00741 if (node_info->list == (PixelInfo *) NULL) 00742 { 00743 (void) ThrowMagickException(exception,GetMagickModule(), 00744 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00745 image->filename); 00746 break; 00747 } 00748 node_info->list[i].red=GetPixelRed(image,p); 00749 node_info->list[i].green=GetPixelGreen(image,p); 00750 node_info->list[i].blue=GetPixelBlue(image,p); 00751 if (image->colorspace == CMYKColorspace) 00752 node_info->list[i].black=GetPixelBlack(image,p); 00753 node_info->list[i].alpha=GetPixelAlpha(image,p); 00754 node_info->list[i].count=1; 00755 node_info->number_unique++; 00756 cube_info->colors++; 00757 if (cube_info->colors > MaximumUniqueColors) 00758 break; 00759 } 00760 p+=GetPixelChannels(image); 00761 } 00762 if (x < (ssize_t) image->columns) 00763 break; 00764 } 00765 image_view=DestroyCacheView(image_view); 00766 cube_info=DestroyCubeInfo(image,cube_info); 00767 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue); 00768 } 00769 00770 /* 00771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00772 % % 00773 % % 00774 % % 00775 % I s P a l e t t e I m a g e % 00776 % % 00777 % % 00778 % % 00779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00780 % 00781 % IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256 00782 % unique colors or less. 00783 % 00784 % The format of the IsPaletteImage method is: 00785 % 00786 % MagickBooleanType IsPaletteImage(const Image *image, 00787 % ExceptionInfo *exception) 00788 % 00789 % A description of each parameter follows. 00790 % 00791 % o image: the image. 00792 % 00793 % o exception: return any errors or warnings in this structure. 00794 % 00795 */ 00796 MagickExport MagickBooleanType IsPaletteImage(const Image *image, 00797 ExceptionInfo *exception) 00798 { 00799 CacheView 00800 *image_view; 00801 00802 CubeInfo 00803 *cube_info; 00804 00805 PixelInfo 00806 pixel, 00807 target; 00808 00809 register const Quantum 00810 *p; 00811 00812 register ssize_t 00813 x; 00814 00815 register NodeInfo 00816 *node_info; 00817 00818 register ssize_t 00819 i; 00820 00821 size_t 00822 id, 00823 index, 00824 level; 00825 00826 ssize_t 00827 y; 00828 00829 assert(image != (Image *) NULL); 00830 assert(image->signature == MagickSignature); 00831 if (image->debug != MagickFalse) 00832 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00833 if ((image->storage_class == PseudoClass) && (image->colors <= 256)) 00834 return(MagickTrue); 00835 if (image->storage_class == PseudoClass) 00836 return(MagickFalse); 00837 /* 00838 Initialize color description tree. 00839 */ 00840 cube_info=GetCubeInfo(); 00841 if (cube_info == (CubeInfo *) NULL) 00842 { 00843 (void) ThrowMagickException(exception,GetMagickModule(), 00844 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 00845 return(MagickFalse); 00846 } 00847 GetPixelInfo(image,&pixel); 00848 GetPixelInfo(image,&target); 00849 image_view=AcquireCacheView(image); 00850 for (y=0; y < (ssize_t) image->rows; y++) 00851 { 00852 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00853 if (p == (const Quantum *) NULL) 00854 break; 00855 for (x=0; x < (ssize_t) image->columns; x++) 00856 { 00857 /* 00858 Start at the root and proceed level by level. 00859 */ 00860 node_info=cube_info->root; 00861 index=MaxTreeDepth-1; 00862 for (level=1; level < MaxTreeDepth; level++) 00863 { 00864 GetPixelInfoPixel(image,p,&pixel); 00865 id=ColorToNodeId(image,&pixel,index); 00866 if (node_info->child[id] == (NodeInfo *) NULL) 00867 { 00868 node_info->child[id]=GetNodeInfo(cube_info,level); 00869 if (node_info->child[id] == (NodeInfo *) NULL) 00870 { 00871 (void) ThrowMagickException(exception,GetMagickModule(), 00872 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00873 image->filename); 00874 break; 00875 } 00876 } 00877 node_info=node_info->child[id]; 00878 index--; 00879 } 00880 if (level < MaxTreeDepth) 00881 break; 00882 for (i=0; i < (ssize_t) node_info->number_unique; i++) 00883 { 00884 target=node_info->list[i]; 00885 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse) 00886 break; 00887 } 00888 if (i < (ssize_t) node_info->number_unique) 00889 node_info->list[i].count++; 00890 else 00891 { 00892 /* 00893 Add this unique color to the color list. 00894 */ 00895 if (node_info->number_unique == 0) 00896 node_info->list=(PixelInfo *) AcquireMagickMemory( 00897 sizeof(*node_info->list)); 00898 else 00899 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list, 00900 (size_t) (i+1),sizeof(*node_info->list)); 00901 if (node_info->list == (PixelInfo *) NULL) 00902 { 00903 (void) ThrowMagickException(exception,GetMagickModule(), 00904 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00905 image->filename); 00906 break; 00907 } 00908 node_info->list[i]=pixel; 00909 node_info->list[i].red=GetPixelRed(image,p); 00910 node_info->list[i].green=GetPixelGreen(image,p); 00911 node_info->list[i].blue=GetPixelBlue(image,p); 00912 if (image->colorspace == CMYKColorspace) 00913 node_info->list[i].black=GetPixelBlack(image,p); 00914 node_info->list[i].alpha=GetPixelAlpha(image,p); 00915 node_info->list[i].count=1; 00916 node_info->number_unique++; 00917 cube_info->colors++; 00918 if (cube_info->colors > 256) 00919 break; 00920 } 00921 p+=GetPixelChannels(image); 00922 } 00923 if (x < (ssize_t) image->columns) 00924 break; 00925 } 00926 image_view=DestroyCacheView(image_view); 00927 cube_info=DestroyCubeInfo(image,cube_info); 00928 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue); 00929 } 00930 00931 /* 00932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00933 % % 00934 % % 00935 % % 00936 % M i n M a x S t r e t c h I m a g e % 00937 % % 00938 % % 00939 % % 00940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00941 % 00942 % MinMaxStretchImage() uses the exact minimum and maximum values found in 00943 % each of the channels given, as the BlackPoint and WhitePoint to linearly 00944 % stretch the colors (and histogram) of the image. The stretch points are 00945 % also moved further inward by the adjustment values given. 00946 % 00947 % If the adjustment values are both zero this function is equivalent to a 00948 % perfect normalization (or autolevel) of the image. 00949 % 00950 % Each channel is stretched independantally of each other (producing color 00951 % distortion) unless the special 'SyncChannels' flag is also provided in the 00952 % channels setting. If this flag is present the minimum and maximum point 00953 % will be extracted from all the given channels, and those channels will be 00954 % stretched by exactly the same amount (preventing color distortion). 00955 % 00956 % In the special case that only ONE value is found in a channel of the image 00957 % that value is not stretched, that value is left as is. 00958 % 00959 % The 'SyncChannels' is turned on in the 'DefaultChannels' setting by 00960 % default. 00961 % 00962 % The format of the MinMaxStretchImage method is: 00963 % 00964 % MagickBooleanType MinMaxStretchImage(Image *image,const double black, 00965 % const double white,const double gamma,ExceptionInfo *exception) 00966 % 00967 % A description of each parameter follows: 00968 % 00969 % o image: The image to auto-level 00970 % 00971 % o black, white: move the black / white point inward from the minimum and 00972 % maximum points by this color value. 00973 % 00974 % o gamma: the gamma. 00975 % 00976 % o exception: return any errors or warnings in this structure. 00977 % 00978 */ 00979 MagickExport MagickBooleanType MinMaxStretchImage(Image *image, 00980 const double black,const double white,const double gamma, 00981 ExceptionInfo *exception) 00982 { 00983 double 00984 min, 00985 max; 00986 00987 register ssize_t 00988 i; 00989 00990 MagickStatusType 00991 status; 00992 00993 status=MagickTrue; 00994 if (image->channel_mask == DefaultChannels) 00995 { 00996 /* 00997 Auto-level all channels equally. 00998 */ 00999 (void) GetImageRange(image,&min,&max,exception); 01000 min+=black; 01001 max-=white; 01002 if (fabs(min-max) >= MagickEpsilon) 01003 status&=LevelImage(image,min,max,gamma,exception); 01004 return(status != 0 ? MagickTrue : MagickFalse); 01005 } 01006 /* 01007 Auto-level each channel separately. 01008 */ 01009 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01010 { 01011 ChannelType 01012 channel_mask; 01013 01014 PixelChannel 01015 channel; 01016 01017 PixelTrait 01018 traits; 01019 01020 channel=GetPixelChannelMapChannel(image,i); 01021 traits=GetPixelChannelMapTraits(image,channel); 01022 if ((traits & UpdatePixelTrait) == 0) 01023 continue; 01024 channel_mask=SetPixelChannelMask(image,(ChannelType) (1 << i)); 01025 status&=GetImageRange(image,&min,&max,exception); 01026 min+=black; 01027 max-=white; 01028 if (fabs(min-max) >= MagickEpsilon) 01029 status&=LevelImage(image,min,max,gamma,exception); 01030 (void) SetPixelChannelMask(image,channel_mask); 01031 } 01032 return(status != 0 ? MagickTrue : MagickFalse); 01033 } 01034 01035 /* 01036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01037 % % 01038 % % 01039 % % 01040 % G e t N u m b e r C o l o r s % 01041 % % 01042 % % 01043 % % 01044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01045 % 01046 % GetNumberColors() returns the number of unique colors in an image. 01047 % 01048 % The format of the GetNumberColors method is: 01049 % 01050 % size_t GetNumberColors(const Image *image,FILE *file, 01051 % ExceptionInfo *exception) 01052 % 01053 % A description of each parameter follows. 01054 % 01055 % o image: the image. 01056 % 01057 % o file: Write a histogram of the color distribution to this file handle. 01058 % 01059 % o exception: return any errors or warnings in this structure. 01060 % 01061 */ 01062 01063 #if defined(__cplusplus) || defined(c_plusplus) 01064 extern "C" { 01065 #endif 01066 01067 static int HistogramCompare(const void *x,const void *y) 01068 { 01069 const PixelInfo 01070 *color_1, 01071 *color_2; 01072 01073 color_1=(const PixelInfo *) x; 01074 color_2=(const PixelInfo *) y; 01075 if (color_2->red != color_1->red) 01076 return((int) color_1->red-(int) color_2->red); 01077 if (color_2->green != color_1->green) 01078 return((int) color_1->green-(int) color_2->green); 01079 if (color_2->blue != color_1->blue) 01080 return((int) color_1->blue-(int) color_2->blue); 01081 return((int) color_2->count-(int) color_1->count); 01082 } 01083 01084 #if defined(__cplusplus) || defined(c_plusplus) 01085 } 01086 #endif 01087 01088 MagickExport size_t GetNumberColors(const Image *image,FILE *file, 01089 ExceptionInfo *exception) 01090 { 01091 #define HistogramImageTag "Histogram/Image" 01092 01093 char 01094 color[MaxTextExtent], 01095 hex[MaxTextExtent], 01096 tuple[MaxTextExtent]; 01097 01098 PixelInfo 01099 *histogram; 01100 01101 MagickBooleanType 01102 status; 01103 01104 PixelInfo 01105 pixel; 01106 01107 register PixelInfo 01108 *p; 01109 01110 register ssize_t 01111 i; 01112 01113 size_t 01114 number_colors; 01115 01116 number_colors=0; 01117 if (file == (FILE *) NULL) 01118 { 01119 CubeInfo 01120 *cube_info; 01121 01122 cube_info=ClassifyImageColors(image,exception); 01123 if (cube_info != (CubeInfo *) NULL) 01124 number_colors=cube_info->colors; 01125 cube_info=DestroyCubeInfo(image,cube_info); 01126 return(number_colors); 01127 } 01128 histogram=GetImageHistogram(image,&number_colors,exception); 01129 if (histogram == (PixelInfo *) NULL) 01130 return(number_colors); 01131 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram), 01132 HistogramCompare); 01133 GetPixelInfo(image,&pixel); 01134 p=histogram; 01135 status=MagickTrue; 01136 for (i=0; i < (ssize_t) number_colors; i++) 01137 { 01138 pixel=(*p); 01139 (void) CopyMagickString(tuple,"(",MaxTextExtent); 01140 ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,tuple); 01141 (void) ConcatenateMagickString(tuple,",",MaxTextExtent); 01142 ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,tuple); 01143 (void) ConcatenateMagickString(tuple,",",MaxTextExtent); 01144 ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,tuple); 01145 if (pixel.colorspace == CMYKColorspace) 01146 { 01147 (void) ConcatenateMagickString(tuple,",",MaxTextExtent); 01148 ConcatenateColorComponent(&pixel,BlackPixelChannel,X11Compliance, 01149 tuple); 01150 } 01151 if (pixel.matte != MagickFalse) 01152 { 01153 (void) ConcatenateMagickString(tuple,",",MaxTextExtent); 01154 ConcatenateColorComponent(&pixel,AlphaPixelChannel,X11Compliance, 01155 tuple); 01156 } 01157 (void) ConcatenateMagickString(tuple,")",MaxTextExtent); 01158 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception); 01159 GetColorTuple(&pixel,MagickTrue,hex); 01160 (void) FormatLocaleFile(file,"%10" MagickSizeFormat,p->count); 01161 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color); 01162 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01163 { 01164 MagickBooleanType 01165 proceed; 01166 01167 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i, 01168 number_colors); 01169 if (proceed == MagickFalse) 01170 status=MagickFalse; 01171 } 01172 p++; 01173 } 01174 (void) fflush(file); 01175 histogram=(PixelInfo *) RelinquishMagickMemory(histogram); 01176 if (status == MagickFalse) 01177 return(0); 01178 return(number_colors); 01179 } 01180 01181 /* 01182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01183 % % 01184 % % 01185 % % 01186 % U n i q u e I m a g e C o l o r s % 01187 % % 01188 % % 01189 % % 01190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01191 % 01192 % UniqueImageColors() returns the unique colors of an image. 01193 % 01194 % The format of the UniqueImageColors method is: 01195 % 01196 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception) 01197 % 01198 % A description of each parameter follows. 01199 % 01200 % o image: the image. 01201 % 01202 % o exception: return any errors or warnings in this structure. 01203 % 01204 */ 01205 01206 static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view, 01207 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception) 01208 { 01209 #define UniqueColorsImageTag "UniqueColors/Image" 01210 01211 MagickBooleanType 01212 status; 01213 01214 register ssize_t 01215 i; 01216 01217 size_t 01218 number_children; 01219 01220 /* 01221 Traverse any children. 01222 */ 01223 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL; 01224 for (i=0; i < (ssize_t) number_children; i++) 01225 if (node_info->child[i] != (NodeInfo *) NULL) 01226 UniqueColorsToImage(unique_image,unique_view,cube_info, 01227 node_info->child[i],exception); 01228 if (node_info->level == (MaxTreeDepth-1)) 01229 { 01230 register PixelInfo 01231 *p; 01232 01233 register Quantum 01234 *restrict q; 01235 01236 status=MagickTrue; 01237 p=node_info->list; 01238 for (i=0; i < (ssize_t) node_info->number_unique; i++) 01239 { 01240 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1, 01241 exception); 01242 if (q == (Quantum *) NULL) 01243 continue; 01244 SetPixelRed(unique_image,p->red,q); 01245 SetPixelGreen(unique_image,p->green,q); 01246 SetPixelBlue(unique_image,p->blue,q); 01247 SetPixelAlpha(unique_image,p->alpha,q); 01248 if (unique_image->colorspace == CMYKColorspace) 01249 SetPixelBlack(unique_image,p->black,q); 01250 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse) 01251 break; 01252 cube_info->x++; 01253 p++; 01254 } 01255 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL) 01256 { 01257 MagickBooleanType 01258 proceed; 01259 01260 proceed=SetImageProgress(unique_image,UniqueColorsImageTag, 01261 cube_info->progress,cube_info->colors); 01262 if (proceed == MagickFalse) 01263 status=MagickFalse; 01264 } 01265 cube_info->progress++; 01266 if (status == MagickFalse) 01267 return; 01268 } 01269 } 01270 01271 MagickExport Image *UniqueImageColors(const Image *image, 01272 ExceptionInfo *exception) 01273 { 01274 CacheView 01275 *unique_view; 01276 01277 CubeInfo 01278 *cube_info; 01279 01280 Image 01281 *unique_image; 01282 01283 cube_info=ClassifyImageColors(image,exception); 01284 if (cube_info == (CubeInfo *) NULL) 01285 return((Image *) NULL); 01286 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception); 01287 if (unique_image == (Image *) NULL) 01288 return(unique_image); 01289 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse) 01290 { 01291 unique_image=DestroyImage(unique_image); 01292 return((Image *) NULL); 01293 } 01294 unique_view=AcquireCacheView(unique_image); 01295 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root, 01296 exception); 01297 unique_view=DestroyCacheView(unique_view); 01298 if (cube_info->colors < MaxColormapSize) 01299 { 01300 QuantizeInfo 01301 *quantize_info; 01302 01303 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL); 01304 quantize_info->number_colors=MaxColormapSize; 01305 quantize_info->dither=MagickFalse; 01306 quantize_info->tree_depth=8; 01307 (void) QuantizeImage(quantize_info,unique_image,exception); 01308 quantize_info=DestroyQuantizeInfo(quantize_info); 01309 } 01310 cube_info=DestroyCubeInfo(image,cube_info); 01311 return(unique_image); 01312 }