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-2015 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->alpha_trait != UndefinedPixelTrait)
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 == MagickCoreSignature);
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=AcquireVirtualCacheView(image,exception);
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 GetPixelInfoPixel(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 target=node_info->list[i];
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=(PixelInfo *) AcquireMagickMemory(
270 sizeof(*node_info->list));
272 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
273 (size_t) (i+1),sizeof(*node_info->list));
274 if (node_info->list == (PixelInfo *) NULL)
276 (void) ThrowMagickException(exception,GetMagickModule(),
277 ResourceLimitError,"MemoryAllocationFailed","`%s'",
281 node_info->list[i]=pixel;
282 node_info->list[i].red=(double) GetPixelRed(image,p);
283 node_info->list[i].green=(double) GetPixelGreen(image,p);
284 node_info->list[i].blue=(double) GetPixelBlue(image,p);
285 if (image->colorspace == CMYKColorspace)
286 node_info->list[i].black=(double) GetPixelBlack(image,p);
287 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
288 node_info->list[i].count=1;
289 node_info->number_unique++;
292 p+=GetPixelChannels(image);
294 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
296 if (proceed == MagickFalse)
299 image_view=DestroyCacheView(image_view);
304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308 + D e f i n e I m a g e H i s t o g r a m %
312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314 % DefineImageHistogram() traverses the color cube tree and notes each colormap
315 % entry. A colormap entry is any node in the color cube tree where the
316 % of unique colors is not zero.
318 % The format of the DefineImageHistogram method is:
320 % DefineImageHistogram(const Image *image,NodeInfo *node_info,
321 % PixelInfo **unique_colors)
323 % A description of each parameter follows.
325 % o image: the image.
327 % o node_info: the address of a structure of type NodeInfo which points to a
328 % node in the color cube tree that is to be pruned.
330 % o histogram: the image histogram.
333 static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
334 PixelInfo **histogram)
343 Traverse any children.
345 number_children=image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
346 for (i=0; i < (ssize_t) number_children; i++)
347 if (node_info->child[i] != (NodeInfo *) NULL)
348 DefineImageHistogram(image,node_info->child[i],histogram);
349 if (node_info->level == (MaxTreeDepth-1))
355 for (i=0; i < (ssize_t) node_info->number_unique; i++)
365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369 + D e s t r o y C u b e I n f o %
373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 % DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
377 % The format of the DestroyCubeInfo method is:
379 % DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
381 % A description of each parameter follows:
383 % o image: the image.
385 % o cube_info: the address of a structure of type CubeInfo.
388 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
394 Release color cube tree storage.
396 DestroyColorCube(image,cube_info->root);
399 nodes=cube_info->node_queue->next;
400 cube_info->node_queue=(Nodes *)
401 RelinquishMagickMemory(cube_info->node_queue);
402 cube_info->node_queue=nodes;
403 } while (cube_info->node_queue != (Nodes *) NULL);
404 return((CubeInfo *) RelinquishMagickMemory(cube_info));
408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 + D e s t r o y C o l o r C u b e %
416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 % DestroyColorCube() traverses the color cube tree and frees the list of
421 % The format of the DestroyColorCube method is:
423 % void DestroyColorCube(const Image *image,const NodeInfo *node_info)
425 % A description of each parameter follows.
427 % o image: the image.
429 % o node_info: the address of a structure of type NodeInfo which points to a
430 % node in the color cube tree that is to be pruned.
433 static void DestroyColorCube(const Image *image,NodeInfo *node_info)
442 Traverse any children.
444 number_children=image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
445 for (i=0; i < (ssize_t) number_children; i++)
446 if (node_info->child[i] != (NodeInfo *) NULL)
447 DestroyColorCube(image,node_info->child[i]);
448 if (node_info->list != (PixelInfo *) NULL)
449 node_info->list=(PixelInfo *) RelinquishMagickMemory(node_info->list);
453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457 + G e t C u b e I n f o %
461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 % GetCubeInfo() initializes the CubeInfo data structure.
465 % The format of the GetCubeInfo method is:
467 % cube_info=GetCubeInfo()
469 % A description of each parameter follows.
471 % o cube_info: A pointer to the Cube structure.
474 static CubeInfo *GetCubeInfo(void)
480 Initialize tree to describe color cube.
482 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
483 if (cube_info == (CubeInfo *) NULL)
484 return((CubeInfo *) NULL);
485 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
487 Initialize root node.
489 cube_info->root=GetNodeInfo(cube_info,0);
490 if (cube_info->root == (NodeInfo *) NULL)
491 return((CubeInfo *) NULL);
496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 % G e t I m a g e H i s t o g r a m %
504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 % GetImageHistogram() returns the unique colors in an image.
508 % The format of the GetImageHistogram method is:
510 % size_t GetImageHistogram(const Image *image,
511 % size_t *number_colors,ExceptionInfo *exception)
513 % A description of each parameter follows.
515 % o image: the image.
517 % o file: Write a histogram of the color distribution to this file handle.
519 % o exception: return any errors or warnings in this structure.
522 MagickExport PixelInfo *GetImageHistogram(const Image *image,
523 size_t *number_colors,ExceptionInfo *exception)
532 histogram=(PixelInfo *) NULL;
533 cube_info=ClassifyImageColors(image,exception);
534 if (cube_info != (CubeInfo *) NULL)
536 histogram=(PixelInfo *) AcquireQuantumMemory((size_t) cube_info->colors,
538 if (histogram == (PixelInfo *) NULL)
539 (void) ThrowMagickException(exception,GetMagickModule(),
540 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
546 *number_colors=cube_info->colors;
548 DefineImageHistogram(image,cube_info->root,&root);
551 cube_info=DestroyCubeInfo(image,cube_info);
556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560 + G e t N o d e I n f o %
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566 % GetNodeInfo() allocates memory for a new node in the color cube tree and
567 % presets all fields to zero.
569 % The format of the GetNodeInfo method is:
571 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
573 % A description of each parameter follows.
575 % o cube_info: A pointer to the CubeInfo structure.
577 % o level: Specifies the level in the storage_class the node resides.
580 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
585 if (cube_info->free_nodes == 0)
591 Allocate a new nodes of nodes.
593 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
594 if (nodes == (Nodes *) NULL)
595 return((NodeInfo *) NULL);
596 nodes->next=cube_info->node_queue;
597 cube_info->node_queue=nodes;
598 cube_info->node_info=nodes->nodes;
599 cube_info->free_nodes=NodesInAList;
601 cube_info->free_nodes--;
602 node_info=cube_info->node_info++;
603 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
604 node_info->level=level;
609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
613 % I d e n t i f y P a l e t t e I m a g e %
617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619 % IdentifyPaletteImage() returns MagickTrue if the image has 256 unique colors
622 % The format of the IdentifyPaletteImage method is:
624 % MagickBooleanType IdentifyPaletteImage(const Image *image,
625 % ExceptionInfo *exception)
627 % A description of each parameter follows.
629 % o image: the image.
631 % o exception: return any errors or warnings in this structure.
635 static MagickBooleanType CheckImageColors(const Image *image,
636 ExceptionInfo *exception,size_t max_colors)
648 register const Quantum
668 if (image->storage_class == PseudoClass)
669 return((image->colors <= max_colors) ? MagickTrue : MagickFalse);
671 Initialize color description tree.
673 cube_info=GetCubeInfo();
674 if (cube_info == (CubeInfo *) NULL)
676 (void) ThrowMagickException(exception,GetMagickModule(),
677 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
680 GetPixelInfo(image,&pixel);
681 GetPixelInfo(image,&target);
682 image_view=AcquireVirtualCacheView(image,exception);
683 for (y=0; y < (ssize_t) image->rows; y++)
685 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
686 if (p == (const Quantum *) NULL)
688 for (x=0; x < (ssize_t) image->columns; x++)
691 Start at the root and proceed level by level.
693 node_info=cube_info->root;
694 index=MaxTreeDepth-1;
695 for (level=1; level < MaxTreeDepth; level++)
697 GetPixelInfoPixel(image,p,&pixel);
698 id=ColorToNodeId(image,&pixel,index);
699 if (node_info->child[id] == (NodeInfo *) NULL)
701 node_info->child[id]=GetNodeInfo(cube_info,level);
702 if (node_info->child[id] == (NodeInfo *) NULL)
704 (void) ThrowMagickException(exception,GetMagickModule(),
705 ResourceLimitError,"MemoryAllocationFailed","`%s'",
710 node_info=node_info->child[id];
713 if (level < MaxTreeDepth)
715 for (i=0; i < (ssize_t) node_info->number_unique; i++)
717 target=node_info->list[i];
718 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
721 if (i < (ssize_t) node_info->number_unique)
722 node_info->list[i].count++;
726 Add this unique color to the color list.
728 if (node_info->number_unique == 0)
729 node_info->list=(PixelInfo *) AcquireMagickMemory(
730 sizeof(*node_info->list));
732 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
733 (size_t) (i+1),sizeof(*node_info->list));
734 if (node_info->list == (PixelInfo *) NULL)
736 (void) ThrowMagickException(exception,GetMagickModule(),
737 ResourceLimitError,"MemoryAllocationFailed","`%s'",
741 node_info->list[i].red=(double) GetPixelRed(image,p);
742 node_info->list[i].green=(double) GetPixelGreen(image,p);
743 node_info->list[i].blue=(double) GetPixelBlue(image,p);
744 if (image->colorspace == CMYKColorspace)
745 node_info->list[i].black=(double) GetPixelBlack(image,p);
746 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
747 node_info->list[i].count=1;
748 node_info->number_unique++;
750 if (cube_info->colors > max_colors)
753 p+=GetPixelChannels(image);
755 if (x < (ssize_t) image->columns)
758 image_view=DestroyCacheView(image_view);
759 cube_info=DestroyCubeInfo(image,cube_info);
760 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
763 MagickExport MagickBooleanType IdentifyPaletteImage(const Image *image,
764 ExceptionInfo *exception)
766 assert(image != (Image *) NULL);
767 assert(image->signature == MagickCoreSignature);
768 if (image->debug != MagickFalse)
769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
770 return(CheckImageColors(image,exception,256));
774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778 % I s H i s t o g r a m I m a g e %
782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784 % IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
787 % The format of the IsHistogramImage method is:
789 % MagickBooleanType IsHistogramImage(const Image *image,
790 % ExceptionInfo *exception)
792 % A description of each parameter follows.
794 % o image: the image.
796 % o exception: return any errors or warnings in this structure.
799 MagickExport MagickBooleanType IsHistogramImage(const Image *image,
800 ExceptionInfo *exception)
802 #define MaximumUniqueColors 1024
804 assert(image != (Image *) NULL);
805 assert(image->signature == MagickCoreSignature);
806 if (image->debug != MagickFalse)
807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
808 return(CheckImageColors(image,exception,MaximumUniqueColors));
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816 % I s P a l e t t e I m a g e %
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 % IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
823 % unique colors or less.
825 % The format of the IsPaletteImage method is:
827 % MagickBooleanType IsPaletteImage(const Image *image)
829 % A description of each parameter follows.
831 % o image: the image.
834 MagickExport MagickBooleanType IsPaletteImage(const Image *image)
836 assert(image != (Image *) NULL);
837 assert(image->signature == MagickCoreSignature);
838 if (image->debug != MagickFalse)
839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
840 if (image->storage_class != PseudoClass)
842 return((image->colors <= 256) ? MagickTrue : MagickFalse);
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 % M i n M a x S t r e t c h I m a g e %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 % MinMaxStretchImage() uses the exact minimum and maximum values found in
857 % each of the channels given, as the BlackPoint and WhitePoint to linearly
858 % stretch the colors (and histogram) of the image. The stretch points are
859 % also moved further inward by the adjustment values given.
861 % If the adjustment values are both zero this function is equivalent to a
862 % perfect normalization (or autolevel) of the image.
864 % Each channel is stretched independantally of each other (producing color
865 % distortion) unless the special 'SyncChannels' flag is also provided in the
866 % channels setting. If this flag is present the minimum and maximum point
867 % will be extracted from all the given channels, and those channels will be
868 % stretched by exactly the same amount (preventing color distortion).
870 % In the special case that only ONE value is found in a channel of the image
871 % that value is not stretched, that value is left as is.
873 % The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
876 % The format of the MinMaxStretchImage method is:
878 % MagickBooleanType MinMaxStretchImage(Image *image,const double black,
879 % const double white,const double gamma,ExceptionInfo *exception)
881 % A description of each parameter follows:
883 % o image: The image to auto-level
885 % o black, white: move the black / white point inward from the minimum and
886 % maximum points by this color value.
888 % o gamma: the gamma.
890 % o exception: return any errors or warnings in this structure.
893 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
894 const double black,const double white,const double gamma,
895 ExceptionInfo *exception)
908 Auto-level each channel.
911 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
916 PixelChannel channel=GetPixelChannelChannel(image,i);
917 PixelTrait traits=GetPixelChannelTraits(image,channel);
918 if ((traits & UpdatePixelTrait) == 0)
920 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
921 status&=GetImageRange(image,&min,&max,exception);
924 if (fabs(min-max) >= MagickEpsilon)
925 status&=LevelImage(image,min,max,gamma,exception);
926 (void) SetImageChannelMask(image,channel_mask);
928 return(status != 0 ? MagickTrue : MagickFalse);
932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
936 % G e t N u m b e r C o l o r s %
940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
942 % GetNumberColors() returns the number of unique colors in an image.
944 % The format of the GetNumberColors method is:
946 % size_t GetNumberColors(const Image *image,FILE *file,
947 % ExceptionInfo *exception)
949 % A description of each parameter follows.
951 % o image: the image.
953 % o file: Write a histogram of the color distribution to this file handle.
955 % o exception: return any errors or warnings in this structure.
959 #if defined(__cplusplus) || defined(c_plusplus)
963 static int HistogramCompare(const void *x,const void *y)
969 color_1=(const PixelInfo *) x;
970 color_2=(const PixelInfo *) y;
971 if (color_2->red != color_1->red)
972 return((int) color_1->red-(int) color_2->red);
973 if (color_2->green != color_1->green)
974 return((int) color_1->green-(int) color_2->green);
975 if (color_2->blue != color_1->blue)
976 return((int) color_1->blue-(int) color_2->blue);
977 return((int) color_2->count-(int) color_1->count);
980 #if defined(__cplusplus) || defined(c_plusplus)
984 MagickExport size_t GetNumberColors(const Image *image,FILE *file,
985 ExceptionInfo *exception)
987 #define HistogramImageTag "Histogram/Image"
990 color[MagickPathExtent],
991 hex[MagickPathExtent],
992 tuple[MagickPathExtent];
1013 if (file == (FILE *) NULL)
1018 cube_info=ClassifyImageColors(image,exception);
1019 if (cube_info != (CubeInfo *) NULL)
1020 number_colors=cube_info->colors;
1021 cube_info=DestroyCubeInfo(image,cube_info);
1022 return(number_colors);
1024 histogram=GetImageHistogram(image,&number_colors,exception);
1025 if (histogram == (PixelInfo *) NULL)
1026 return(number_colors);
1027 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1029 GetPixelInfo(image,&pixel);
1032 for (i=0; i < (ssize_t) number_colors; i++)
1035 (void) CopyMagickString(tuple,"(",MagickPathExtent);
1036 ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,tuple);
1037 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
1038 ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,tuple);
1039 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
1040 ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,tuple);
1041 if (pixel.colorspace == CMYKColorspace)
1043 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
1044 ConcatenateColorComponent(&pixel,BlackPixelChannel,X11Compliance,
1047 if (pixel.alpha_trait != UndefinedPixelTrait)
1049 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
1050 ConcatenateColorComponent(&pixel,AlphaPixelChannel,X11Compliance,
1053 (void) ConcatenateMagickString(tuple,")",MagickPathExtent);
1054 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception);
1055 GetColorTuple(&pixel,MagickTrue,hex);
1056 (void) FormatLocaleFile(file,"%10.20g",(double) ((MagickOffsetType)
1058 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color);
1059 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1064 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1066 if (proceed == MagickFalse)
1071 (void) fflush(file);
1072 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1073 if (status == MagickFalse)
1075 return(number_colors);
1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083 % U n i q u e I m a g e C o l o r s %
1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089 % UniqueImageColors() returns the unique colors of an image.
1091 % The format of the UniqueImageColors method is:
1093 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1095 % A description of each parameter follows.
1097 % o image: the image.
1099 % o exception: return any errors or warnings in this structure.
1103 static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1104 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
1106 #define UniqueColorsImageTag "UniqueColors/Image"
1118 Traverse any children.
1120 number_children=unique_image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
1121 for (i=0; i < (ssize_t) number_children; i++)
1122 if (node_info->child[i] != (NodeInfo *) NULL)
1123 UniqueColorsToImage(unique_image,unique_view,cube_info,
1124 node_info->child[i],exception);
1125 if (node_info->level == (MaxTreeDepth-1))
1135 for (i=0; i < (ssize_t) node_info->number_unique; i++)
1137 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1139 if (q == (Quantum *) NULL)
1141 SetPixelRed(unique_image,ClampToQuantum(p->red),q);
1142 SetPixelGreen(unique_image,ClampToQuantum(p->green),q);
1143 SetPixelBlue(unique_image,ClampToQuantum(p->blue),q);
1144 SetPixelAlpha(unique_image,ClampToQuantum(p->alpha),q);
1145 if (unique_image->colorspace == CMYKColorspace)
1146 SetPixelBlack(unique_image,ClampToQuantum(p->black),q);
1147 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
1152 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
1157 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
1158 cube_info->progress,cube_info->colors);
1159 if (proceed == MagickFalse)
1162 cube_info->progress++;
1163 if (status == MagickFalse)
1168 MagickExport Image *UniqueImageColors(const Image *image,
1169 ExceptionInfo *exception)
1180 cube_info=ClassifyImageColors(image,exception);
1181 if (cube_info == (CubeInfo *) NULL)
1182 return((Image *) NULL);
1183 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1184 if (unique_image == (Image *) NULL)
1185 return(unique_image);
1186 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse)
1188 unique_image=DestroyImage(unique_image);
1189 return((Image *) NULL);
1191 unique_view=AcquireAuthenticCacheView(unique_image,exception);
1192 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1194 unique_view=DestroyCacheView(unique_view);
1195 cube_info=DestroyCubeInfo(image,cube_info);
1196 return(unique_image);