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)
996 if ((channel & SyncChannels) != 0)
999 Auto-level all channels equally.
1001 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
1004 if ( fabs(min-max) >= MagickEpsilon )
1005 status = LevelImageChannel(image,channel,min,max,1.0);
1009 Auto-level each channel separately.
1011 if ((channel & RedChannel) != 0)
1013 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1016 if ( fabs(min-max) >= MagickEpsilon )
1017 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
1019 if ((channel & GreenChannel) != 0)
1021 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1025 if ( fabs(min-max) >= MagickEpsilon )
1026 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
1028 if ((channel & BlueChannel) != 0)
1030 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1034 if ( fabs(min-max) >= MagickEpsilon )
1035 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
1037 if (((channel & OpacityChannel) != 0) &&
1038 (image->matte == MagickTrue))
1040 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1044 if ( fabs(min-max) >= MagickEpsilon )
1045 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
1047 if (((channel & IndexChannel) != 0) &&
1048 (image->colorspace == CMYKColorspace))
1050 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1054 if ( fabs(min-max) >= MagickEpsilon )
1055 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
1061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065 % G e t N u m b e r C o l o r s %
1069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 % GetNumberColors() returns the number of unique colors in an image.
1073 % The format of the GetNumberColors method is:
1075 % unsigned long GetNumberColors(const Image *image,FILE *file,
1076 % ExceptionInfo *exception)
1078 % A description of each parameter follows.
1080 % o image: the image.
1082 % o file: Write a histogram of the color distribution to this file handle.
1084 % o exception: return any errors or warnings in this structure.
1088 #if defined(__cplusplus) || defined(c_plusplus)
1092 static int HistogramCompare(const void *x,const void *y)
1098 color_1=(const ColorPacket *) x;
1099 color_2=(const ColorPacket *) y;
1100 if (color_2->pixel.red != color_1->pixel.red)
1101 return((int) color_1->pixel.red-(int) color_2->pixel.red);
1102 if (color_2->pixel.green != color_1->pixel.green)
1103 return((int) color_1->pixel.green-(int) color_2->pixel.green);
1104 if (color_2->pixel.blue != color_1->pixel.blue)
1105 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1106 return((int) color_2->count-(int) color_1->count);
1109 #if defined(__cplusplus) || defined(c_plusplus)
1113 MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
1114 ExceptionInfo *exception)
1116 #define HistogramImageTag "Histogram/Image"
1119 color[MaxTextExtent],
1121 tuple[MaxTextExtent];
1132 register ColorPacket
1142 if (file == (FILE *) NULL)
1147 cube_info=ClassifyImageColors(image,exception);
1148 if (cube_info != (CubeInfo *) NULL)
1149 number_colors=cube_info->colors;
1150 cube_info=DestroyCubeInfo(image,cube_info);
1151 return(number_colors);
1153 histogram=GetImageHistogram(image,&number_colors,exception);
1154 if (histogram == (ColorPacket *) NULL)
1155 return(number_colors);
1156 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1158 GetMagickPixelPacket(image,&pixel);
1160 for (i=0; i < (long) number_colors; i++)
1162 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1163 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1164 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1165 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1166 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1167 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1168 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1169 if (pixel.colorspace == CMYKColorspace)
1171 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1172 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1174 if (pixel.matte != MagickFalse)
1176 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1177 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1179 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1180 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1181 GetColorTuple(&pixel,MagickTrue,hex);
1182 (void) fprintf(file,MagickSizeFormat,p->count);
1183 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
1184 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1189 proceed=SetImageProgress(image,HistogramImageTag,i,number_colors);
1190 if (proceed == MagickFalse)
1195 (void) fflush(file);
1196 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1197 return(number_colors);
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205 % U n i q u e I m a g e C o l o r s %
1209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211 % UniqueImageColors() returns the unique colors of an image.
1213 % The format of the UniqueImageColors method is:
1215 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1217 % A description of each parameter follows.
1219 % o image: the image.
1221 % o exception: return any errors or warnings in this structure.
1225 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
1226 const NodeInfo *node_info,ExceptionInfo *exception)
1228 #define UniqueColorsImageTag "UniqueColors/Image"
1240 Traverse any children.
1242 number_children=image->matte == MagickFalse ? 8UL : 16UL;
1243 for (i=0; i < (long) number_children; i++)
1244 if (node_info->child[i] != (NodeInfo *) NULL)
1245 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
1246 if (node_info->level == (MaxTreeDepth-1))
1248 register ColorPacket
1251 register IndexPacket
1254 register PixelPacket
1258 for (i=0; i < (long) node_info->number_unique; i++)
1260 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
1261 if (q == (PixelPacket *) NULL)
1263 indexes=GetAuthenticIndexQueue(image);
1265 if (image->colorspace == CMYKColorspace)
1267 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1272 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1277 proceed=SetImageProgress(image,UniqueColorsImageTag,
1278 cube_info->progress,cube_info->colors);
1279 if (proceed == MagickFalse)
1282 cube_info->progress++;
1286 MagickExport Image *UniqueImageColors(const Image *image,
1287 ExceptionInfo *exception)
1295 cube_info=ClassifyImageColors(image,exception);
1296 if (cube_info == (CubeInfo *) NULL)
1297 return((Image *) NULL);
1298 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1299 if (unique_image == (Image *) NULL)
1300 return(unique_image);
1301 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1303 InheritException(exception,&unique_image->exception);
1304 unique_image=DestroyImage(unique_image);
1305 return((Image *) NULL);
1307 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
1308 if (cube_info->colors < MaxColormapSize)
1313 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1314 quantize_info->number_colors=MaxColormapSize;
1315 quantize_info->dither=MagickFalse;
1316 quantize_info->tree_depth=8;
1317 (void) QuantizeImage(quantize_info,unique_image);
1318 quantize_info=DestroyQuantizeInfo(quantize_info);
1320 cube_info=DestroyCubeInfo(image,cube_info);
1321 return(unique_image);