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
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 MagickPixelPacket *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->opacity)) >> index) &
175 static CubeInfo *ClassifyImageColors(const Image *image,
176 ExceptionInfo *exception)
178 #define EvaluateImageTag " Compute image colors... "
199 register const IndexPacket
202 register const PixelPacket
215 Initialize color description tree.
217 assert(image != (const Image *) NULL);
218 assert(image->signature == MagickSignature);
219 if (image->debug != MagickFalse)
220 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
221 cube_info=GetCubeInfo();
222 if (cube_info == (CubeInfo *) NULL)
224 (void) ThrowMagickException(exception,GetMagickModule(),
225 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
228 GetMagickPixelPacket(image,&pixel);
229 GetMagickPixelPacket(image,&target);
230 image_view=AcquireCacheView(image);
231 for (y=0; y < (ssize_t) image->rows; y++)
233 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
234 if (p == (const PixelPacket *) NULL)
236 indexes=GetCacheViewVirtualIndexQueue(image_view);
237 for (x=0; x < (ssize_t) image->columns; x++)
240 Start at the root and proceed level by level.
242 node_info=cube_info->root;
243 index=MaxTreeDepth-1;
244 for (level=1; level < MaxTreeDepth; level++)
246 SetMagickPixelPacket(image,p,indexes+x,&pixel);
247 id=ColorToNodeId(image,&pixel,index);
248 if (node_info->child[id] == (NodeInfo *) NULL)
250 node_info->child[id]=GetNodeInfo(cube_info,level);
251 if (node_info->child[id] == (NodeInfo *) NULL)
253 (void) ThrowMagickException(exception,GetMagickModule(),
254 ResourceLimitError,"MemoryAllocationFailed","`%s'",
259 node_info=node_info->child[id];
262 for (i=0; i < (ssize_t) node_info->number_unique; i++)
264 SetMagickPixelPacket(image,&node_info->list[i].pixel,
265 &node_info->list[i].index,&target);
266 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
269 if (i < (ssize_t) node_info->number_unique)
270 node_info->list[i].count++;
273 if (node_info->number_unique == 0)
274 node_info->list=(ColorPacket *) AcquireMagickMemory(
275 sizeof(*node_info->list));
277 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
278 (size_t) (i+1),sizeof(*node_info->list));
279 if (node_info->list == (ColorPacket *) NULL)
281 (void) ThrowMagickException(exception,GetMagickModule(),
282 ResourceLimitError,"MemoryAllocationFailed","`%s'",
286 node_info->list[i].pixel=(*p);
287 if ((image->colorspace == CMYKColorspace) ||
288 (image->storage_class == PseudoClass))
289 node_info->list[i].index=indexes[x];
290 node_info->list[i].count=1;
291 node_info->number_unique++;
296 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
298 if (proceed == MagickFalse)
301 image_view=DestroyCacheView(image_view);
306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310 + D e f i n e I m a g e H i s t o g r a m %
314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 % DefineImageHistogram() traverses the color cube tree and notes each colormap
317 % entry. A colormap entry is any node in the color cube tree where the
318 % of unique colors is not zero.
320 % The format of the DefineImageHistogram method is:
322 % DefineImageHistogram(const Image *image,NodeInfo *node_info,
323 % ColorPacket **unique_colors)
325 % A description of each parameter follows.
327 % o image: the image.
329 % o node_info: the address of a structure of type NodeInfo which points to a
330 % node in the color cube tree that is to be pruned.
332 % o histogram: the image histogram.
335 static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
336 ColorPacket **histogram)
345 Traverse any children.
347 number_children=image->matte == MagickFalse ? 8UL : 16UL;
348 for (i=0; i < (ssize_t) number_children; i++)
349 if (node_info->child[i] != (NodeInfo *) NULL)
350 DefineImageHistogram(image,node_info->child[i],histogram);
351 if (node_info->level == (MaxTreeDepth-1))
357 for (i=0; i < (ssize_t) node_info->number_unique; i++)
359 (*histogram)->pixel=p->pixel;
360 (*histogram)->index=p->index;
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 != (ColorPacket *) NULL)
453 node_info->list=(ColorPacket *) 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 *) AcquireAlignedMemory(1,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 ColorPacket *GetImageHistogram(const Image *image,
527 size_t *number_colors,ExceptionInfo *exception)
536 histogram=(ColorPacket *) NULL;
537 cube_info=ClassifyImageColors(image,exception);
538 if (cube_info != (CubeInfo *) NULL)
540 histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
542 if (histogram == (ColorPacket *) 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 *) AcquireAlignedMemory(1,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
656 register const IndexPacket
659 register const PixelPacket
676 assert(image != (Image *) NULL);
677 assert(image->signature == MagickSignature);
678 if (image->debug != MagickFalse)
679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
680 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
682 if (image->storage_class == PseudoClass)
685 Initialize color description tree.
687 cube_info=GetCubeInfo();
688 if (cube_info == (CubeInfo *) NULL)
690 (void) ThrowMagickException(exception,GetMagickModule(),
691 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
694 GetMagickPixelPacket(image,&pixel);
695 GetMagickPixelPacket(image,&target);
696 image_view=AcquireCacheView(image);
697 for (y=0; y < (ssize_t) image->rows; y++)
699 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
700 if (p == (const PixelPacket *) NULL)
702 indexes=GetCacheViewVirtualIndexQueue(image_view);
703 for (x=0; x < (ssize_t) image->columns; x++)
706 Start at the root and proceed level by level.
708 node_info=cube_info->root;
709 index=MaxTreeDepth-1;
710 for (level=1; level < MaxTreeDepth; level++)
712 SetMagickPixelPacket(image,p,indexes+x,&pixel);
713 id=ColorToNodeId(image,&pixel,index);
714 if (node_info->child[id] == (NodeInfo *) NULL)
716 node_info->child[id]=GetNodeInfo(cube_info,level);
717 if (node_info->child[id] == (NodeInfo *) NULL)
719 (void) ThrowMagickException(exception,GetMagickModule(),
720 ResourceLimitError,"MemoryAllocationFailed","`%s'",
725 node_info=node_info->child[id];
728 if (level < MaxTreeDepth)
730 for (i=0; i < (ssize_t) node_info->number_unique; i++)
732 SetMagickPixelPacket(image,&node_info->list[i].pixel,
733 &node_info->list[i].index,&target);
734 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
737 if (i < (ssize_t) node_info->number_unique)
738 node_info->list[i].count++;
742 Add this unique color to the color list.
744 if (node_info->number_unique == 0)
745 node_info->list=(ColorPacket *) AcquireMagickMemory(
746 sizeof(*node_info->list));
748 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
749 (size_t) (i+1),sizeof(*node_info->list));
750 if (node_info->list == (ColorPacket *) NULL)
752 (void) ThrowMagickException(exception,GetMagickModule(),
753 ResourceLimitError,"MemoryAllocationFailed","`%s'",
757 node_info->list[i].pixel=(*p);
758 if ((image->colorspace == CMYKColorspace) ||
759 (image->storage_class == PseudoClass))
760 node_info->list[i].index=indexes[x];
761 node_info->list[i].count=1;
762 node_info->number_unique++;
764 if (cube_info->colors > MaximumUniqueColors)
769 if (x < (ssize_t) image->columns)
772 image_view=DestroyCacheView(image_view);
773 cube_info=DestroyCubeInfo(image,cube_info);
774 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782 % I s P a l e t t e I m a g e %
786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788 % IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
789 % unique colors or less.
791 % The format of the IsPaletteImage method is:
793 % MagickBooleanType IsPaletteImage(const Image *image,
794 % ExceptionInfo *exception)
796 % A description of each parameter follows.
798 % o image: the image.
800 % o exception: return any errors or warnings in this structure.
803 MagickExport MagickBooleanType IsPaletteImage(const Image *image,
804 ExceptionInfo *exception)
819 register const IndexPacket
822 register const PixelPacket
839 assert(image != (Image *) NULL);
840 assert(image->signature == MagickSignature);
841 if (image->debug != MagickFalse)
842 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
843 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
845 if (image->storage_class == PseudoClass)
848 Initialize color description tree.
850 cube_info=GetCubeInfo();
851 if (cube_info == (CubeInfo *) NULL)
853 (void) ThrowMagickException(exception,GetMagickModule(),
854 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
857 GetMagickPixelPacket(image,&pixel);
858 GetMagickPixelPacket(image,&target);
859 image_view=AcquireCacheView(image);
860 for (y=0; y < (ssize_t) image->rows; y++)
862 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
863 if (p == (const PixelPacket *) NULL)
865 indexes=GetCacheViewVirtualIndexQueue(image_view);
866 for (x=0; x < (ssize_t) image->columns; x++)
869 Start at the root and proceed level by level.
871 node_info=cube_info->root;
872 index=MaxTreeDepth-1;
873 for (level=1; level < MaxTreeDepth; level++)
875 SetMagickPixelPacket(image,p,indexes+x,&pixel);
876 id=ColorToNodeId(image,&pixel,index);
877 if (node_info->child[id] == (NodeInfo *) NULL)
879 node_info->child[id]=GetNodeInfo(cube_info,level);
880 if (node_info->child[id] == (NodeInfo *) NULL)
882 (void) ThrowMagickException(exception,GetMagickModule(),
883 ResourceLimitError,"MemoryAllocationFailed","`%s'",
888 node_info=node_info->child[id];
891 if (level < MaxTreeDepth)
893 for (i=0; i < (ssize_t) node_info->number_unique; i++)
895 SetMagickPixelPacket(image,&node_info->list[i].pixel,
896 &node_info->list[i].index,&target);
897 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
900 if (i < (ssize_t) node_info->number_unique)
901 node_info->list[i].count++;
905 Add this unique color to the color list.
907 if (node_info->number_unique == 0)
908 node_info->list=(ColorPacket *) AcquireMagickMemory(
909 sizeof(*node_info->list));
911 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
912 (size_t) (i+1),sizeof(*node_info->list));
913 if (node_info->list == (ColorPacket *) NULL)
915 (void) ThrowMagickException(exception,GetMagickModule(),
916 ResourceLimitError,"MemoryAllocationFailed","`%s'",
920 node_info->list[i].pixel=(*p);
921 if ((image->colorspace == CMYKColorspace) ||
922 (image->storage_class == PseudoClass))
923 node_info->list[i].index=indexes[x];
924 node_info->list[i].count=1;
925 node_info->number_unique++;
927 if (cube_info->colors > 256)
932 if (x < (ssize_t) image->columns)
935 image_view=DestroyCacheView(image_view);
936 cube_info=DestroyCubeInfo(image,cube_info);
937 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945 % M i n M a x S t r e t c h I m a g e %
949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951 % MinMaxStretchImage() uses the exact minimum and maximum values found in
952 % each of the channels given, as the BlackPoint and WhitePoint to linearly
953 % stretch the colors (and histogram) of the image. The stretch points are
954 % also moved further inward by the adjustment values given.
956 % If the adjustment values are both zero this function is equivelent to a
957 % perfect normalization (or autolevel) of the image.
959 % Each channel is stretched independantally of each other (producing color
960 % distortion) unless the special 'SyncChannels' flag is also provided in the
961 % channels setting. If this flag is present the minimum and maximum point
962 % will be extracted from all the given channels, and those channels will be
963 % stretched by exactly the same amount (preventing color distortion).
965 % In the special case that only ONE value is found in a channel of the image
966 % that value is not stretched, that value is left as is.
968 % The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
971 % The format of the MinMaxStretchImage method is:
973 % MagickBooleanType MinMaxStretchImage(Image *image,
974 % const ChannelType channel, const double black_adjust,
975 % const double white_adjust)
977 % A description of each parameter follows:
979 % o image: The image to auto-level
981 % o channel: The channels to auto-level. If the special 'SyncChannels'
982 % flag is set, all the given channels are stretched by the same amount.
984 % o black_adjust, white_adjust: Move the Black/White Point inward
985 % from the minimum and maximum points by this color value.
989 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
990 const ChannelType channel,const double black_value,const double white_value)
1000 if ((channel & SyncChannels) != 0)
1003 Auto-level all channels equally.
1005 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
1008 if (fabs(min-max) >= MagickEpsilon)
1009 status&=LevelImageChannel(image,channel,min,max,1.0);
1010 return(status != 0 ? MagickTrue : MagickFalse);
1013 Auto-level each channel separately.
1015 if ((channel & RedChannel) != 0)
1017 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1020 if (fabs(min-max) >= MagickEpsilon)
1021 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
1023 if ((channel & GreenChannel) != 0)
1025 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1029 if (fabs(min-max) >= MagickEpsilon)
1030 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
1032 if ((channel & BlueChannel) != 0)
1034 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1038 if (fabs(min-max) >= MagickEpsilon)
1039 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
1041 if (((channel & OpacityChannel) != 0) &&
1042 (image->matte == MagickTrue))
1044 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1048 if (fabs(min-max) >= MagickEpsilon)
1049 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
1051 if (((channel & IndexChannel) != 0) &&
1052 (image->colorspace == CMYKColorspace))
1054 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1058 if (fabs(min-max) >= MagickEpsilon)
1059 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
1061 return(status != 0 ? MagickTrue : MagickFalse);
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 % G e t N u m b e r C o l o r s %
1073 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1075 % GetNumberColors() returns the number of unique colors in an image.
1077 % The format of the GetNumberColors method is:
1079 % size_t GetNumberColors(const Image *image,FILE *file,
1080 % ExceptionInfo *exception)
1082 % A description of each parameter follows.
1084 % o image: the image.
1086 % o file: Write a histogram of the color distribution to this file handle.
1088 % o exception: return any errors or warnings in this structure.
1092 #if defined(__cplusplus) || defined(c_plusplus)
1096 static int HistogramCompare(const void *x,const void *y)
1102 color_1=(const ColorPacket *) x;
1103 color_2=(const ColorPacket *) y;
1104 if (color_2->pixel.red != color_1->pixel.red)
1105 return((int) color_1->pixel.red-(int) color_2->pixel.red);
1106 if (color_2->pixel.green != color_1->pixel.green)
1107 return((int) color_1->pixel.green-(int) color_2->pixel.green);
1108 if (color_2->pixel.blue != color_1->pixel.blue)
1109 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1110 return((int) color_2->count-(int) color_1->count);
1113 #if defined(__cplusplus) || defined(c_plusplus)
1117 MagickExport size_t GetNumberColors(const Image *image,FILE *file,
1118 ExceptionInfo *exception)
1120 #define HistogramImageTag "Histogram/Image"
1123 color[MaxTextExtent],
1125 tuple[MaxTextExtent];
1136 register ColorPacket
1146 if (file == (FILE *) NULL)
1151 cube_info=ClassifyImageColors(image,exception);
1152 if (cube_info != (CubeInfo *) NULL)
1153 number_colors=cube_info->colors;
1154 cube_info=DestroyCubeInfo(image,cube_info);
1155 return(number_colors);
1157 histogram=GetImageHistogram(image,&number_colors,exception);
1158 if (histogram == (ColorPacket *) NULL)
1159 return(number_colors);
1160 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1162 GetMagickPixelPacket(image,&pixel);
1164 for (i=0; i < (ssize_t) number_colors; i++)
1166 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1167 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1168 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1169 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1170 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1171 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1172 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1173 if (pixel.colorspace == CMYKColorspace)
1175 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1176 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1178 if (pixel.matte != MagickFalse)
1180 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1181 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1183 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1184 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1185 GetColorTuple(&pixel,MagickTrue,hex);
1186 (void) fprintf(file,"%10" MagickSizeFormat,p->count);
1187 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
1188 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1193 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1195 if (proceed == MagickFalse)
1200 (void) fflush(file);
1201 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1202 return(number_colors);
1206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1210 % U n i q u e I m a g e C o l o r s %
1214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216 % UniqueImageColors() returns the unique colors of an image.
1218 % The format of the UniqueImageColors method is:
1220 % Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1222 % A description of each parameter follows.
1224 % o image: the image.
1226 % o exception: return any errors or warnings in this structure.
1230 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
1231 const NodeInfo *node_info,ExceptionInfo *exception)
1233 #define UniqueColorsImageTag "UniqueColors/Image"
1245 Traverse any children.
1247 number_children=image->matte == MagickFalse ? 8UL : 16UL;
1248 for (i=0; i < (ssize_t) number_children; i++)
1249 if (node_info->child[i] != (NodeInfo *) NULL)
1250 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
1251 if (node_info->level == (MaxTreeDepth-1))
1253 register ColorPacket
1256 register IndexPacket
1259 register PixelPacket
1263 for (i=0; i < (ssize_t) node_info->number_unique; i++)
1265 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
1266 if (q == (PixelPacket *) NULL)
1268 indexes=GetAuthenticIndexQueue(image);
1270 if (image->colorspace == CMYKColorspace)
1272 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1277 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1282 proceed=SetImageProgress(image,UniqueColorsImageTag,
1283 cube_info->progress,cube_info->colors);
1284 if (proceed == MagickFalse)
1287 cube_info->progress++;
1291 MagickExport Image *UniqueImageColors(const Image *image,
1292 ExceptionInfo *exception)
1300 cube_info=ClassifyImageColors(image,exception);
1301 if (cube_info == (CubeInfo *) NULL)
1302 return((Image *) NULL);
1303 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1304 if (unique_image == (Image *) NULL)
1305 return(unique_image);
1306 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1308 InheritException(exception,&unique_image->exception);
1309 unique_image=DestroyImage(unique_image);
1310 return((Image *) NULL);
1312 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
1313 if (cube_info->colors < MaxColormapSize)
1318 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1319 quantize_info->number_colors=MaxColormapSize;
1320 quantize_info->dither=MagickFalse;
1321 quantize_info->tree_depth=8;
1322 (void) QuantizeImage(quantize_info,unique_image);
1323 quantize_info=DestroyQuantizeInfo(quantize_info);
1325 cube_info=DestroyCubeInfo(image,cube_info);
1326 return(unique_image);