2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % H H IIIII SSSSS TTTTT OOO GGGG RRRR AAA M M %
7 % H H I SS T O O G R R A A MM MM %
8 % HHHHH I SSS T O O G GG RRRR AAAAA M M M %
9 % H H I SS T O O G G R R A A M M %
10 % H H IIIII SSSSS T OOO GGG R R A A M M %
13 % MagickCore Histogram Methods %
21 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/cache-view.h"
45 #include "MagickCore/color-private.h"
46 #include "MagickCore/enhance.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/hashmap.h"
50 #include "MagickCore/histogram.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/list.h"
53 #include "MagickCore/memory_.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/pixel-accessor.h"
56 #include "MagickCore/prepress.h"
57 #include "MagickCore/quantize.h"
58 #include "MagickCore/registry.h"
59 #include "MagickCore/semaphore.h"
60 #include "MagickCore/splay-tree.h"
61 #include "MagickCore/statistic.h"
62 #include "MagickCore/string_.h"
67 #define MaxTreeDepth 8
68 #define NodesInAList 1536
73 typedef struct _NodeInfo
97 typedef struct _CubeInfo
120 Forward declarations.
126 *GetNodeInfo(CubeInfo *,const size_t);
129 DestroyColorCube(const Image *,NodeInfo *);
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136 + C l a s s i f y I m a g e C o l o r s %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 % ClassifyImageColors() builds a populated CubeInfo tree for the specified
143 % image. The returned tree should be deallocated using DestroyCubeInfo()
144 % once it is no longer needed.
146 % The format of the ClassifyImageColors() method is:
148 % CubeInfo *ClassifyImageColors(const Image *image,
149 % ExceptionInfo *exception)
151 % A description of each parameter follows.
153 % o image: the image.
155 % o exception: return any errors or warnings in this structure.
159 static inline size_t ColorToNodeId(const Image *image,
160 const PixelInfo *pixel,size_t index)
166 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
167 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
168 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
169 if (image->matte != MagickFalse)
170 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->alpha)) >> index) &
175 static CubeInfo *ClassifyImageColors(const Image *image,
176 ExceptionInfo *exception)
178 #define EvaluateImageTag " Compute image colors... "
196 register const Quantum
212 Initialize color description tree.
214 assert(image != (const Image *) NULL);
215 assert(image->signature == MagickSignature);
216 if (image->debug != MagickFalse)
217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
218 cube_info=GetCubeInfo();
219 if (cube_info == (CubeInfo *) NULL)
221 (void) ThrowMagickException(exception,GetMagickModule(),
222 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
225 GetPixelInfo(image,&pixel);
226 GetPixelInfo(image,&target);
227 image_view=AcquireCacheView(image);
228 for (y=0; y < (ssize_t) image->rows; y++)
230 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
231 if (p == (const Quantum *) NULL)
233 for (x=0; x < (ssize_t) image->columns; x++)
236 Start at the root and proceed level by level.
238 node_info=cube_info->root;
239 index=MaxTreeDepth-1;
240 for (level=1; level < MaxTreeDepth; level++)
242 SetPixelInfo(image,p,&pixel);
243 id=ColorToNodeId(image,&pixel,index);
244 if (node_info->child[id] == (NodeInfo *) NULL)
246 node_info->child[id]=GetNodeInfo(cube_info,level);
247 if (node_info->child[id] == (NodeInfo *) NULL)
249 (void) ThrowMagickException(exception,GetMagickModule(),
250 ResourceLimitError,"MemoryAllocationFailed","`%s'",
255 node_info=node_info->child[id];
258 for (i=0; i < (ssize_t) node_info->number_unique; i++)
260 SetPixelInfoPacket(image,&node_info->list[i],&target);
261 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
264 if (i < (ssize_t) node_info->number_unique)
265 node_info->list[i].count++;
268 if (node_info->number_unique == 0)
269 node_info->list=(PixelPacket *) AcquireMagickMemory(
270 sizeof(*node_info->list));
272 node_info->list=(PixelPacket *) ResizeQuantumMemory(node_info->list,
273 (size_t) (i+1),sizeof(*node_info->list));
274 if (node_info->list == (PixelPacket *) NULL)
276 (void) ThrowMagickException(exception,GetMagickModule(),
277 ResourceLimitError,"MemoryAllocationFailed","`%s'",
281 node_info->list[i].red=GetPixelRed(image,p);
282 node_info->list[i].green=GetPixelGreen(image,p);
283 node_info->list[i].blue=GetPixelBlue(image,p);
284 if (image->colorspace == CMYKColorspace)
285 node_info->list[i].black=GetPixelBlack(image,p);
286 node_info->list[i].alpha=GetPixelAlpha(image,p);
287 node_info->list[i].count=1;
288 node_info->number_unique++;
291 p+=GetPixelChannels(image);
293 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
295 if (proceed == MagickFalse)
298 image_view=DestroyCacheView(image_view);
303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 + D e f i n e I m a g e H i s t o g r a m %
311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313 % DefineImageHistogram() traverses the color cube tree and notes each colormap
314 % entry. A colormap entry is any node in the color cube tree where the
315 % of unique colors is not zero.
317 % The format of the DefineImageHistogram method is:
319 % DefineImageHistogram(const Image *image,NodeInfo *node_info,
320 % PixelPacket **unique_colors)
322 % A description of each parameter follows.
324 % o image: the image.
326 % o node_info: the address of a structure of type NodeInfo which points to a
327 % node in the color cube tree that is to be pruned.
329 % o histogram: the image histogram.
332 static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
333 PixelPacket **histogram)
342 Traverse any children.
344 number_children=image->matte == MagickFalse ? 8UL : 16UL;
345 for (i=0; i < (ssize_t) number_children; i++)
346 if (node_info->child[i] != (NodeInfo *) NULL)
347 DefineImageHistogram(image,node_info->child[i],histogram);
348 if (node_info->level == (MaxTreeDepth-1))
354 for (i=0; i < (ssize_t) node_info->number_unique; i++)
356 (*histogram)->red=p->red;
357 (*histogram)->green=p->green;
358 (*histogram)->blue=p->blue;
359 (*histogram)->black=p->black;
360 (*histogram)->alpha=p->alpha;
361 (*histogram)->count=p->count;
369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
373 + D e s t r o y C u b e I n f o %
377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379 % DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
381 % The format of the DestroyCubeInfo method is:
383 % DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
385 % A description of each parameter follows:
387 % o image: the image.
389 % o cube_info: the address of a structure of type CubeInfo.
392 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
398 Release color cube tree storage.
400 DestroyColorCube(image,cube_info->root);
403 nodes=cube_info->node_queue->next;
404 cube_info->node_queue=(Nodes *)
405 RelinquishMagickMemory(cube_info->node_queue);
406 cube_info->node_queue=nodes;
407 } while (cube_info->node_queue != (Nodes *) NULL);
408 return((CubeInfo *) RelinquishMagickMemory(cube_info));
412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416 + D e s t r o y C o l o r C u b e %
420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 % DestroyColorCube() traverses the color cube tree and frees the list of
425 % The format of the DestroyColorCube method is:
427 % void DestroyColorCube(const Image *image,const NodeInfo *node_info)
429 % A description of each parameter follows.
431 % o image: the image.
433 % o node_info: the address of a structure of type NodeInfo which points to a
434 % node in the color cube tree that is to be pruned.
437 static void DestroyColorCube(const Image *image,NodeInfo *node_info)
446 Traverse any children.
448 number_children=image->matte == MagickFalse ? 8UL : 16UL;
449 for (i=0; i < (ssize_t) number_children; i++)
450 if (node_info->child[i] != (NodeInfo *) NULL)
451 DestroyColorCube(image,node_info->child[i]);
452 if (node_info->list != (PixelPacket *) NULL)
453 node_info->list=(PixelPacket *) RelinquishMagickMemory(node_info->list);
457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 + G e t C u b e I n f o %
465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 % GetCubeInfo() initializes the CubeInfo data structure.
469 % The format of the GetCubeInfo method is:
471 % cube_info=GetCubeInfo()
473 % A description of each parameter follows.
475 % o cube_info: A pointer to the Cube structure.
478 static CubeInfo *GetCubeInfo(void)
484 Initialize tree to describe color cube.
486 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
487 if (cube_info == (CubeInfo *) NULL)
488 return((CubeInfo *) NULL);
489 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
491 Initialize root node.
493 cube_info->root=GetNodeInfo(cube_info,0);
494 if (cube_info->root == (NodeInfo *) NULL)
495 return((CubeInfo *) NULL);
500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 % G e t I m a g e H i s t o g r a m %
508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 % GetImageHistogram() returns the unique colors in an image.
512 % The format of the GetImageHistogram method is:
514 % size_t GetImageHistogram(const Image *image,
515 % size_t *number_colors,ExceptionInfo *exception)
517 % A description of each parameter follows.
519 % o image: the image.
521 % o file: Write a histogram of the color distribution to this file handle.
523 % o exception: return any errors or warnings in this structure.
526 MagickExport PixelPacket *GetImageHistogram(const Image *image,
527 size_t *number_colors,ExceptionInfo *exception)
536 histogram=(PixelPacket *) NULL;
537 cube_info=ClassifyImageColors(image,exception);
538 if (cube_info != (CubeInfo *) NULL)
540 histogram=(PixelPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
542 if (histogram == (PixelPacket *) NULL)
543 (void) ThrowMagickException(exception,GetMagickModule(),
544 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
550 *number_colors=cube_info->colors;
552 DefineImageHistogram(image,cube_info->root,&root);
555 cube_info=DestroyCubeInfo(image,cube_info);
560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564 + G e t N o d e I n f o %
568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570 % GetNodeInfo() allocates memory for a new node in the color cube tree and
571 % presets all fields to zero.
573 % The format of the GetNodeInfo method is:
575 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
577 % A description of each parameter follows.
579 % o cube_info: A pointer to the CubeInfo structure.
581 % o level: Specifies the level in the storage_class the node resides.
584 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
589 if (cube_info->free_nodes == 0)
595 Allocate a new nodes of nodes.
597 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
598 if (nodes == (Nodes *) NULL)
599 return((NodeInfo *) NULL);
600 nodes->next=cube_info->node_queue;
601 cube_info->node_queue=nodes;
602 cube_info->node_info=nodes->nodes;
603 cube_info->free_nodes=NodesInAList;
605 cube_info->free_nodes--;
606 node_info=cube_info->node_info++;
607 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
608 node_info->level=level;
613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
617 % I s H i s t o g r a m I m a g e %
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623 % IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
626 % The format of the IsHistogramImage method is:
628 % MagickBooleanType IsHistogramImage(const Image *image,
629 % ExceptionInfo *exception)
631 % A description of each parameter follows.
633 % o image: the image.
635 % o exception: return any errors or warnings in this structure.
638 MagickExport MagickBooleanType IsHistogramImage(const Image *image,
639 ExceptionInfo *exception)
641 #define MaximumUniqueColors 1024
653 register const Quantum
673 assert(image != (Image *) NULL);
674 assert(image->signature == MagickSignature);
675 if (image->debug != MagickFalse)
676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
677 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
679 if (image->storage_class == PseudoClass)
682 Initialize color description tree.
684 cube_info=GetCubeInfo();
685 if (cube_info == (CubeInfo *) NULL)
687 (void) ThrowMagickException(exception,GetMagickModule(),
688 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
691 GetPixelInfo(image,&pixel);
692 GetPixelInfo(image,&target);
693 image_view=AcquireCacheView(image);
694 for (y=0; y < (ssize_t) image->rows; y++)
696 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
697 if (p == (const Quantum *) NULL)
699 for (x=0; x < (ssize_t) image->columns; x++)
702 Start at the root and proceed level by level.
704 node_info=cube_info->root;
705 index=MaxTreeDepth-1;
706 for (level=1; level < MaxTreeDepth; level++)
708 SetPixelInfo(image,p,&pixel);
709 id=ColorToNodeId(image,&pixel,index);
710 if (node_info->child[id] == (NodeInfo *) NULL)
712 node_info->child[id]=GetNodeInfo(cube_info,level);
713 if (node_info->child[id] == (NodeInfo *) NULL)
715 (void) ThrowMagickException(exception,GetMagickModule(),
716 ResourceLimitError,"MemoryAllocationFailed","`%s'",
721 node_info=node_info->child[id];
724 if (level < MaxTreeDepth)
726 for (i=0; i < (ssize_t) node_info->number_unique; i++)
728 SetPixelInfoPacket(image,&node_info->list[i],&target);
729 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
732 if (i < (ssize_t) node_info->number_unique)
733 node_info->list[i].count++;
737 Add this unique color to the color list.
739 if (node_info->number_unique == 0)
740 node_info->list=(PixelPacket *) AcquireMagickMemory(
741 sizeof(*node_info->list));
743 node_info->list=(PixelPacket *) ResizeQuantumMemory(node_info->list,
744 (size_t) (i+1),sizeof(*node_info->list));
745 if (node_info->list == (PixelPacket *) NULL)
747 (void) ThrowMagickException(exception,GetMagickModule(),
748 ResourceLimitError,"MemoryAllocationFailed","`%s'",
752 node_info->list[i].red=GetPixelRed(image,p);
753 node_info->list[i].green=GetPixelGreen(image,p);
754 node_info->list[i].blue=GetPixelBlue(image,p);
755 if (image->colorspace == CMYKColorspace)
756 node_info->list[i].black=GetPixelBlack(image,p);
757 node_info->list[i].alpha=GetPixelAlpha(image,p);
758 node_info->list[i].count=1;
759 node_info->number_unique++;
761 if (cube_info->colors > MaximumUniqueColors)
764 p+=GetPixelChannels(image);
766 if (x < (ssize_t) image->columns)
769 image_view=DestroyCacheView(image_view);
770 cube_info=DestroyCubeInfo(image,cube_info);
771 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779 % I s P a l e t t e I m a g e %
783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785 % IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
786 % unique colors or less.
788 % The format of the IsPaletteImage method is:
790 % MagickBooleanType IsPaletteImage(const Image *image,
791 % ExceptionInfo *exception)
793 % A description of each parameter follows.
795 % o image: the image.
797 % o exception: return any errors or warnings in this structure.
800 MagickExport MagickBooleanType IsPaletteImage(const Image *image,
801 ExceptionInfo *exception)
813 register const Quantum
833 assert(image != (Image *) NULL);
834 assert(image->signature == MagickSignature);
835 if (image->debug != MagickFalse)
836 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
837 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
839 if (image->storage_class == PseudoClass)
842 Initialize color description tree.
844 cube_info=GetCubeInfo();
845 if (cube_info == (CubeInfo *) NULL)
847 (void) ThrowMagickException(exception,GetMagickModule(),
848 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
851 GetPixelInfo(image,&pixel);
852 GetPixelInfo(image,&target);
853 image_view=AcquireCacheView(image);
854 for (y=0; y < (ssize_t) image->rows; y++)
856 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
857 if (p == (const Quantum *) NULL)
859 for (x=0; x < (ssize_t) image->columns; x++)
862 Start at the root and proceed level by level.
864 node_info=cube_info->root;
865 index=MaxTreeDepth-1;
866 for (level=1; level < MaxTreeDepth; level++)
868 SetPixelInfo(image,p,&pixel);
869 id=ColorToNodeId(image,&pixel,index);
870 if (node_info->child[id] == (NodeInfo *) NULL)
872 node_info->child[id]=GetNodeInfo(cube_info,level);
873 if (node_info->child[id] == (NodeInfo *) NULL)
875 (void) ThrowMagickException(exception,GetMagickModule(),
876 ResourceLimitError,"MemoryAllocationFailed","`%s'",
881 node_info=node_info->child[id];
884 if (level < MaxTreeDepth)
886 for (i=0; i < (ssize_t) node_info->number_unique; i++)
888 SetPixelInfoPacket(image,&node_info->list[i],&target);
889 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
892 if (i < (ssize_t) node_info->number_unique)
893 node_info->list[i].count++;
897 Add this unique color to the color list.
899 if (node_info->number_unique == 0)
900 node_info->list=(PixelPacket *) AcquireMagickMemory(
901 sizeof(*node_info->list));
903 node_info->list=(PixelPacket *) ResizeQuantumMemory(node_info->list,
904 (size_t) (i+1),sizeof(*node_info->list));
905 if (node_info->list == (PixelPacket *) NULL)
907 (void) ThrowMagickException(exception,GetMagickModule(),
908 ResourceLimitError,"MemoryAllocationFailed","`%s'",
912 node_info->list[i].red=GetPixelRed(image,p);
913 node_info->list[i].green=GetPixelGreen(image,p);
914 node_info->list[i].blue=GetPixelBlue(image,p);
915 if (image->colorspace == CMYKColorspace)
916 node_info->list[i].black=GetPixelBlack(image,p);
917 node_info->list[i].alpha=GetPixelAlpha(image,p);
918 node_info->list[i].count=1;
919 node_info->number_unique++;
921 if (cube_info->colors > 256)
924 p+=GetPixelChannels(image);
926 if (x < (ssize_t) image->columns)
929 image_view=DestroyCacheView(image_view);
930 cube_info=DestroyCubeInfo(image,cube_info);
931 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 % M i n M a x S t r e t c h I m a g e %
943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945 % MinMaxStretchImage() uses the exact minimum and maximum values found in
946 % each of the channels given, as the BlackPoint and WhitePoint to linearly
947 % stretch the colors (and histogram) of the image. The stretch points are
948 % also moved further inward by the adjustment values given.
950 % If the adjustment values are both zero this function is equivalent to a
951 % perfect normalization (or autolevel) of the image.
953 % Each channel is stretched independantally of each other (producing color
954 % distortion) unless the special 'SyncChannels' flag is also provided in the
955 % channels setting. If this flag is present the minimum and maximum point
956 % will be extracted from all the given channels, and those channels will be
957 % stretched by exactly the same amount (preventing color distortion).
959 % In the special case that only ONE value is found in a channel of the image
960 % that value is not stretched, that value is left as is.
962 % The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
965 % The format of the MinMaxStretchImage method is:
967 % MagickBooleanType MinMaxStretchImage(Image *image,const double black,
968 % const double white)
970 % A description of each parameter follows:
972 % o image: The image to auto-level
974 % o black, white: move the black / white point inward from the minimum and
975 % maximum points by this color value.
978 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
979 const double black,const double white)
989 if (image->sync != MagickFalse)
992 Auto-level all channels equally.
994 (void) GetImageChannelRange(image,DefaultChannels,&min,&max,
998 if (fabs(min-max) >= MagickEpsilon)
999 status&=LevelImage(image,min,max,1.0);
1000 return(status != 0 ? MagickTrue : MagickFalse);
1003 Auto-level each channel separately.
1005 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1007 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1010 if (fabs(min-max) >= MagickEpsilon)
1012 PushPixelComponentMap(image,RedChannel);
1013 status&=LevelImage(image,min,max,1.0);
1014 PopPixelComponentMap(image);
1017 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1019 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1023 if (fabs(min-max) >= MagickEpsilon)
1025 PushPixelComponentMap(image,GreenChannel);
1026 status&=LevelImage(image,min,max,1.0);
1027 PopPixelComponentMap(image);
1030 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1032 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1036 if (fabs(min-max) >= MagickEpsilon)
1038 PushPixelComponentMap(image,BlueChannel);
1039 status&=LevelImage(image,min,max,1.0);
1040 PopPixelComponentMap(image);
1043 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1044 (image->colorspace == CMYKColorspace))
1046 (void) GetImageChannelRange(image,BlackChannel,&min,&max,
1050 if (fabs(min-max) >= MagickEpsilon)
1052 PushPixelComponentMap(image,BlackChannel);
1053 status&=LevelImage(image,min,max,1.0);
1054 PopPixelComponentMap(image);
1057 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1058 (image->matte == MagickTrue))
1060 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1064 if (fabs(min-max) >= MagickEpsilon)
1066 PushPixelComponentMap(image,AlphaChannel);
1067 status&=LevelImage(image,min,max,1.0);
1068 PopPixelComponentMap(image);
1071 return(status != 0 ? MagickTrue : MagickFalse);
1075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 % G e t N u m b e r C o l o r s %
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085 % GetNumberColors() returns the number of unique colors in an image.
1087 % The format of the GetNumberColors method is:
1089 % size_t GetNumberColors(const Image *image,FILE *file,
1090 % ExceptionInfo *exception)
1092 % A description of each parameter follows.
1094 % o image: the image.
1096 % o file: Write a histogram of the color distribution to this file handle.
1098 % o exception: return any errors or warnings in this structure.
1102 #if defined(__cplusplus) || defined(c_plusplus)
1106 static int HistogramCompare(const void *x,const void *y)
1112 color_1=(const PixelPacket *) x;
1113 color_2=(const PixelPacket *) y;
1114 if (color_2->red != color_1->red)
1115 return((int) color_1->red-(int) color_2->red);
1116 if (color_2->green != color_1->green)
1117 return((int) color_1->green-(int) color_2->green);
1118 if (color_2->blue != color_1->blue)
1119 return((int) color_1->blue-(int) color_2->blue);
1120 return((int) color_2->count-(int) color_1->count);
1123 #if defined(__cplusplus) || defined(c_plusplus)
1127 MagickExport size_t GetNumberColors(const Image *image,FILE *file,
1128 ExceptionInfo *exception)
1130 #define HistogramImageTag "Histogram/Image"
1133 color[MaxTextExtent],
1135 tuple[MaxTextExtent];
1146 register PixelPacket
1156 if (file == (FILE *) NULL)
1161 cube_info=ClassifyImageColors(image,exception);
1162 if (cube_info != (CubeInfo *) NULL)
1163 number_colors=cube_info->colors;
1164 cube_info=DestroyCubeInfo(image,cube_info);
1165 return(number_colors);
1167 histogram=GetImageHistogram(image,&number_colors,exception);
1168 if (histogram == (PixelPacket *) NULL)
1169 return(number_colors);
1170 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1172 GetPixelInfo(image,&pixel);
1175 for (i=0; i < (ssize_t) number_colors; i++)
1177 SetPixelInfoPacket(image,p,&pixel);
1178 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1179 ConcatenateColorComponent(&pixel,RedPixelComponent,X11Compliance,tuple);
1180 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1181 ConcatenateColorComponent(&pixel,GreenPixelComponent,X11Compliance,tuple);
1182 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1183 ConcatenateColorComponent(&pixel,BluePixelComponent,X11Compliance,tuple);
1184 if (pixel.colorspace == CMYKColorspace)
1186 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1187 ConcatenateColorComponent(&pixel,BlackPixelComponent,X11Compliance,
1190 if (pixel.matte != MagickFalse)
1192 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1193 ConcatenateColorComponent(&pixel,AlphaPixelComponent,X11Compliance,
1196 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1197 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1198 GetColorTuple(&pixel,MagickTrue,hex);
1199 (void) FormatLocaleFile(file,"%10" MagickSizeFormat,p->count);
1200 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color);
1201 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1206 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1208 if (proceed == MagickFalse)
1213 (void) fflush(file);
1214 histogram=(PixelPacket *) RelinquishMagickMemory(histogram);
1215 if (status == MagickFalse)
1217 return(number_colors);
1221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225 % U n i q u e I m a g e C o l o r s %
1229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231 % UniqueImageColors() returns the unique colors of an image.
1233 % The format of the UniqueImageColors method is:
1235 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1237 % A description of each parameter follows.
1239 % o image: the image.
1241 % o exception: return any errors or warnings in this structure.
1245 static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1246 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
1248 #define UniqueColorsImageTag "UniqueColors/Image"
1260 Traverse any children.
1262 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL;
1263 for (i=0; i < (ssize_t) number_children; i++)
1264 if (node_info->child[i] != (NodeInfo *) NULL)
1265 UniqueColorsToImage(unique_image,unique_view,cube_info,
1266 node_info->child[i],exception);
1267 if (node_info->level == (MaxTreeDepth-1))
1269 register PixelPacket
1277 for (i=0; i < (ssize_t) node_info->number_unique; i++)
1279 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1281 if (q == (const Quantum *) NULL)
1283 SetPixelRed(unique_image,p->red,q);
1284 SetPixelGreen(unique_image,p->green,q);
1285 SetPixelBlue(unique_image,p->blue,q);
1286 SetPixelAlpha(unique_image,p->alpha,q);
1287 if (unique_image->colorspace == CMYKColorspace)
1288 SetPixelBlack(unique_image,p->black,q);
1289 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
1294 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
1299 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
1300 cube_info->progress,cube_info->colors);
1301 if (proceed == MagickFalse)
1304 cube_info->progress++;
1305 if (status == MagickFalse)
1310 MagickExport Image *UniqueImageColors(const Image *image,
1311 ExceptionInfo *exception)
1322 cube_info=ClassifyImageColors(image,exception);
1323 if (cube_info == (CubeInfo *) NULL)
1324 return((Image *) NULL);
1325 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1326 if (unique_image == (Image *) NULL)
1327 return(unique_image);
1328 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1330 InheritException(exception,&unique_image->exception);
1331 unique_image=DestroyImage(unique_image);
1332 return((Image *) NULL);
1334 unique_view=AcquireCacheView(unique_image);
1335 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1337 unique_view=DestroyCacheView(unique_view);
1338 if (cube_info->colors < MaxColormapSize)
1343 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1344 quantize_info->number_colors=MaxColormapSize;
1345 quantize_info->dither=MagickFalse;
1346 quantize_info->tree_depth=8;
1347 (void) QuantizeImage(quantize_info,unique_image);
1348 quantize_info=DestroyQuantizeInfo(quantize_info);
1350 cube_info=DestroyCubeInfo(image,cube_info);
1351 return(unique_image);