2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % EEEEE N N H H AAA N N CCCC EEEEE %
7 % E NN N H H A A NN N C E %
8 % EEE N N N HHHHH AAAAA N N N C EEE %
9 % E N NN H H A A N NN C E %
10 % EEEEE N N H H A A N N CCCC EEEEE %
13 % MagickCore Image Enhancement Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite-private.h"
51 #include "MagickCore/enhance.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/fx.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
57 #include "MagickCore/histogram.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/option.h"
64 #include "MagickCore/pixel-accessor.h"
65 #include "MagickCore/quantum.h"
66 #include "MagickCore/quantum-private.h"
67 #include "MagickCore/resample.h"
68 #include "MagickCore/resample-private.h"
69 #include "MagickCore/statistic.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/string-private.h"
72 #include "MagickCore/thread-private.h"
73 #include "MagickCore/token.h"
74 #include "MagickCore/xml-tree.h"
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 % A u t o G a m m a I m a g e %
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % AutoGammaImage() extract the 'mean' from the image and adjust the image
88 % to try make set its gamma appropriatally.
90 % The format of the AutoGammaImage method is:
92 % MagickBooleanType AutoGammaImage(Image *image)
94 % A description of each parameter follows:
96 % o image: The image to auto-level
99 MagickExport MagickBooleanType AutoGammaImage(Image *image)
111 if (image->sync != MagickFalse)
114 Apply gamma correction equally accross all given channels.
116 (void) GetImageMean(image,&mean,&sans,&image->exception);
117 gamma=log(mean*QuantumScale)/log_mean;
118 return(LevelImage(image,0.0,(double) QuantumRange,gamma));
121 Auto-gamma each channel separately.
124 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
126 PushPixelChannelMap(image,RedChannel);
127 (void) GetImageMean(image,&mean,&sans,&image->exception);
128 gamma=log(mean*QuantumScale)/log_mean;
129 status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
130 PopPixelChannelMap(image);
132 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
134 PushPixelChannelMap(image,GreenChannel);
135 (void) GetImageMean(image,&mean,&sans,&image->exception);
136 gamma=log(mean*QuantumScale)/log_mean;
137 status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
138 PopPixelChannelMap(image);
140 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
142 PushPixelChannelMap(image,BlueChannel);
143 (void) GetImageMean(image,&mean,&sans,&image->exception);
144 gamma=log(mean*QuantumScale)/log_mean;
145 status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
146 PopPixelChannelMap(image);
148 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
149 (image->colorspace == CMYKColorspace))
151 PushPixelChannelMap(image,BlackChannel);
152 (void) GetImageMean(image,&mean,&sans,&image->exception);
153 gamma=log(mean*QuantumScale)/log_mean;
154 status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
155 PopPixelChannelMap(image);
157 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
158 (image->matte == MagickTrue))
160 PushPixelChannelMap(image,AlphaChannel);
161 (void) GetImageMean(image,&mean,&sans,&image->exception);
162 gamma=log(mean*QuantumScale)/log_mean;
163 status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
164 PopPixelChannelMap(image);
166 return(status != 0 ? MagickTrue : MagickFalse);
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174 % A u t o L e v e l I m a g e %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 % AutoLevelImage() adjusts the levels of a particular image channel by
181 % scaling the minimum and maximum values to the full quantum range.
183 % The format of the LevelImage method is:
185 % MagickBooleanType AutoLevelImage(Image *image)
187 % A description of each parameter follows:
189 % o image: The image to auto-level
192 MagickExport MagickBooleanType AutoLevelImage(Image *image)
194 return(MinMaxStretchImage(image,0.0,0.0));
198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 % B r i g h t n e s s C o n t r a s t I m a g e %
206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208 % BrightnessContrastImage() changes the brightness and/or contrast of an
209 % image. It converts the brightness and contrast parameters into slope and
210 % intercept and calls a polynomical function to apply to the image.
212 % The format of the BrightnessContrastImage method is:
214 % MagickBooleanType BrightnessContrastImage(Image *image,
215 % const double brightness,const double contrast)
217 % A description of each parameter follows:
219 % o image: the image.
221 % o brightness: the brightness percent (-100 .. 100).
223 % o contrast: the contrast percent (-100 .. 100).
226 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
227 const double brightness,const double contrast)
229 #define BrightnessContastImageTag "BrightnessContast/Image"
241 Compute slope and intercept.
243 assert(image != (Image *) NULL);
244 assert(image->signature == MagickSignature);
245 if (image->debug != MagickFalse)
246 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
248 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
251 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
252 coefficients[0]=slope;
253 coefficients[1]=intercept;
254 status=FunctionImage(image,PolynomialFunction,2,coefficients,
260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264 % C o l o r D e c i s i o n L i s t I m a g e %
268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
271 % (CCC) file which solely contains one or more color corrections and applies
272 % the correction to the image. Here is a sample CCC file:
274 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
275 % <ColorCorrection id="cc03345">
277 % <Slope> 0.9 1.2 0.5 </Slope>
278 % <Offset> 0.4 -0.5 0.6 </Offset>
279 % <Power> 1.0 0.8 1.5 </Power>
282 % <Saturation> 0.85 </Saturation>
285 % </ColorCorrectionCollection>
287 % which includes the slop, offset, and power for each of the RGB channels
288 % as well as the saturation.
290 % The format of the ColorDecisionListImage method is:
292 % MagickBooleanType ColorDecisionListImage(Image *image,
293 % const char *color_correction_collection)
295 % A description of each parameter follows:
297 % o image: the image.
299 % o color_correction_collection: the color correction collection in XML.
302 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
303 const char *color_correction_collection)
305 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
307 typedef struct _Correction
315 typedef struct _ColorCorrection
330 token[MaxTextExtent];
364 Allocate and initialize cdl maps.
366 assert(image != (Image *) NULL);
367 assert(image->signature == MagickSignature);
368 if (image->debug != MagickFalse)
369 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
370 if (color_correction_collection == (const char *) NULL)
372 ccc=NewXMLTree((const char *) color_correction_collection,&image->exception);
373 if (ccc == (XMLTreeInfo *) NULL)
375 cc=GetXMLTreeChild(ccc,"ColorCorrection");
376 if (cc == (XMLTreeInfo *) NULL)
378 ccc=DestroyXMLTree(ccc);
381 color_correction.red.slope=1.0;
382 color_correction.red.offset=0.0;
383 color_correction.red.power=1.0;
384 color_correction.green.slope=1.0;
385 color_correction.green.offset=0.0;
386 color_correction.green.power=1.0;
387 color_correction.blue.slope=1.0;
388 color_correction.blue.offset=0.0;
389 color_correction.blue.power=1.0;
390 color_correction.saturation=0.0;
391 sop=GetXMLTreeChild(cc,"SOPNode");
392 if (sop != (XMLTreeInfo *) NULL)
399 slope=GetXMLTreeChild(sop,"Slope");
400 if (slope != (XMLTreeInfo *) NULL)
402 content=GetXMLTreeContent(slope);
403 p=(const char *) content;
404 for (i=0; (*p != '\0') && (i < 3); i++)
406 GetMagickToken(p,&p,token);
408 GetMagickToken(p,&p,token);
413 color_correction.red.slope=InterpretLocaleValue(token,
419 color_correction.green.slope=InterpretLocaleValue(token,
425 color_correction.blue.slope=InterpretLocaleValue(token,
432 offset=GetXMLTreeChild(sop,"Offset");
433 if (offset != (XMLTreeInfo *) NULL)
435 content=GetXMLTreeContent(offset);
436 p=(const char *) content;
437 for (i=0; (*p != '\0') && (i < 3); i++)
439 GetMagickToken(p,&p,token);
441 GetMagickToken(p,&p,token);
446 color_correction.red.offset=InterpretLocaleValue(token,
452 color_correction.green.offset=InterpretLocaleValue(token,
458 color_correction.blue.offset=InterpretLocaleValue(token,
465 power=GetXMLTreeChild(sop,"Power");
466 if (power != (XMLTreeInfo *) NULL)
468 content=GetXMLTreeContent(power);
469 p=(const char *) content;
470 for (i=0; (*p != '\0') && (i < 3); i++)
472 GetMagickToken(p,&p,token);
474 GetMagickToken(p,&p,token);
479 color_correction.red.power=InterpretLocaleValue(token,
485 color_correction.green.power=InterpretLocaleValue(token,
491 color_correction.blue.power=InterpretLocaleValue(token,
499 sat=GetXMLTreeChild(cc,"SATNode");
500 if (sat != (XMLTreeInfo *) NULL)
505 saturation=GetXMLTreeChild(sat,"Saturation");
506 if (saturation != (XMLTreeInfo *) NULL)
508 content=GetXMLTreeContent(saturation);
509 p=(const char *) content;
510 GetMagickToken(p,&p,token);
511 color_correction.saturation=InterpretLocaleValue(token,
515 ccc=DestroyXMLTree(ccc);
516 if (image->debug != MagickFalse)
518 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
519 " Color Correction Collection:");
520 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
521 " color_correction.red.slope: %g",color_correction.red.slope);
522 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
523 " color_correction.red.offset: %g",color_correction.red.offset);
524 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
525 " color_correction.red.power: %g",color_correction.red.power);
526 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
527 " color_correction.green.slope: %g",color_correction.green.slope);
528 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
529 " color_correction.green.offset: %g",color_correction.green.offset);
530 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
531 " color_correction.green.power: %g",color_correction.green.power);
532 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
533 " color_correction.blue.slope: %g",color_correction.blue.slope);
534 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
535 " color_correction.blue.offset: %g",color_correction.blue.offset);
536 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
537 " color_correction.blue.power: %g",color_correction.blue.power);
538 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
539 " color_correction.saturation: %g",color_correction.saturation);
541 cdl_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
542 if (cdl_map == (PixelPacket *) NULL)
543 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
545 #if defined(MAGICKCORE_OPENMP_SUPPORT)
546 #pragma omp parallel for schedule(dynamic,4)
548 for (i=0; i <= (ssize_t) MaxMap; i++)
550 cdl_map[i].red=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
551 MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
552 color_correction.red.offset,color_correction.red.power)))));
553 cdl_map[i].green=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
554 MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
555 color_correction.green.offset,color_correction.green.power)))));
556 cdl_map[i].blue=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
557 MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
558 color_correction.blue.offset,color_correction.blue.power)))));
560 if (image->storage_class == PseudoClass)
563 Apply transfer function to colormap.
565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
566 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
568 for (i=0; i < (ssize_t) image->colors; i++)
573 luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
574 0.0722*image->colormap[i].blue;
575 image->colormap[i].red=ClampToQuantum(luma+color_correction.saturation*
576 cdl_map[ScaleQuantumToMap(image->colormap[i].red)].red-luma);
577 image->colormap[i].green=ClampToQuantum(luma+
578 color_correction.saturation*cdl_map[ScaleQuantumToMap(
579 image->colormap[i].green)].green-luma);
580 image->colormap[i].blue=ClampToQuantum(luma+color_correction.saturation*
581 cdl_map[ScaleQuantumToMap(image->colormap[i].blue)].blue-luma);
585 Apply transfer function to image.
589 exception=(&image->exception);
590 image_view=AcquireCacheView(image);
591 #if defined(MAGICKCORE_OPENMP_SUPPORT)
592 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
594 for (y=0; y < (ssize_t) image->rows; y++)
605 if (status == MagickFalse)
607 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
608 if (q == (const Quantum *) NULL)
613 for (x=0; x < (ssize_t) image->columns; x++)
615 luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
616 GetPixelBlue(image,q);
617 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
618 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
619 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
620 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
621 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
622 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
623 q+=GetPixelChannels(image);
625 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
627 if (image->progress_monitor != (MagickProgressMonitor) NULL)
632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
633 #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
635 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
636 progress++,image->rows);
637 if (proceed == MagickFalse)
641 image_view=DestroyCacheView(image_view);
642 cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651 % C l u t I m a g e %
655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
657 % ClutImage() replaces each color value in the given image, by using it as an
658 % index to lookup a replacement color value in a Color Look UP Table in the
659 % form of an image. The values are extracted along a diagonal of the CLUT
660 % image so either a horizontal or vertial gradient image can be used.
662 % Typically this is used to either re-color a gray-scale image according to a
663 % color gradient in the CLUT image, or to perform a freeform histogram
664 % (level) adjustment according to the (typically gray-scale) gradient in the
667 % When the 'channel' mask includes the matte/alpha transparency channel but
668 % one image has no such channel it is assumed that that image is a simple
669 % gray-scale image that will effect the alpha channel values, either for
670 % gray-scale coloring (with transparent or semi-transparent colors), or
671 % a histogram adjustment of existing alpha channel values. If both images
672 % have matte channels, direct and normal indexing is applied, which is rarely
675 % The format of the ClutImage method is:
677 % MagickBooleanType ClutImage(Image *image,Image *clut_image)
679 % A description of each parameter follows:
681 % o image: the image, which is replaced by indexed CLUT values
683 % o clut_image: the color lookup table image for replacement color values.
685 % o channel: the channel.
688 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
690 #define ClampAlphaPixelChannel(pixel) ClampToQuantum((pixel)->alpha)
691 #define ClampBlackPixelChannel(pixel) ClampToQuantum((pixel)->black)
692 #define ClampBluePixelChannel(pixel) ClampToQuantum((pixel)->blue)
693 #define ClampGreenPixelChannel(pixel) ClampToQuantum((pixel)->green)
694 #define ClampRedPixelChannel(pixel) ClampToQuantum((pixel)->red)
695 #define ClutImageTag "Clut/Image"
720 assert(image != (Image *) NULL);
721 assert(image->signature == MagickSignature);
722 if (image->debug != MagickFalse)
723 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
724 assert(clut_image != (Image *) NULL);
725 assert(clut_image->signature == MagickSignature);
726 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
728 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
730 if (clut_map == (PixelInfo *) NULL)
731 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
738 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
739 exception=(&image->exception);
740 clut_view=AcquireCacheView(clut_image);
741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
742 #pragma omp parallel for schedule(dynamic,4)
744 for (i=0; i <= (ssize_t) MaxMap; i++)
746 GetPixelInfo(clut_image,clut_map+i);
747 (void) InterpolatePixelInfo(clut_image,clut_view,
748 UndefinedInterpolatePixel,QuantumScale*i*(clut_image->columns-adjust),
749 QuantumScale*i*(clut_image->rows-adjust),clut_map+i,exception);
751 clut_view=DestroyCacheView(clut_view);
752 image_view=AcquireCacheView(image);
753 #if defined(MAGICKCORE_OPENMP_SUPPORT)
754 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
756 for (y=0; y < (ssize_t) image->rows; y++)
767 if (status == MagickFalse)
769 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
770 if (q == (const Quantum *) NULL)
775 GetPixelInfo(image,&pixel);
776 for (x=0; x < (ssize_t) image->columns; x++)
778 SetPixelInfo(image,q,&pixel);
779 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
780 SetPixelRed(image,ClampRedPixelChannel(clut_map+
781 ScaleQuantumToMap(GetPixelRed(image,q))),q);
782 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
783 SetPixelGreen(image,ClampGreenPixelChannel(clut_map+
784 ScaleQuantumToMap(GetPixelGreen(image,q))),q);
785 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
786 SetPixelBlue(image,ClampBluePixelChannel(clut_map+
787 ScaleQuantumToMap(GetPixelBlue(image,q))),q);
788 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
789 (image->colorspace == CMYKColorspace))
790 SetPixelBlack(image,ClampBlackPixelChannel(clut_map+
791 ScaleQuantumToMap(GetPixelBlack(image,q))),q);
792 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
794 if (clut_image->matte == MagickFalse)
795 SetPixelAlpha(image,GetPixelInfoIntensity(clut_map+
796 ScaleQuantumToMap((Quantum) GetPixelAlpha(image,q))),q);
798 if (image->matte == MagickFalse)
799 SetPixelAlpha(image,ClampAlphaPixelChannel(clut_map+
800 ScaleQuantumToMap((Quantum) GetPixelInfoIntensity(&pixel))),q);
802 SetPixelAlpha(image,ClampAlphaPixelChannel(clut_map+
803 ScaleQuantumToMap(GetPixelAlpha(image,q))),q);
805 q+=GetPixelChannels(image);
807 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
809 if (image->progress_monitor != (MagickProgressMonitor) NULL)
814 #if defined(MAGICKCORE_OPENMP_SUPPORT)
815 #pragma omp critical (MagickCore_ClutImage)
817 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
818 if (proceed == MagickFalse)
822 image_view=DestroyCacheView(image_view);
823 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
824 if ((clut_image->matte != MagickFalse) &&
825 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
826 (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835 % C o n t r a s t I m a g e %
839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841 % ContrastImage() enhances the intensity differences between the lighter and
842 % darker elements of the image. Set sharpen to a MagickTrue to increase the
843 % image contrast otherwise the contrast is reduced.
845 % The format of the ContrastImage method is:
847 % MagickBooleanType ContrastImage(Image *image,
848 % const MagickBooleanType sharpen)
850 % A description of each parameter follows:
852 % o image: the image.
854 % o sharpen: Increase or decrease image contrast.
858 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
866 Enhance contrast: dark color become darker, light color become lighter.
868 assert(red != (Quantum *) NULL);
869 assert(green != (Quantum *) NULL);
870 assert(blue != (Quantum *) NULL);
874 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
875 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
877 if (brightness > 1.0)
880 if (brightness < 0.0)
882 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
885 MagickExport MagickBooleanType ContrastImage(Image *image,
886 const MagickBooleanType sharpen)
888 #define ContrastImageTag "Contrast/Image"
911 assert(image != (Image *) NULL);
912 assert(image->signature == MagickSignature);
913 if (image->debug != MagickFalse)
914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
915 sign=sharpen != MagickFalse ? 1 : -1;
916 if (image->storage_class == PseudoClass)
919 Contrast enhance colormap.
921 for (i=0; i < (ssize_t) image->colors; i++)
922 Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
923 &image->colormap[i].blue);
926 Contrast enhance image.
930 exception=(&image->exception);
931 image_view=AcquireCacheView(image);
932 #if defined(MAGICKCORE_OPENMP_SUPPORT)
933 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
935 for (y=0; y < (ssize_t) image->rows; y++)
948 if (status == MagickFalse)
950 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
951 if (q == (const Quantum *) NULL)
956 for (x=0; x < (ssize_t) image->columns; x++)
958 red=GetPixelRed(image,q);
959 green=GetPixelGreen(image,q);
960 blue=GetPixelBlue(image,q);
961 Contrast(sign,&red,&green,&blue);
962 SetPixelRed(image,red,q);
963 SetPixelGreen(image,green,q);
964 SetPixelBlue(image,blue,q);
965 q+=GetPixelChannels(image);
967 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
969 if (image->progress_monitor != (MagickProgressMonitor) NULL)
974 #if defined(MAGICKCORE_OPENMP_SUPPORT)
975 #pragma omp critical (MagickCore_ContrastImage)
977 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
978 if (proceed == MagickFalse)
982 image_view=DestroyCacheView(image_view);
987 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
991 % C o n t r a s t S t r e t c h I m a g e %
995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997 % ContrastStretchImage() is a simple image enhancement technique that attempts
998 % to improve the contrast in an image by `stretching' the range of intensity
999 % values it contains to span a desired range of values. It differs from the
1000 % more sophisticated histogram equalization in that it can only apply a
1001 % linear scaling function to the image pixel values. As a result the
1002 % `enhancement' is less harsh.
1004 % The format of the ContrastStretchImage method is:
1006 % MagickBooleanType ContrastStretchImage(Image *image,
1007 % const char *levels)
1009 % A description of each parameter follows:
1011 % o image: the image.
1013 % o black_point: the black point.
1015 % o white_point: the white point.
1017 % o levels: Specify the levels where the black and white points have the
1018 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
1021 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1022 const double black_point,const double white_point)
1024 #define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
1025 #define ContrastStretchImageTag "ContrastStretch/Image"
1055 Allocate histogram and stretch map.
1057 assert(image != (Image *) NULL);
1058 assert(image->signature == MagickSignature);
1059 if (image->debug != MagickFalse)
1060 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1061 histogram=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1062 sizeof(*histogram));
1063 stretch_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1064 sizeof(*stretch_map));
1065 if ((histogram == (PixelInfo *) NULL) ||
1066 (stretch_map == (PixelInfo *) NULL))
1067 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1073 exception=(&image->exception);
1074 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
1075 image_view=AcquireCacheView(image);
1076 for (y=0; y < (ssize_t) image->rows; y++)
1078 register const Quantum
1084 if (status == MagickFalse)
1086 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1087 if (p == (const Quantum *) NULL)
1092 if (image->sync != MagickFalse)
1093 for (x=0; x < (ssize_t) image->columns; x++)
1098 intensity=GetPixelIntensity(image,p);
1099 histogram[ScaleQuantumToMap(intensity)].red++;
1100 histogram[ScaleQuantumToMap(intensity)].green++;
1101 histogram[ScaleQuantumToMap(intensity)].blue++;
1102 histogram[ScaleQuantumToMap(intensity)].black++;
1103 p+=GetPixelChannels(image);
1106 for (x=0; x < (ssize_t) image->columns; x++)
1108 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1109 histogram[ScaleQuantumToMap(GetPixelRed(image,p))].red++;
1110 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1111 histogram[ScaleQuantumToMap(GetPixelGreen(image,p))].green++;
1112 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1113 histogram[ScaleQuantumToMap(GetPixelBlue(image,p))].blue++;
1114 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1115 (image->colorspace == CMYKColorspace))
1116 histogram[ScaleQuantumToMap(GetPixelBlack(image,p))].black++;
1117 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1118 histogram[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha++;
1119 p+=GetPixelChannels(image);
1123 Find the histogram boundaries by locating the black/white levels.
1126 white.red=MaxRange(QuantumRange);
1127 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1130 for (i=0; i <= (ssize_t) MaxMap; i++)
1132 intensity+=histogram[i].red;
1133 if (intensity > black_point)
1136 black.red=(MagickRealType) i;
1138 for (i=(ssize_t) MaxMap; i != 0; i--)
1140 intensity+=histogram[i].red;
1141 if (intensity > ((double) image->columns*image->rows-white_point))
1144 white.red=(MagickRealType) i;
1147 white.green=MaxRange(QuantumRange);
1148 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1151 for (i=0; i <= (ssize_t) MaxMap; i++)
1153 intensity+=histogram[i].green;
1154 if (intensity > black_point)
1157 black.green=(MagickRealType) i;
1159 for (i=(ssize_t) MaxMap; i != 0; i--)
1161 intensity+=histogram[i].green;
1162 if (intensity > ((double) image->columns*image->rows-white_point))
1165 white.green=(MagickRealType) i;
1168 white.blue=MaxRange(QuantumRange);
1169 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1172 for (i=0; i <= (ssize_t) MaxMap; i++)
1174 intensity+=histogram[i].blue;
1175 if (intensity > black_point)
1178 black.blue=(MagickRealType) i;
1180 for (i=(ssize_t) MaxMap; i != 0; i--)
1182 intensity+=histogram[i].blue;
1183 if (intensity > ((double) image->columns*image->rows-white_point))
1186 white.blue=(MagickRealType) i;
1189 white.alpha=MaxRange(QuantumRange);
1190 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1193 for (i=0; i <= (ssize_t) MaxMap; i++)
1195 intensity+=histogram[i].alpha;
1196 if (intensity > black_point)
1199 black.alpha=(MagickRealType) i;
1201 for (i=(ssize_t) MaxMap; i != 0; i--)
1203 intensity+=histogram[i].alpha;
1204 if (intensity > ((double) image->columns*image->rows-white_point))
1207 white.alpha=(MagickRealType) i;
1210 white.black=MaxRange(QuantumRange);
1211 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && (image->colorspace == CMYKColorspace))
1214 for (i=0; i <= (ssize_t) MaxMap; i++)
1216 intensity+=histogram[i].black;
1217 if (intensity > black_point)
1220 black.black=(MagickRealType) i;
1222 for (i=(ssize_t) MaxMap; i != 0; i--)
1224 intensity+=histogram[i].black;
1225 if (intensity > ((double) image->columns*image->rows-white_point))
1228 white.black=(MagickRealType) i;
1230 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1232 Stretch the histogram to create the stretched image mapping.
1234 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
1235 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1236 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1238 for (i=0; i <= (ssize_t) MaxMap; i++)
1240 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1242 if (i < (ssize_t) black.red)
1243 stretch_map[i].red=0.0;
1245 if (i > (ssize_t) white.red)
1246 stretch_map[i].red=(MagickRealType) QuantumRange;
1248 if (black.red != white.red)
1249 stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
1250 (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
1252 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1254 if (i < (ssize_t) black.green)
1255 stretch_map[i].green=0.0;
1257 if (i > (ssize_t) white.green)
1258 stretch_map[i].green=(MagickRealType) QuantumRange;
1260 if (black.green != white.green)
1261 stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
1262 (MagickRealType) (MaxMap*(i-black.green)/(white.green-
1265 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1267 if (i < (ssize_t) black.blue)
1268 stretch_map[i].blue=0.0;
1270 if (i > (ssize_t) white.blue)
1271 stretch_map[i].blue=(MagickRealType) QuantumRange;
1273 if (black.blue != white.blue)
1274 stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
1275 (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
1278 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1280 if (i < (ssize_t) black.alpha)
1281 stretch_map[i].alpha=0.0;
1283 if (i > (ssize_t) white.alpha)
1284 stretch_map[i].alpha=(MagickRealType) QuantumRange;
1286 if (black.alpha != white.alpha)
1287 stretch_map[i].alpha=(MagickRealType) ScaleMapToQuantum(
1288 (MagickRealType) (MaxMap*(i-black.alpha)/(white.alpha-
1291 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1292 (image->colorspace == CMYKColorspace))
1294 if (i < (ssize_t) black.black)
1295 stretch_map[i].black=0.0;
1297 if (i > (ssize_t) white.black)
1298 stretch_map[i].black=(MagickRealType) QuantumRange;
1300 if (black.black != white.black)
1301 stretch_map[i].black=(MagickRealType) ScaleMapToQuantum(
1302 (MagickRealType) (MaxMap*(i-black.black)/(white.black-
1309 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) || (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1310 (image->colorspace == CMYKColorspace)))
1311 image->storage_class=DirectClass;
1312 if (image->storage_class == PseudoClass)
1317 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1318 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1320 for (i=0; i < (ssize_t) image->colors; i++)
1322 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1324 if (black.red != white.red)
1325 image->colormap[i].red=ClampToQuantum(stretch_map[
1326 ScaleQuantumToMap(image->colormap[i].red)].red);
1328 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1330 if (black.green != white.green)
1331 image->colormap[i].green=ClampToQuantum(stretch_map[
1332 ScaleQuantumToMap(image->colormap[i].green)].green);
1334 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1336 if (black.blue != white.blue)
1337 image->colormap[i].blue=ClampToQuantum(stretch_map[
1338 ScaleQuantumToMap(image->colormap[i].blue)].blue);
1340 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1342 if (black.alpha != white.alpha)
1343 image->colormap[i].alpha=ClampToQuantum(stretch_map[
1344 ScaleQuantumToMap(image->colormap[i].alpha)].alpha);
1353 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1354 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1356 for (y=0; y < (ssize_t) image->rows; y++)
1364 if (status == MagickFalse)
1366 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1367 if (q == (const Quantum *) NULL)
1372 for (x=0; x < (ssize_t) image->columns; x++)
1374 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1376 if (black.red != white.red)
1377 SetPixelRed(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1378 GetPixelRed(image,q))].red),q);
1380 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1382 if (black.green != white.green)
1383 SetPixelGreen(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1384 GetPixelGreen(image,q))].green),q);
1386 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1388 if (black.blue != white.blue)
1389 SetPixelBlue(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1390 GetPixelBlue(image,q))].blue),q);
1392 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1393 (image->colorspace == CMYKColorspace))
1395 if (black.black != white.black)
1396 SetPixelBlack(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1397 GetPixelBlack(image,q))].black),q);
1399 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1401 if (black.alpha != white.alpha)
1402 SetPixelAlpha(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1403 GetPixelAlpha(image,q))].alpha),q);
1405 q+=GetPixelChannels(image);
1407 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1409 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1414 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1415 #pragma omp critical (MagickCore_ContrastStretchImage)
1417 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1419 if (proceed == MagickFalse)
1423 image_view=DestroyCacheView(image_view);
1424 stretch_map=(PixelInfo *) RelinquishMagickMemory(stretch_map);
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 % E n h a n c e I m a g e %
1437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1439 % EnhanceImage() applies a digital filter that improves the quality of a
1442 % The format of the EnhanceImage method is:
1444 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1446 % A description of each parameter follows:
1448 % o image: the image.
1450 % o exception: return any errors or warnings in this structure.
1453 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1455 #define Enhance(weight) \
1456 mean=((MagickRealType) GetPixelRed(image,r)+pixel.red)/2; \
1457 distance=(MagickRealType) GetPixelRed(image,r)-(MagickRealType) pixel.red; \
1458 distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
1459 mean)*distance*distance; \
1460 mean=((MagickRealType) GetPixelGreen(image,r)+pixel.green)/2; \
1461 distance=(MagickRealType) GetPixelGreen(image,r)- \
1462 (MagickRealType) pixel.green; \
1463 distance_squared+=4.0*distance*distance; \
1464 mean=((MagickRealType) GetPixelBlue(image,r)+pixel.blue)/2; \
1465 distance=(MagickRealType) GetPixelBlue(image,r)- \
1466 (MagickRealType) pixel.blue; \
1467 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
1468 QuantumRange+1.0)-1.0-mean)*distance*distance; \
1469 mean=((MagickRealType) GetPixelAlpha(image,r)+pixel.alpha)/2; \
1470 distance=(MagickRealType) GetPixelAlpha(image,r)-(MagickRealType) pixel.alpha; \
1471 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
1472 QuantumRange+1.0)-1.0-mean)*distance*distance; \
1473 if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
1474 QuantumRange/25.0f)) \
1476 aggregate.red+=(weight)*GetPixelRed(image,r); \
1477 aggregate.green+=(weight)*GetPixelGreen(image,r); \
1478 aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1479 aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1480 total_weight+=(weight); \
1483 #define EnhanceImageTag "Enhance/Image"
1505 Initialize enhanced image attributes.
1507 assert(image != (const Image *) NULL);
1508 assert(image->signature == MagickSignature);
1509 if (image->debug != MagickFalse)
1510 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1511 assert(exception != (ExceptionInfo *) NULL);
1512 assert(exception->signature == MagickSignature);
1513 if ((image->columns < 5) || (image->rows < 5))
1514 return((Image *) NULL);
1515 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1517 if (enhance_image == (Image *) NULL)
1518 return((Image *) NULL);
1519 if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
1521 InheritException(exception,&enhance_image->exception);
1522 enhance_image=DestroyImage(enhance_image);
1523 return((Image *) NULL);
1530 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1531 image_view=AcquireCacheView(image);
1532 enhance_view=AcquireCacheView(enhance_image);
1533 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1534 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1536 for (y=0; y < (ssize_t) image->rows; y++)
1538 register const Quantum
1548 Read another scan line.
1550 if (status == MagickFalse)
1552 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1553 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1555 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1560 for (x=0; x < (ssize_t) image->columns; x++)
1574 register const Quantum
1578 Compute weighted average of target pixel color components.
1582 r=p+2*(image->columns+4)+2;
1583 GetPixelPacket(image,r,&pixel);
1585 Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
1586 r=p+(image->columns+4);
1587 Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
1588 r=p+2*(image->columns+4);
1589 Enhance(10.0); Enhance(40.0); Enhance(80.0); Enhance(40.0); Enhance(10.0);
1590 r=p+3*(image->columns+4);
1591 Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
1592 r=p+4*(image->columns+4);
1593 Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
1594 SetPixelRed(enhance_image,(Quantum) ((aggregate.red+
1595 (total_weight/2)-1)/total_weight),q);
1596 SetPixelGreen(enhance_image,(Quantum) ((aggregate.green+
1597 (total_weight/2)-1)/total_weight),q);
1598 SetPixelBlue(enhance_image,(Quantum) ((aggregate.blue+
1599 (total_weight/2)-1)/total_weight),q);
1600 SetPixelAlpha(enhance_image,(Quantum) ((aggregate.alpha+
1601 (total_weight/2)-1)/total_weight),q);
1602 p+=GetPixelChannels(image);
1603 q+=GetPixelChannels(enhance_image);
1605 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1607 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1612 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1613 #pragma omp critical (MagickCore_EnhanceImage)
1615 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1616 if (proceed == MagickFalse)
1620 enhance_view=DestroyCacheView(enhance_view);
1621 image_view=DestroyCacheView(image_view);
1622 return(enhance_image);
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 % E q u a l i z e I m a g e %
1634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636 % EqualizeImage() applies a histogram equalization to the image.
1638 % The format of the EqualizeImage method is:
1640 % MagickBooleanType EqualizeImage(Image *image)
1642 % A description of each parameter follows:
1644 % o image: the image.
1646 % o channel: the channel.
1649 MagickExport MagickBooleanType EqualizeImage(Image *image)
1651 #define EqualizeImageTag "Equalize/Image"
1680 Allocate and initialize histogram arrays.
1682 assert(image != (Image *) NULL);
1683 assert(image->signature == MagickSignature);
1684 if (image->debug != MagickFalse)
1685 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1686 equalize_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1687 sizeof(*equalize_map));
1688 histogram=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1689 sizeof(*histogram));
1690 map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
1691 if ((equalize_map == (PixelInfo *) NULL) ||
1692 (histogram == (PixelInfo *) NULL) ||
1693 (map == (PixelInfo *) NULL))
1695 if (map != (PixelInfo *) NULL)
1696 map=(PixelInfo *) RelinquishMagickMemory(map);
1697 if (histogram != (PixelInfo *) NULL)
1698 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1699 if (equalize_map != (PixelInfo *) NULL)
1700 equalize_map=(PixelInfo *) RelinquishMagickMemory(equalize_map);
1701 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1707 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
1708 exception=(&image->exception);
1709 for (y=0; y < (ssize_t) image->rows; y++)
1711 register const Quantum
1717 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1718 if (p == (const Quantum *) NULL)
1720 for (x=0; x < (ssize_t) image->columns; x++)
1722 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1723 histogram[ScaleQuantumToMap(GetPixelRed(image,p))].red++;
1724 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1725 histogram[ScaleQuantumToMap(GetPixelGreen(image,p))].green++;
1726 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1727 histogram[ScaleQuantumToMap(GetPixelBlue(image,p))].blue++;
1728 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1729 (image->colorspace == CMYKColorspace))
1730 histogram[ScaleQuantumToMap(GetPixelBlack(image,p))].black++;
1731 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1732 histogram[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha++;
1733 p+=GetPixelChannels(image);
1737 Integrate the histogram to get the equalization map.
1739 (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
1740 for (i=0; i <= (ssize_t) MaxMap; i++)
1742 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1743 intensity.red+=histogram[i].red;
1744 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1745 intensity.green+=histogram[i].green;
1746 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1747 intensity.blue+=histogram[i].blue;
1748 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1749 (image->colorspace == CMYKColorspace))
1750 intensity.black+=histogram[i].black;
1751 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1752 intensity.alpha+=histogram[i].alpha;
1756 white=map[(int) MaxMap];
1757 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
1758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1759 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1761 for (i=0; i <= (ssize_t) MaxMap; i++)
1763 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1764 (white.red != black.red))
1765 equalize_map[i].red=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1766 ((MaxMap*(map[i].red-black.red))/(white.red-black.red)));
1767 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1768 (white.green != black.green))
1769 equalize_map[i].green=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1770 ((MaxMap*(map[i].green-black.green))/(white.green-black.green)));
1771 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1772 (white.blue != black.blue))
1773 equalize_map[i].blue=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1774 ((MaxMap*(map[i].blue-black.blue))/(white.blue-black.blue)));
1775 if ((((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1776 (image->colorspace == CMYKColorspace)) &&
1777 (white.black != black.black))
1778 equalize_map[i].black=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1779 ((MaxMap*(map[i].black-black.black))/(white.black-black.black)));
1780 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1781 (white.alpha != black.alpha))
1782 equalize_map[i].alpha=(MagickRealType) ScaleMapToQuantum(
1783 (MagickRealType) ((MaxMap*(map[i].alpha-black.alpha))/
1784 (white.alpha-black.alpha)));
1786 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1787 map=(PixelInfo *) RelinquishMagickMemory(map);
1788 if (image->storage_class == PseudoClass)
1793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1794 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1796 for (i=0; i < (ssize_t) image->colors; i++)
1798 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1799 (white.red != black.red))
1800 image->colormap[i].red=ClampToQuantum(equalize_map[
1801 ScaleQuantumToMap(image->colormap[i].red)].red);
1802 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1803 (white.green != black.green))
1804 image->colormap[i].green=ClampToQuantum(equalize_map[
1805 ScaleQuantumToMap(image->colormap[i].green)].green);
1806 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1807 (white.blue != black.blue))
1808 image->colormap[i].blue=ClampToQuantum(equalize_map[
1809 ScaleQuantumToMap(image->colormap[i].blue)].blue);
1810 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1811 (white.alpha != black.alpha))
1812 image->colormap[i].alpha=ClampToQuantum(equalize_map[
1813 ScaleQuantumToMap(image->colormap[i].alpha)].alpha);
1821 exception=(&image->exception);
1822 image_view=AcquireCacheView(image);
1823 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1824 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1826 for (y=0; y < (ssize_t) image->rows; y++)
1834 if (status == MagickFalse)
1836 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1837 if (q == (const Quantum *) NULL)
1842 for (x=0; x < (ssize_t) image->columns; x++)
1844 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1845 (white.red != black.red))
1846 SetPixelRed(image,ClampToQuantum(equalize_map[
1847 ScaleQuantumToMap(GetPixelRed(image,q))].red),q);
1848 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1849 (white.green != black.green))
1850 SetPixelGreen(image,ClampToQuantum(equalize_map[
1851 ScaleQuantumToMap(GetPixelGreen(image,q))].green),q);
1852 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1853 (white.blue != black.blue))
1854 SetPixelBlue(image,ClampToQuantum(equalize_map[
1855 ScaleQuantumToMap(GetPixelBlue(image,q))].blue),q);
1856 if ((((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1857 (image->colorspace == CMYKColorspace)) &&
1858 (white.black != black.black))
1859 SetPixelBlack(image,ClampToQuantum(equalize_map[
1860 ScaleQuantumToMap(GetPixelBlack(image,q))].black),q);
1861 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1862 (white.alpha != black.alpha))
1863 SetPixelAlpha(image,ClampToQuantum(equalize_map[
1864 ScaleQuantumToMap(GetPixelAlpha(image,q))].alpha),q);
1865 q+=GetPixelChannels(image);
1867 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1869 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1874 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1875 #pragma omp critical (MagickCore_EqualizeImage)
1877 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1878 if (proceed == MagickFalse)
1882 image_view=DestroyCacheView(image_view);
1883 equalize_map=(PixelInfo *) RelinquishMagickMemory(equalize_map);
1888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1892 % G a m m a I m a g e %
1896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1898 % GammaImage() gamma-corrects a particular image channel. The same
1899 % image viewed on different devices will have perceptual differences in the
1900 % way the image's intensities are represented on the screen. Specify
1901 % individual gamma levels for the red, green, and blue channels, or adjust
1902 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1904 % You can also reduce the influence of a particular channel with a gamma
1907 % The format of the GammaImage method is:
1909 % MagickBooleanType GammaImage(Image *image,const double gamma,
1910 % ExceptionInfo *exception)
1912 % A description of each parameter follows:
1914 % o image: the image.
1916 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1918 % o gamma: the image gamma.
1921 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1922 ExceptionInfo *exception)
1924 #define GammaCorrectImageTag "GammaCorrect/Image"
1945 Allocate and initialize gamma maps.
1947 assert(image != (Image *) NULL);
1948 assert(image->signature == MagickSignature);
1949 if (image->debug != MagickFalse)
1950 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1953 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1954 if (gamma_map == (Quantum *) NULL)
1955 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1957 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1959 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (MaxMap > 256)
1960 #pragma omp parallel for
1962 for (i=0; i <= (ssize_t) MaxMap; i++)
1963 gamma_map[i]=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
1964 MagickRealType) (MaxMap*pow((double) i/MaxMap,1.0/gamma))));
1965 if (image->storage_class == PseudoClass)
1968 Gamma-correct colormap.
1970 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1971 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1973 for (i=0; i < (ssize_t) image->colors; i++)
1975 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1976 image->colormap[i].red=gamma_map[
1977 ScaleQuantumToMap(image->colormap[i].red)];
1978 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1979 image->colormap[i].green=gamma_map[
1980 ScaleQuantumToMap(image->colormap[i].green)];
1981 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1982 image->colormap[i].blue=gamma_map[
1983 ScaleQuantumToMap(image->colormap[i].blue)];
1984 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1985 image->colormap[i].alpha=gamma_map[
1986 ScaleQuantumToMap(image->colormap[i].alpha)];
1990 Gamma-correct image.
1994 image_view=AcquireCacheView(image);
1995 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1996 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1998 for (y=0; y < (ssize_t) image->rows; y++)
2006 if (status == MagickFalse)
2008 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2009 if (q == (const Quantum *) NULL)
2014 for (x=0; x < (ssize_t) image->columns; x++)
2019 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2024 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2025 if ((traits & UpdatePixelTrait) != 0)
2026 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
2028 q+=GetPixelChannels(image);
2030 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2032 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2037 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2038 #pragma omp critical (MagickCore_GammaImage)
2040 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
2042 if (proceed == MagickFalse)
2046 image_view=DestroyCacheView(image_view);
2047 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2048 if (image->gamma != 0.0)
2049 image->gamma*=gamma;
2054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2058 % H a l d C l u t I m a g e %
2062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2064 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2065 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2066 % Create it with the HALD coder. You can apply any color transformation to
2067 % the Hald image and then use this method to apply the transform to the
2070 % The format of the HaldClutImage method is:
2072 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image)
2074 % A description of each parameter follows:
2076 % o image: the image, which is replaced by indexed CLUT values
2078 % o hald_image: the color lookup table image for replacement color values.
2082 static inline size_t MagickMin(const size_t x,const size_t y)
2089 MagickExport MagickBooleanType HaldClutImage(Image *image,
2090 const Image *hald_image)
2092 #define HaldClutImageTag "Clut/Image"
2094 typedef struct _HaldInfo
2129 assert(image != (Image *) NULL);
2130 assert(image->signature == MagickSignature);
2131 if (image->debug != MagickFalse)
2132 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2133 assert(hald_image != (Image *) NULL);
2134 assert(hald_image->signature == MagickSignature);
2135 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2136 return(MagickFalse);
2137 if (image->matte == MagickFalse)
2138 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
2144 length=MagickMin(hald_image->columns,hald_image->rows);
2145 for (level=2; (level*level*level) < length; level++) ;
2147 cube_size=level*level;
2148 width=(double) hald_image->columns;
2149 GetPixelInfo(hald_image,&zero);
2150 exception=(&image->exception);
2151 image_view=AcquireCacheView(image);
2152 hald_view=AcquireCacheView(hald_image);
2153 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2154 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2156 for (y=0; y < (ssize_t) image->rows; y++)
2177 if (status == MagickFalse)
2179 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2180 if (q == (const Quantum *) NULL)
2190 for (x=0; x < (ssize_t) image->columns; x++)
2192 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2193 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2194 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2195 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2196 point.x-=floor(point.x);
2197 point.y-=floor(point.y);
2198 point.z-=floor(point.z);
2199 (void) InterpolatePixelInfo(image,hald_view,
2200 UndefinedInterpolatePixel,fmod(offset,width),floor(offset/width),
2202 (void) InterpolatePixelInfo(image,hald_view,
2203 UndefinedInterpolatePixel,fmod(offset+level,width),floor((offset+level)/
2204 width),&pixel2,exception);
2205 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,
2206 pixel2.alpha,point.y,&pixel3);
2208 (void) InterpolatePixelInfo(image,hald_view,
2209 UndefinedInterpolatePixel,fmod(offset,width),floor(offset/width),
2211 (void) InterpolatePixelInfo(image,hald_view,
2212 UndefinedInterpolatePixel,fmod(offset+level,width),floor((offset+level)/
2213 width),&pixel2,exception);
2214 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,
2215 pixel2.alpha,point.y,&pixel4);
2216 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,
2217 pixel4.alpha,point.z,&pixel);
2218 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2220 ClampToQuantum(pixel.red),q);
2221 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2222 SetPixelGreen(image,
2223 ClampToQuantum(pixel.green),q);
2224 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2226 ClampToQuantum(pixel.blue),q);
2227 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2228 (image->colorspace == CMYKColorspace))
2229 SetPixelBlack(image,
2230 ClampToQuantum(pixel.black),q);
2231 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && (image->matte != MagickFalse))
2232 SetPixelAlpha(image,
2233 ClampToQuantum(pixel.alpha),q);
2234 q+=GetPixelChannels(image);
2236 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2238 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2243 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2244 #pragma omp critical (MagickCore_HaldClutImage)
2246 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2247 if (proceed == MagickFalse)
2251 hald_view=DestroyCacheView(hald_view);
2252 image_view=DestroyCacheView(image_view);
2257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2261 % L e v e l I m a g e %
2265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2267 % LevelImage() adjusts the levels of a particular image channel by
2268 % scaling the colors falling between specified white and black points to
2269 % the full available quantum range.
2271 % The parameters provided represent the black, and white points. The black
2272 % point specifies the darkest color in the image. Colors darker than the
2273 % black point are set to zero. White point specifies the lightest color in
2274 % the image. Colors brighter than the white point are set to the maximum
2277 % If a '!' flag is given, map black and white colors to the given levels
2278 % rather than mapping those levels to black and white. See
2279 % LevelizeImage() below.
2281 % Gamma specifies a gamma correction to apply to the image.
2283 % The format of the LevelImage method is:
2285 % MagickBooleanType LevelImage(Image *image,const char *levels)
2287 % A description of each parameter follows:
2289 % o image: the image.
2291 % o levels: Specify the levels where the black and white points have the
2292 % range of 0-QuantumRange, and gamma has the range 0-10 (e.g. 10x90%+2).
2293 % A '!' flag inverts the re-mapping.
2296 MagickExport MagickBooleanType LevelImage(Image *image,
2297 const double black_point,const double white_point,const double gamma)
2299 #define LevelImageTag "Level/Image"
2300 #define LevelQuantum(x) (ClampToQuantum((MagickRealType) QuantumRange* \
2301 pow(scale*((double) (x)-black_point),1.0/gamma)))
2325 Allocate and initialize levels map.
2327 assert(image != (Image *) NULL);
2328 assert(image->signature == MagickSignature);
2329 if (image->debug != MagickFalse)
2330 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2331 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2332 if (image->storage_class == PseudoClass)
2333 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2334 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2336 for (i=0; i < (ssize_t) image->colors; i++)
2341 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2342 image->colormap[i].red=LevelQuantum(image->colormap[i].red);
2343 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2344 image->colormap[i].green=LevelQuantum(image->colormap[i].green);
2345 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2346 image->colormap[i].blue=LevelQuantum(image->colormap[i].blue);
2347 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2348 image->colormap[i].alpha=LevelQuantum(image->colormap[i].alpha);
2355 exception=(&image->exception);
2356 image_view=AcquireCacheView(image);
2357 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2358 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2360 for (y=0; y < (ssize_t) image->rows; y++)
2368 if (status == MagickFalse)
2370 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2371 if (q == (const Quantum *) NULL)
2376 for (x=0; x < (ssize_t) image->columns; x++)
2378 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2379 SetPixelRed(image,LevelQuantum(
2380 GetPixelRed(image,q)),q);
2381 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2382 SetPixelGreen(image,
2383 LevelQuantum(GetPixelGreen(image,q)),q);
2384 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2386 LevelQuantum(GetPixelBlue(image,q)),q);
2387 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2388 (image->matte == MagickTrue))
2389 SetPixelAlpha(image,
2390 LevelQuantum(GetPixelAlpha(image,q)),q);
2391 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2392 (image->colorspace == CMYKColorspace))
2393 SetPixelBlack(image,
2394 LevelQuantum(GetPixelBlack(image,q)),q);
2395 q+=GetPixelChannels(image);
2397 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2405 #pragma omp critical (MagickCore_LevelImage)
2407 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2408 if (proceed == MagickFalse)
2412 image_view=DestroyCacheView(image_view);
2417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2421 % L e v e l i z e I m a g e C h a n n e l %
2425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2427 % LevelizeImage() applies the reversed LevelImage() operation to just
2428 % the specific channels specified. It compresses the full range of color
2429 % values, so that they lie between the given black and white points. Gamma is
2430 % applied before the values are mapped.
2432 % LevelizeImage() can be called with by using a +level command line
2433 % API option, or using a '!' on a -level or LevelImage() geometry string.
2435 % It can be used for example de-contrast a greyscale image to the exact
2436 % levels specified. Or by using specific levels for each channel of an image
2437 % you can convert a gray-scale image to any linear color gradient, according
2440 % The format of the LevelizeImage method is:
2442 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2443 % const double white_point,const double gamma)
2445 % A description of each parameter follows:
2447 % o image: the image.
2449 % o black_point: The level to map zero (black) to.
2451 % o white_point: The level to map QuantiumRange (white) to.
2453 % o gamma: adjust gamma by this factor before mapping values.
2456 MagickExport MagickBooleanType LevelizeImage(Image *image,
2457 const double black_point,const double white_point,const double gamma)
2459 #define LevelizeImageTag "Levelize/Image"
2460 #define LevelizeValue(x) (ClampToQuantum(((MagickRealType) \
2461 pow((double) (QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
2483 Allocate and initialize levels map.
2485 assert(image != (Image *) NULL);
2486 assert(image->signature == MagickSignature);
2487 if (image->debug != MagickFalse)
2488 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2489 if (image->storage_class == PseudoClass)
2490 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2491 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2493 for (i=0; i < (ssize_t) image->colors; i++)
2498 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2499 image->colormap[i].red=LevelizeValue(image->colormap[i].red);
2500 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2501 image->colormap[i].green=LevelizeValue(image->colormap[i].green);
2502 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2503 image->colormap[i].blue=LevelizeValue(image->colormap[i].blue);
2504 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2505 image->colormap[i].alpha=LevelizeValue(image->colormap[i].alpha);
2512 exception=(&image->exception);
2513 image_view=AcquireCacheView(image);
2514 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2515 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2517 for (y=0; y < (ssize_t) image->rows; y++)
2525 if (status == MagickFalse)
2527 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2528 if (q == (const Quantum *) NULL)
2533 for (x=0; x < (ssize_t) image->columns; x++)
2535 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2536 SetPixelRed(image,LevelizeValue(GetPixelRed(image,q)),q);
2537 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2538 SetPixelGreen(image,LevelizeValue(GetPixelGreen(image,q)),q);
2539 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2540 SetPixelBlue(image,LevelizeValue(GetPixelBlue(image,q)),q);
2541 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2542 (image->colorspace == CMYKColorspace))
2543 SetPixelBlack(image,LevelizeValue(GetPixelBlack(image,q)),q);
2544 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2545 (image->matte == MagickTrue))
2546 SetPixelAlpha(image,LevelizeValue(GetPixelAlpha(image,q)),q);
2547 q+=GetPixelChannels(image);
2549 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2551 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2556 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2557 #pragma omp critical (MagickCore_LevelizeImage)
2559 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2560 if (proceed == MagickFalse)
2564 image_view=DestroyCacheView(image_view);
2569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2573 % L e v e l I m a g e C o l o r s %
2577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2579 % LevelImageColors() maps the given color to "black" and "white" values,
2580 % linearly spreading out the colors, and level values on a channel by channel
2581 % bases, as per LevelImage(). The given colors allows you to specify
2582 % different level ranges for each of the color channels separately.
2584 % If the boolean 'invert' is set true the image values will modifyed in the
2585 % reverse direction. That is any existing "black" and "white" colors in the
2586 % image will become the color values given, with all other values compressed
2587 % appropriatally. This effectivally maps a greyscale gradient into the given
2590 % The format of the LevelImageColors method is:
2592 % MagickBooleanType LevelImageColors(Image *image,
2593 % const PixelInfo *black_color,const PixelInfo *white_color,
2594 % const MagickBooleanType invert)
2596 % A description of each parameter follows:
2598 % o image: the image.
2600 % o black_color: The color to map black to/from
2602 % o white_point: The color to map white to/from
2604 % o invert: if true map the colors (levelize), rather than from (level)
2607 MagickExport MagickBooleanType LevelImageColors(Image *image,
2608 const PixelInfo *black_color,const PixelInfo *white_color,
2609 const MagickBooleanType invert)
2615 Allocate and initialize levels map.
2617 assert(image != (Image *) NULL);
2618 assert(image->signature == MagickSignature);
2619 if (image->debug != MagickFalse)
2620 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2622 if (invert == MagickFalse)
2624 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2626 PushPixelChannelMap(image,RedChannel);
2627 status|=LevelImage(image,black_color->red,white_color->red,1.0);
2628 PopPixelChannelMap(image);
2630 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2632 PushPixelChannelMap(image,GreenChannel);
2633 status|=LevelImage(image,black_color->green,white_color->green,1.0);
2634 PopPixelChannelMap(image);
2636 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2638 PushPixelChannelMap(image,BlueChannel);
2639 status|=LevelImage(image,black_color->blue,white_color->blue,1.0);
2640 PopPixelChannelMap(image);
2642 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2643 (image->colorspace == CMYKColorspace))
2645 PushPixelChannelMap(image,BlackChannel);
2646 status|=LevelImage(image,black_color->black,white_color->black,1.0);
2647 PopPixelChannelMap(image);
2649 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2650 (image->matte == MagickTrue))
2652 PushPixelChannelMap(image,AlphaChannel);
2653 status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0);
2654 PopPixelChannelMap(image);
2659 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2661 PushPixelChannelMap(image,RedChannel);
2662 status|=LevelizeImage(image,black_color->red,white_color->red,1.0);
2663 PopPixelChannelMap(image);
2665 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2667 PushPixelChannelMap(image,GreenChannel);
2668 status|=LevelizeImage(image,black_color->green,white_color->green,
2670 PopPixelChannelMap(image);
2672 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2674 PushPixelChannelMap(image,BlueChannel);
2675 status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0);
2676 PopPixelChannelMap(image);
2678 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2679 (image->colorspace == CMYKColorspace))
2681 PushPixelChannelMap(image,BlackChannel);
2682 status|=LevelizeImage(image,black_color->black,white_color->black,
2684 PopPixelChannelMap(image);
2686 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2687 (image->matte == MagickTrue))
2689 PushPixelChannelMap(image,AlphaChannel);
2690 status|=LevelizeImage(image,black_color->alpha,white_color->alpha,
2692 PopPixelChannelMap(image);
2695 return(status == 0 ? MagickFalse : MagickTrue);
2699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2703 % L i n e a r S t r e t c h I m a g e %
2707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709 % LinearStretchImage() discards any pixels below the black point and above
2710 % the white point and levels the remaining pixels.
2712 % The format of the LinearStretchImage method is:
2714 % MagickBooleanType LinearStretchImage(Image *image,
2715 % const double black_point,const double white_point)
2717 % A description of each parameter follows:
2719 % o image: the image.
2721 % o black_point: the black point.
2723 % o white_point: the white point.
2726 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2727 const double black_point,const double white_point)
2729 #define LinearStretchImageTag "LinearStretch/Image"
2747 Allocate histogram and linear map.
2749 assert(image != (Image *) NULL);
2750 assert(image->signature == MagickSignature);
2751 histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
2752 sizeof(*histogram));
2753 if (histogram == (MagickRealType *) NULL)
2754 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2759 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2760 exception=(&image->exception);
2761 for (y=0; y < (ssize_t) image->rows; y++)
2763 register const Quantum
2769 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2770 if (p == (const Quantum *) NULL)
2772 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2774 histogram[ScaleQuantumToMap(GetPixelIntensity(image,p))]++;
2775 p+=GetPixelChannels(image);
2779 Find the histogram boundaries by locating the black and white point levels.
2782 for (black=0; black < (ssize_t) MaxMap; black++)
2784 intensity+=histogram[black];
2785 if (intensity >= black_point)
2789 for (white=(ssize_t) MaxMap; white != 0; white--)
2791 intensity+=histogram[white];
2792 if (intensity >= white_point)
2795 histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
2796 status=LevelImage(image,(double) black,(double) white,1.0);
2801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2805 % M o d u l a t e I m a g e %
2809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2811 % ModulateImage() lets you control the brightness, saturation, and hue
2812 % of an image. Modulate represents the brightness, saturation, and hue
2813 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2814 % modulation is lightness, saturation, and hue. And if the colorspace is
2815 % HWB, use blackness, whiteness, and hue.
2817 % The format of the ModulateImage method is:
2819 % MagickBooleanType ModulateImage(Image *image,const char *modulate)
2821 % A description of each parameter follows:
2823 % o image: the image.
2825 % o modulate: Define the percent change in brightness, saturation, and
2830 static void ModulateHSB(const double percent_hue,
2831 const double percent_saturation,const double percent_brightness,
2832 Quantum *red,Quantum *green,Quantum *blue)
2840 Increase or decrease color brightness, saturation, or hue.
2842 assert(red != (Quantum *) NULL);
2843 assert(green != (Quantum *) NULL);
2844 assert(blue != (Quantum *) NULL);
2845 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2846 hue+=0.5*(0.01*percent_hue-1.0);
2851 saturation*=0.01*percent_saturation;
2852 brightness*=0.01*percent_brightness;
2853 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2856 static void ModulateHSL(const double percent_hue,
2857 const double percent_saturation,const double percent_lightness,
2858 Quantum *red,Quantum *green,Quantum *blue)
2866 Increase or decrease color lightness, saturation, or hue.
2868 assert(red != (Quantum *) NULL);
2869 assert(green != (Quantum *) NULL);
2870 assert(blue != (Quantum *) NULL);
2871 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2872 hue+=0.5*(0.01*percent_hue-1.0);
2877 saturation*=0.01*percent_saturation;
2878 lightness*=0.01*percent_lightness;
2879 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2882 static void ModulateHWB(const double percent_hue,const double percent_whiteness, const double percent_blackness,Quantum *red,Quantum *green,Quantum *blue)
2890 Increase or decrease color blackness, whiteness, or hue.
2892 assert(red != (Quantum *) NULL);
2893 assert(green != (Quantum *) NULL);
2894 assert(blue != (Quantum *) NULL);
2895 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2896 hue+=0.5*(0.01*percent_hue-1.0);
2901 blackness*=0.01*percent_blackness;
2902 whiteness*=0.01*percent_whiteness;
2903 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2906 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
2908 #define ModulateImageTag "Modulate/Image"
2946 Initialize modulate table.
2948 assert(image != (Image *) NULL);
2949 assert(image->signature == MagickSignature);
2950 if (image->debug != MagickFalse)
2951 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2952 if (modulate == (char *) NULL)
2953 return(MagickFalse);
2954 flags=ParseGeometry(modulate,&geometry_info);
2955 percent_brightness=geometry_info.rho;
2956 percent_saturation=geometry_info.sigma;
2957 if ((flags & SigmaValue) == 0)
2958 percent_saturation=100.0;
2959 percent_hue=geometry_info.xi;
2960 if ((flags & XiValue) == 0)
2962 colorspace=UndefinedColorspace;
2963 artifact=GetImageArtifact(image,"modulate:colorspace");
2964 if (artifact != (const char *) NULL)
2965 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2966 MagickFalse,artifact);
2967 if (image->storage_class == PseudoClass)
2972 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2973 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2975 for (i=0; i < (ssize_t) image->colors; i++)
2980 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2981 &image->colormap[i].red,&image->colormap[i].green,
2982 &image->colormap[i].blue);
2988 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2989 &image->colormap[i].red,&image->colormap[i].green,
2990 &image->colormap[i].blue);
2995 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2996 &image->colormap[i].red,&image->colormap[i].green,
2997 &image->colormap[i].blue);
3007 exception=(&image->exception);
3008 image_view=AcquireCacheView(image);
3009 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3010 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3012 for (y=0; y < (ssize_t) image->rows; y++)
3025 if (status == MagickFalse)
3027 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3028 if (q == (const Quantum *) NULL)
3033 for (x=0; x < (ssize_t) image->columns; x++)
3035 red=GetPixelRed(image,q);
3036 green=GetPixelGreen(image,q);
3037 blue=GetPixelBlue(image,q);
3042 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3049 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3055 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3060 SetPixelRed(image,red,q);
3061 SetPixelGreen(image,green,q);
3062 SetPixelBlue(image,blue,q);
3063 q+=GetPixelChannels(image);
3065 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3067 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3072 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3073 #pragma omp critical (MagickCore_ModulateImage)
3075 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3076 if (proceed == MagickFalse)
3080 image_view=DestroyCacheView(image_view);
3085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3089 % N e g a t e I m a g e %
3093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3095 % NegateImage() negates the colors in the reference image. The grayscale
3096 % option means that only grayscale values within the image are negated.
3098 % The format of the NegateImage method is:
3100 % MagickBooleanType NegateImage(Image *image,
3101 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3103 % A description of each parameter follows:
3105 % o image: the image.
3107 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3109 % o exception: return any errors or warnings in this structure.
3112 MagickExport MagickBooleanType NegateImage(Image *image,
3113 const MagickBooleanType grayscale,ExceptionInfo *exception)
3115 #define NegateImageTag "Negate/Image"
3132 assert(image != (Image *) NULL);
3133 assert(image->signature == MagickSignature);
3134 if (image->debug != MagickFalse)
3135 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3136 if (image->storage_class == PseudoClass)
3141 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3142 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3144 for (i=0; i < (ssize_t) image->colors; i++)
3146 if (grayscale != MagickFalse)
3147 if ((image->colormap[i].red != image->colormap[i].green) ||
3148 (image->colormap[i].green != image->colormap[i].blue))
3150 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3151 image->colormap[i].red=(Quantum) QuantumRange-
3152 image->colormap[i].red;
3153 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3154 image->colormap[i].green=(Quantum) QuantumRange-
3155 image->colormap[i].green;
3156 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3157 image->colormap[i].blue=(Quantum) QuantumRange-
3158 image->colormap[i].blue;
3166 image_view=AcquireCacheView(image);
3167 if (grayscale != MagickFalse)
3169 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3170 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3172 for (y=0; y < (ssize_t) image->rows; y++)
3183 if (status == MagickFalse)
3185 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3187 if (q == (const Quantum *) NULL)
3192 for (x=0; x < (ssize_t) image->columns; x++)
3197 if (IsPixelGray(image,q) != MagickFalse)
3199 q+=GetPixelChannels(image);
3202 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3207 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3208 if ((traits & UpdatePixelTrait) != 0)
3209 q[i]=QuantumRange-q[i];
3211 q+=GetPixelChannels(image);
3213 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3214 if (sync == MagickFalse)
3216 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3221 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3222 #pragma omp critical (MagickCore_NegateImage)
3224 proceed=SetImageProgress(image,NegateImageTag,progress++,
3226 if (proceed == MagickFalse)
3230 image_view=DestroyCacheView(image_view);
3236 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3237 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3239 for (y=0; y < (ssize_t) image->rows; y++)
3247 if (status == MagickFalse)
3249 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3250 if (q == (const Quantum *) NULL)
3255 for (x=0; x < (ssize_t) image->columns; x++)
3260 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3265 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3266 if ((traits & UpdatePixelTrait) != 0)
3267 q[i]=QuantumRange-q[i];
3269 q+=GetPixelChannels(image);
3271 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3273 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3279 #pragma omp critical (MagickCore_NegateImage)
3281 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3282 if (proceed == MagickFalse)
3286 image_view=DestroyCacheView(image_view);
3291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3295 % N o r m a l i z e I m a g e %
3299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3301 % NormalizeImage() enhances the contrast of a color image by mapping the
3302 % darkest 2 percent of all pixel to black and the brightest 1 percent to white.
3304 % The format of the NormalizeImage method is:
3306 % MagickBooleanType NormalizeImage(Image *image)
3308 % A description of each parameter follows:
3310 % o image: the image.
3313 MagickExport MagickBooleanType NormalizeImage(Image *image)
3319 black_point=(double) image->columns*image->rows*0.0015;
3320 white_point=(double) image->columns*image->rows*0.9995;
3321 return(ContrastStretchImage(image,black_point,white_point));
3325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3329 % S i g m o i d a l C o n t r a s t I m a g e %
3333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3335 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3336 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3337 % sigmoidal transfer function without saturating highlights or shadows.
3338 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3339 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3340 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3341 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3344 % The format of the SigmoidalContrastImage method is:
3346 % MagickBooleanType SigmoidalContrastImage(Image *image,
3347 % const MagickBooleanType sharpen,const char *levels)
3349 % A description of each parameter follows:
3351 % o image: the image.
3353 % o sharpen: Increase or decrease image contrast.
3355 % o alpha: strength of the contrast, the larger the number the more
3356 % 'threshold-like' it becomes.
3358 % o beta: midpoint of the function as a color value 0 to QuantumRange.
3361 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3362 const MagickBooleanType sharpen,const double contrast,const double midpoint)
3364 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3388 Allocate and initialize sigmoidal maps.
3390 assert(image != (Image *) NULL);
3391 assert(image->signature == MagickSignature);
3392 if (image->debug != MagickFalse)
3393 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3394 sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
3395 sizeof(*sigmoidal_map));
3396 if (sigmoidal_map == (MagickRealType *) NULL)
3397 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3399 (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
3400 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3401 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3403 for (i=0; i <= (ssize_t) MaxMap; i++)
3405 if (sharpen != MagickFalse)
3407 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3408 (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
3409 (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/
3410 (double) QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/
3411 (double) QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/
3412 (double) QuantumRange)))))+0.5));
3415 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3416 (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/
3417 (double) QuantumRange*contrast))+((double) i/MaxMap)*((1.0/
3418 (1.0+exp(contrast*(midpoint/(double) QuantumRange-1.0))))-(1.0/
3419 (1.0+exp(midpoint/(double) QuantumRange*contrast))))))/
3420 (1.0/(1.0+exp(midpoint/(double) QuantumRange*contrast))+
3421 ((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/
3422 (double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/
3423 (double) QuantumRange*contrast))))))/contrast)));
3425 if (image->storage_class == PseudoClass)
3428 Sigmoidal-contrast enhance colormap.
3430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3431 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3433 for (i=0; i < (ssize_t) image->colors; i++)
3435 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3436 image->colormap[i].red=ClampToQuantum(sigmoidal_map[
3437 ScaleQuantumToMap(image->colormap[i].red)]);
3438 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3439 image->colormap[i].green=ClampToQuantum(sigmoidal_map[
3440 ScaleQuantumToMap(image->colormap[i].green)]);
3441 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3442 image->colormap[i].blue=ClampToQuantum(sigmoidal_map[
3443 ScaleQuantumToMap(image->colormap[i].blue)]);
3444 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3445 image->colormap[i].alpha=ClampToQuantum(sigmoidal_map[
3446 ScaleQuantumToMap(image->colormap[i].alpha)]);
3450 Sigmoidal-contrast enhance image.
3454 exception=(&image->exception);
3455 image_view=AcquireCacheView(image);
3456 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3457 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3459 for (y=0; y < (ssize_t) image->rows; y++)
3467 if (status == MagickFalse)
3469 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3470 if (q == (const Quantum *) NULL)
3475 for (x=0; x < (ssize_t) image->columns; x++)
3477 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3478 SetPixelRed(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3479 GetPixelRed(image,q))]),q);
3480 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3481 SetPixelGreen(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3482 GetPixelGreen(image,q))]),q);
3483 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3484 SetPixelBlue(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3485 GetPixelBlue(image,q))]),q);
3486 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3487 (image->colorspace == CMYKColorspace))
3488 SetPixelBlack(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3489 GetPixelBlack(image,q))]),q);
3490 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3491 SetPixelAlpha(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3492 GetPixelAlpha(image,q))]),q);
3493 q+=GetPixelChannels(image);
3495 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3497 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3502 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3503 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3505 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3507 if (proceed == MagickFalse)
3511 image_view=DestroyCacheView(image_view);
3512 sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);