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-2010 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 "magick/studio.h"
44 #include "magick/cache-view.h"
45 #include "magick/color-private.h"
46 #include "magick/enhance.h"
47 #include "magick/exception.h"
48 #include "magick/exception-private.h"
49 #include "magick/hashmap.h"
50 #include "magick/histogram.h"
51 #include "magick/image.h"
52 #include "magick/list.h"
53 #include "magick/memory_.h"
54 #include "magick/monitor-private.h"
55 #include "magick/pixel-private.h"
56 #include "magick/prepress.h"
57 #include "magick/quantize.h"
58 #include "magick/registry.h"
59 #include "magick/semaphore.h"
60 #include "magick/splay-tree.h"
61 #include "magick/statistic.h"
62 #include "magick/string_.h"
67 #define MaxTreeDepth 8
68 #define NodesInAList 1536
73 typedef struct _NodeInfo
97 typedef struct _CubeInfo
118 Forward declarations.
124 *GetNodeInfo(CubeInfo *,const unsigned long);
127 DestroyColorCube(const Image *,NodeInfo *);
130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 + C l a s s i f y I m a g e C o l o r s %
138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 % ClassifyImageColors() builds a populated CubeInfo tree for the specified
141 % image. The returned tree should be deallocated using DestroyCubeInfo()
142 % once it is no longer needed.
144 % The format of the ClassifyImageColors() method is:
146 % CubeInfo *ClassifyImageColors(const Image *image,
147 % ExceptionInfo *exception)
149 % A description of each parameter follows.
151 % o image: the image.
153 % o exception: return any errors or warnings in this structure.
157 static inline unsigned long ColorToNodeId(const Image *image,
158 const MagickPixelPacket *pixel,unsigned long index)
164 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
165 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
166 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
167 if (image->matte != MagickFalse)
168 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->opacity)) >> index) &
173 static CubeInfo *ClassifyImageColors(const Image *image,
174 ExceptionInfo *exception)
176 #define EvaluateImageTag " Compute image colors... "
197 register const IndexPacket
200 register const PixelPacket
207 register unsigned long
213 Initialize color description tree.
215 assert(image != (const Image *) NULL);
216 assert(image->signature == MagickSignature);
217 if (image->debug != MagickFalse)
218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
219 cube_info=GetCubeInfo();
220 if (cube_info == (CubeInfo *) NULL)
222 (void) ThrowMagickException(exception,GetMagickModule(),
223 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
226 GetMagickPixelPacket(image,&pixel);
227 GetMagickPixelPacket(image,&target);
228 image_view=AcquireCacheView(image);
229 for (y=0; y < (long) image->rows; y++)
231 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
232 if (p == (const PixelPacket *) NULL)
234 indexes=GetCacheViewVirtualIndexQueue(image_view);
235 for (x=0; x < (long) image->columns; x++)
238 Start at the root and proceed level by level.
240 node_info=cube_info->root;
241 index=MaxTreeDepth-1;
242 for (level=1; level < MaxTreeDepth; level++)
244 SetMagickPixelPacket(image,p,indexes+x,&pixel);
245 id=ColorToNodeId(image,&pixel,index);
246 if (node_info->child[id] == (NodeInfo *) NULL)
248 node_info->child[id]=GetNodeInfo(cube_info,level);
249 if (node_info->child[id] == (NodeInfo *) NULL)
251 (void) ThrowMagickException(exception,GetMagickModule(),
252 ResourceLimitError,"MemoryAllocationFailed","`%s'",
257 node_info=node_info->child[id];
260 for (i=0; i < (long) node_info->number_unique; i++)
262 SetMagickPixelPacket(image,&node_info->list[i].pixel,
263 &node_info->list[i].index,&target);
264 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
267 if (i < (long) node_info->number_unique)
268 node_info->list[i].count++;
271 if (node_info->number_unique == 0)
272 node_info->list=(ColorPacket *) AcquireMagickMemory(
273 sizeof(*node_info->list));
275 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
276 (size_t) (i+1),sizeof(*node_info->list));
277 if (node_info->list == (ColorPacket *) NULL)
279 (void) ThrowMagickException(exception,GetMagickModule(),
280 ResourceLimitError,"MemoryAllocationFailed","`%s'",
284 node_info->list[i].pixel=(*p);
285 if ((image->colorspace == CMYKColorspace) ||
286 (image->storage_class == PseudoClass))
287 node_info->list[i].index=indexes[x];
288 node_info->list[i].count=1;
289 node_info->number_unique++;
294 proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
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 % ColorPacket **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 ColorPacket **histogram)
342 Traverse any children.
344 number_children=image->matte == MagickFalse ? 8UL : 16UL;
345 for (i=0; i < (long) 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 < (long) node_info->number_unique; i++)
356 (*histogram)->pixel=p->pixel;
357 (*histogram)->index=p->index;
358 (*histogram)->count=p->count;
366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 + D e s t r o y C u b e I n f o %
374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
376 % DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
378 % The format of the DestroyCubeInfo method is:
380 % DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
382 % A description of each parameter follows:
384 % o image: the image.
386 % o cube_info: the address of a structure of type CubeInfo.
389 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
395 Release color cube tree storage.
397 DestroyColorCube(image,cube_info->root);
400 nodes=cube_info->node_queue->next;
401 cube_info->node_queue=(Nodes *)
402 RelinquishMagickMemory(cube_info->node_queue);
403 cube_info->node_queue=nodes;
404 } while (cube_info->node_queue != (Nodes *) NULL);
405 return((CubeInfo *) RelinquishMagickMemory(cube_info));
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 + D e s t r o y C o l o r C u b e %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 % DestroyColorCube() traverses the color cube tree and frees the list of
422 % The format of the DestroyColorCube method is:
424 % void DestroyColorCube(const Image *image,const NodeInfo *node_info)
426 % A description of each parameter follows.
428 % o image: the image.
430 % o node_info: the address of a structure of type NodeInfo which points to a
431 % node in the color cube tree that is to be pruned.
434 static void DestroyColorCube(const Image *image,NodeInfo *node_info)
443 Traverse any children.
445 number_children=image->matte == MagickFalse ? 8UL : 16UL;
446 for (i=0; i < (long) number_children; i++)
447 if (node_info->child[i] != (NodeInfo *) NULL)
448 DestroyColorCube(image,node_info->child[i]);
449 if (node_info->list != (ColorPacket *) NULL)
450 node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458 + G e t C u b e I n f o %
462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464 % GetCubeInfo() initializes the CubeInfo data structure.
466 % The format of the GetCubeInfo method is:
468 % cube_info=GetCubeInfo()
470 % A description of each parameter follows.
472 % o cube_info: A pointer to the Cube structure.
475 static CubeInfo *GetCubeInfo(void)
481 Initialize tree to describe color cube.
483 cube_info=(CubeInfo *) AcquireAlignedMemory(1,sizeof(*cube_info));
484 if (cube_info == (CubeInfo *) NULL)
485 return((CubeInfo *) NULL);
486 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
488 Initialize root node.
490 cube_info->root=GetNodeInfo(cube_info,0);
491 if (cube_info->root == (NodeInfo *) NULL)
492 return((CubeInfo *) NULL);
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 % G e t I m a g e H i s t o g r a m %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507 % GetImageHistogram() returns the unique colors in an image.
509 % The format of the GetImageHistogram method is:
511 % unsigned long GetImageHistogram(const Image *image,
512 % unsigned long *number_colors,ExceptionInfo *exception)
514 % A description of each parameter follows.
516 % o image: the image.
518 % o file: Write a histogram of the color distribution to this file handle.
520 % o exception: return any errors or warnings in this structure.
523 MagickExport ColorPacket *GetImageHistogram(const Image *image,
524 unsigned long *number_colors,ExceptionInfo *exception)
533 histogram=(ColorPacket *) NULL;
534 cube_info=ClassifyImageColors(image,exception);
535 if (cube_info != (CubeInfo *) NULL)
537 histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
539 if (histogram == (ColorPacket *) NULL)
540 (void) ThrowMagickException(exception,GetMagickModule(),
541 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
547 *number_colors=cube_info->colors;
549 DefineImageHistogram(image,cube_info->root,&root);
552 cube_info=DestroyCubeInfo(image,cube_info);
557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561 + G e t N o d e I n f o %
565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 % GetNodeInfo() allocates memory for a new node in the color cube tree and
568 % presets all fields to zero.
570 % The format of the GetNodeInfo method is:
572 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
574 % A description of each parameter follows.
576 % o cube_info: A pointer to the CubeInfo structure.
578 % o level: Specifies the level in the storage_class the node resides.
581 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
586 if (cube_info->free_nodes == 0)
592 Allocate a new nodes of nodes.
594 nodes=(Nodes *) AcquireAlignedMemory(1,sizeof(*nodes));
595 if (nodes == (Nodes *) NULL)
596 return((NodeInfo *) NULL);
597 nodes->next=cube_info->node_queue;
598 cube_info->node_queue=nodes;
599 cube_info->node_info=nodes->nodes;
600 cube_info->free_nodes=NodesInAList;
602 cube_info->free_nodes--;
603 node_info=cube_info->node_info++;
604 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
605 node_info->level=level;
610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
614 % I s H i s t o g r a m I m a g e %
618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 % IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
623 % The format of the IsHistogramImage method is:
625 % MagickBooleanType IsHistogramImage(const Image *image,
626 % ExceptionInfo *exception)
628 % A description of each parameter follows.
630 % o image: the image.
632 % o exception: return any errors or warnings in this structure.
635 MagickExport MagickBooleanType IsHistogramImage(const Image *image,
636 ExceptionInfo *exception)
638 #define MaximumUniqueColors 1024
653 register const IndexPacket
656 register const PixelPacket
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 GetMagickPixelPacket(image,&pixel);
692 GetMagickPixelPacket(image,&target);
693 image_view=AcquireCacheView(image);
694 for (y=0; y < (long) image->rows; y++)
696 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
697 if (p == (const PixelPacket *) NULL)
699 indexes=GetCacheViewVirtualIndexQueue(image_view);
700 for (x=0; x < (long) image->columns; x++)
703 Start at the root and proceed level by level.
705 node_info=cube_info->root;
706 index=MaxTreeDepth-1;
707 for (level=1; level < MaxTreeDepth; level++)
709 SetMagickPixelPacket(image,p,indexes+x,&pixel);
710 id=ColorToNodeId(image,&pixel,index);
711 if (node_info->child[id] == (NodeInfo *) NULL)
713 node_info->child[id]=GetNodeInfo(cube_info,level);
714 if (node_info->child[id] == (NodeInfo *) NULL)
716 (void) ThrowMagickException(exception,GetMagickModule(),
717 ResourceLimitError,"MemoryAllocationFailed","`%s'",
722 node_info=node_info->child[id];
725 if (level < MaxTreeDepth)
727 for (i=0; i < (long) node_info->number_unique; i++)
729 SetMagickPixelPacket(image,&node_info->list[i].pixel,
730 &node_info->list[i].index,&target);
731 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
734 if (i < (long) node_info->number_unique)
735 node_info->list[i].count++;
739 Add this unique color to the color list.
741 if (node_info->number_unique == 0)
742 node_info->list=(ColorPacket *) AcquireMagickMemory(
743 sizeof(*node_info->list));
745 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
746 (size_t) (i+1),sizeof(*node_info->list));
747 if (node_info->list == (ColorPacket *) NULL)
749 (void) ThrowMagickException(exception,GetMagickModule(),
750 ResourceLimitError,"MemoryAllocationFailed","`%s'",
754 node_info->list[i].pixel=(*p);
755 if ((image->colorspace == CMYKColorspace) ||
756 (image->storage_class == PseudoClass))
757 node_info->list[i].index=indexes[x];
758 node_info->list[i].count=1;
759 node_info->number_unique++;
761 if (cube_info->colors > MaximumUniqueColors)
766 if (x < (long) image->columns)
769 image_view=DestroyCacheView(image_view);
770 cube_info=DestroyCubeInfo(image,cube_info);
771 return(y < (long) 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)
816 register const IndexPacket
819 register const PixelPacket
836 assert(image != (Image *) NULL);
837 assert(image->signature == MagickSignature);
838 if (image->debug != MagickFalse)
839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
840 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
842 if (image->storage_class == PseudoClass)
845 Initialize color description tree.
847 cube_info=GetCubeInfo();
848 if (cube_info == (CubeInfo *) NULL)
850 (void) ThrowMagickException(exception,GetMagickModule(),
851 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
854 GetMagickPixelPacket(image,&pixel);
855 GetMagickPixelPacket(image,&target);
856 image_view=AcquireCacheView(image);
857 for (y=0; y < (long) image->rows; y++)
859 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
860 if (p == (const PixelPacket *) NULL)
862 indexes=GetCacheViewVirtualIndexQueue(image_view);
863 for (x=0; x < (long) image->columns; x++)
866 Start at the root and proceed level by level.
868 node_info=cube_info->root;
869 index=MaxTreeDepth-1;
870 for (level=1; level < MaxTreeDepth; level++)
872 SetMagickPixelPacket(image,p,indexes+x,&pixel);
873 id=ColorToNodeId(image,&pixel,index);
874 if (node_info->child[id] == (NodeInfo *) NULL)
876 node_info->child[id]=GetNodeInfo(cube_info,level);
877 if (node_info->child[id] == (NodeInfo *) NULL)
879 (void) ThrowMagickException(exception,GetMagickModule(),
880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
885 node_info=node_info->child[id];
888 if (level < MaxTreeDepth)
890 for (i=0; i < (long) node_info->number_unique; i++)
892 SetMagickPixelPacket(image,&node_info->list[i].pixel,
893 &node_info->list[i].index,&target);
894 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
897 if (i < (long) node_info->number_unique)
898 node_info->list[i].count++;
902 Add this unique color to the color list.
904 if (node_info->number_unique == 0)
905 node_info->list=(ColorPacket *) AcquireMagickMemory(
906 sizeof(*node_info->list));
908 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
909 (size_t) (i+1),sizeof(*node_info->list));
910 if (node_info->list == (ColorPacket *) NULL)
912 (void) ThrowMagickException(exception,GetMagickModule(),
913 ResourceLimitError,"MemoryAllocationFailed","`%s'",
917 node_info->list[i].pixel=(*p);
918 if ((image->colorspace == CMYKColorspace) ||
919 (image->storage_class == PseudoClass))
920 node_info->list[i].index=indexes[x];
921 node_info->list[i].count=1;
922 node_info->number_unique++;
924 if (cube_info->colors > 256)
929 if (x < (long) image->columns)
932 image_view=DestroyCacheView(image_view);
933 cube_info=DestroyCubeInfo(image,cube_info);
934 return(y < (long) image->rows ? MagickFalse : MagickTrue);
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
942 % M i n M a x S t r e t c h I m a g e %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948 % MinMaxStretchImage() uses the exact minimum and maximum values found in
949 % each of the channels given, as the BlackPoint and WhitePoint to linearly
950 % stretch the colors (and histogram) of the image. The stretch points are
951 % also moved further inward by the adjustment values given.
953 % If the adjustment values are both zero this function is equivelent to a
954 % perfect normalization (or autolevel) of the image.
956 % Each channel is stretched independantally of each other (producing color
957 % distortion) unless the special 'SyncChannels' flag is also provided in the
958 % channels setting. If this flag is present the minimum and maximum point
959 % will be extracted from all the given channels, and those channels will be
960 % stretched by exactly the same amount (preventing color distortion).
962 % In the special case that only ONE value is found in a channel of the image
963 % that value is not stretched, that value is left as is.
965 % The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
968 % The format of the MinMaxStretchImage method is:
970 % MagickBooleanType MinMaxStretchImage(Image *image,
971 % const ChannelType channel, const double black_adjust,
972 % const double white_adjust)
974 % A description of each parameter follows:
976 % o image: The image to auto-level
978 % o channel: The channels to auto-level. If the special 'SyncChannels'
979 % flag is set, all the given channels are stretched by the same amount.
981 % o black_adjust, white_adjust: Move the Black/White Point inward
982 % from the minimum and maximum points by this color value.
986 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
987 const ChannelType channel,const double black_value,const double white_value)
997 if ((channel & SyncChannels) != 0)
1000 Auto-level all channels equally.
1002 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
1005 if (fabs(min-max) >= MagickEpsilon)
1006 status&=LevelImageChannel(image,channel,min,max,1.0);
1007 return(status != 0 ? MagickTrue : MagickFalse);
1010 Auto-level each channel separately.
1012 if ((channel & RedChannel) != 0)
1014 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1017 if (fabs(min-max) >= MagickEpsilon)
1018 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
1020 if ((channel & GreenChannel) != 0)
1022 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1026 if (fabs(min-max) >= MagickEpsilon)
1027 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
1029 if ((channel & BlueChannel) != 0)
1031 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1035 if (fabs(min-max) >= MagickEpsilon)
1036 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
1038 if (((channel & OpacityChannel) != 0) &&
1039 (image->matte == MagickTrue))
1041 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1045 if (fabs(min-max) >= MagickEpsilon)
1046 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
1048 if (((channel & IndexChannel) != 0) &&
1049 (image->colorspace == CMYKColorspace))
1051 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1055 if (fabs(min-max) >= MagickEpsilon)
1056 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
1058 return(status != 0 ? MagickTrue : MagickFalse);
1062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066 % G e t N u m b e r C o l o r s %
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1072 % GetNumberColors() returns the number of unique colors in an image.
1074 % The format of the GetNumberColors method is:
1076 % unsigned long GetNumberColors(const Image *image,FILE *file,
1077 % ExceptionInfo *exception)
1079 % A description of each parameter follows.
1081 % o image: the image.
1083 % o file: Write a histogram of the color distribution to this file handle.
1085 % o exception: return any errors or warnings in this structure.
1089 #if defined(__cplusplus) || defined(c_plusplus)
1093 static int HistogramCompare(const void *x,const void *y)
1099 color_1=(const ColorPacket *) x;
1100 color_2=(const ColorPacket *) y;
1101 if (color_2->pixel.red != color_1->pixel.red)
1102 return((int) color_1->pixel.red-(int) color_2->pixel.red);
1103 if (color_2->pixel.green != color_1->pixel.green)
1104 return((int) color_1->pixel.green-(int) color_2->pixel.green);
1105 if (color_2->pixel.blue != color_1->pixel.blue)
1106 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1107 return((int) color_2->count-(int) color_1->count);
1110 #if defined(__cplusplus) || defined(c_plusplus)
1114 MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
1115 ExceptionInfo *exception)
1117 #define HistogramImageTag "Histogram/Image"
1120 color[MaxTextExtent],
1122 tuple[MaxTextExtent];
1133 register ColorPacket
1143 if (file == (FILE *) NULL)
1148 cube_info=ClassifyImageColors(image,exception);
1149 if (cube_info != (CubeInfo *) NULL)
1150 number_colors=cube_info->colors;
1151 cube_info=DestroyCubeInfo(image,cube_info);
1152 return(number_colors);
1154 histogram=GetImageHistogram(image,&number_colors,exception);
1155 if (histogram == (ColorPacket *) NULL)
1156 return(number_colors);
1157 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1159 GetMagickPixelPacket(image,&pixel);
1161 for (i=0; i < (long) number_colors; i++)
1163 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1164 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1165 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1166 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1167 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1168 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1169 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1170 if (pixel.colorspace == CMYKColorspace)
1172 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1173 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1175 if (pixel.matte != MagickFalse)
1177 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1178 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1180 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1181 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1182 GetColorTuple(&pixel,MagickTrue,hex);
1183 (void) fprintf(file,MagickSizeFormat,p->count);
1184 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
1185 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1190 proceed=SetImageProgress(image,HistogramImageTag,i,number_colors);
1191 if (proceed == MagickFalse)
1196 (void) fflush(file);
1197 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1198 return(number_colors);
1202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1206 % U n i q u e I m a g e C o l o r s %
1210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 % UniqueImageColors() returns the unique colors of an image.
1214 % The format of the UniqueImageColors method is:
1216 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1218 % A description of each parameter follows.
1220 % o image: the image.
1222 % o exception: return any errors or warnings in this structure.
1226 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
1227 const NodeInfo *node_info,ExceptionInfo *exception)
1229 #define UniqueColorsImageTag "UniqueColors/Image"
1241 Traverse any children.
1243 number_children=image->matte == MagickFalse ? 8UL : 16UL;
1244 for (i=0; i < (long) number_children; i++)
1245 if (node_info->child[i] != (NodeInfo *) NULL)
1246 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
1247 if (node_info->level == (MaxTreeDepth-1))
1249 register ColorPacket
1252 register IndexPacket
1255 register PixelPacket
1259 for (i=0; i < (long) node_info->number_unique; i++)
1261 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
1262 if (q == (PixelPacket *) NULL)
1264 indexes=GetAuthenticIndexQueue(image);
1266 if (image->colorspace == CMYKColorspace)
1268 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1273 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1278 proceed=SetImageProgress(image,UniqueColorsImageTag,
1279 cube_info->progress,cube_info->colors);
1280 if (proceed == MagickFalse)
1283 cube_info->progress++;
1287 MagickExport Image *UniqueImageColors(const Image *image,
1288 ExceptionInfo *exception)
1296 cube_info=ClassifyImageColors(image,exception);
1297 if (cube_info == (CubeInfo *) NULL)
1298 return((Image *) NULL);
1299 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1300 if (unique_image == (Image *) NULL)
1301 return(unique_image);
1302 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1304 InheritException(exception,&unique_image->exception);
1305 unique_image=DestroyImage(unique_image);
1306 return((Image *) NULL);
1308 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
1309 if (cube_info->colors < MaxColormapSize)
1314 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1315 quantize_info->number_colors=MaxColormapSize;
1316 quantize_info->dither=MagickFalse;
1317 quantize_info->tree_depth=8;
1318 (void) QuantizeImage(quantize_info,unique_image);
1319 quantize_info=DestroyQuantizeInfo(quantize_info);
1321 cube_info=DestroyCubeInfo(image,cube_info);
1322 return(unique_image);