MagickCore  7.0.7
Convert, Edit, Or Compose Bitmap Images
enhance.c
Go to the documentation of this file.
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
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 %
11 % %
12 % %
13 % MagickCore Image Enhancement Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://www.imagemagick.org/script/license.php %
27 % %
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. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/cache.h"
49 #include "MagickCore/cache-view.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
53 #include "MagickCore/colorspace.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
59 #include "MagickCore/fx.h"
60 #include "MagickCore/gem.h"
61 #include "MagickCore/gem-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/pixel.h"
72 #include "MagickCore/quantum.h"
74 #include "MagickCore/resample.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/statistic.h"
78 #include "MagickCore/string_.h"
81 #include "MagickCore/threshold.h"
82 #include "MagickCore/token.h"
83 #include "MagickCore/xml-tree.h"
85 
86 /*
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 % %
89 % %
90 % %
91 % A u t o G a m m a I m a g e %
92 % %
93 % %
94 % %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96 %
97 % AutoGammaImage() extract the 'mean' from the image and adjust the image
98 % to try make set its gamma appropriatally.
99 %
100 % The format of the AutoGammaImage method is:
101 %
102 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
103 %
104 % A description of each parameter follows:
105 %
106 % o image: The image to auto-level
107 %
108 % o exception: return any errors or warnings in this structure.
109 %
110 */
112  ExceptionInfo *exception)
113 {
114  double
115  gamma,
116  log_mean,
117  mean,
118  sans;
119 
121  status;
122 
123  register ssize_t
124  i;
125 
126  log_mean=log(0.5);
127  if (image->channel_mask == DefaultChannels)
128  {
129  /*
130  Apply gamma correction equally across all given channels.
131  */
132  (void) GetImageMean(image,&mean,&sans,exception);
133  gamma=log(mean*QuantumScale)/log_mean;
134  return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
135  }
136  /*
137  Auto-gamma each channel separately.
138  */
139  status=MagickTrue;
140  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
141  {
143  channel_mask;
144 
145  PixelChannel channel = GetPixelChannelChannel(image,i);
146  PixelTrait traits = GetPixelChannelTraits(image,channel);
147  if ((traits & UpdatePixelTrait) == 0)
148  continue;
149  channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
150  status=GetImageMean(image,&mean,&sans,exception);
151  gamma=log(mean*QuantumScale)/log_mean;
152  status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
153  (void) SetImageChannelMask(image,channel_mask);
154  if (status == MagickFalse)
155  break;
156  }
157  return(status != 0 ? MagickTrue : MagickFalse);
158 }
159 
160 /*
161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162 % %
163 % %
164 % %
165 % A u t o L e v e l I m a g e %
166 % %
167 % %
168 % %
169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
170 %
171 % AutoLevelImage() adjusts the levels of a particular image channel by
172 % scaling the minimum and maximum values to the full quantum range.
173 %
174 % The format of the LevelImage method is:
175 %
176 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
177 %
178 % A description of each parameter follows:
179 %
180 % o image: The image to auto-level
181 %
182 % o exception: return any errors or warnings in this structure.
183 %
184 */
186  ExceptionInfo *exception)
187 {
188  return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
189 }
190 
191 /*
192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 % %
194 % %
195 % %
196 % B r i g h t n e s s C o n t r a s t I m a g e %
197 % %
198 % %
199 % %
200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201 %
202 % BrightnessContrastImage() changes the brightness and/or contrast of an
203 % image. It converts the brightness and contrast parameters into slope and
204 % intercept and calls a polynomical function to apply to the image.
205 %
206 % The format of the BrightnessContrastImage method is:
207 %
208 % MagickBooleanType BrightnessContrastImage(Image *image,
209 % const double brightness,const double contrast,ExceptionInfo *exception)
210 %
211 % A description of each parameter follows:
212 %
213 % o image: the image.
214 %
215 % o brightness: the brightness percent (-100 .. 100).
216 %
217 % o contrast: the contrast percent (-100 .. 100).
218 %
219 % o exception: return any errors or warnings in this structure.
220 %
221 */
223  const double brightness,const double contrast,ExceptionInfo *exception)
224 {
225 #define BrightnessContastImageTag "BrightnessContast/Image"
226 
227  double
228  alpha,
229  coefficients[2],
230  intercept,
231  slope;
232 
234  status;
235 
236  /*
237  Compute slope and intercept.
238  */
239  assert(image != (Image *) NULL);
240  assert(image->signature == MagickCoreSignature);
241  if (image->debug != MagickFalse)
242  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
243  alpha=contrast;
244  slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
245  if (slope < 0.0)
246  slope=0.0;
247  intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
248  coefficients[0]=slope;
249  coefficients[1]=intercept;
250  status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
251  return(status);
252 }
253 
254 /*
255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256 % %
257 % %
258 % %
259 % C l u t I m a g e %
260 % %
261 % %
262 % %
263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264 %
265 % ClutImage() replaces each color value in the given image, by using it as an
266 % index to lookup a replacement color value in a Color Look UP Table in the
267 % form of an image. The values are extracted along a diagonal of the CLUT
268 % image so either a horizontal or vertial gradient image can be used.
269 %
270 % Typically this is used to either re-color a gray-scale image according to a
271 % color gradient in the CLUT image, or to perform a freeform histogram
272 % (level) adjustment according to the (typically gray-scale) gradient in the
273 % CLUT image.
274 %
275 % When the 'channel' mask includes the matte/alpha transparency channel but
276 % one image has no such channel it is assumed that that image is a simple
277 % gray-scale image that will effect the alpha channel values, either for
278 % gray-scale coloring (with transparent or semi-transparent colors), or
279 % a histogram adjustment of existing alpha channel values. If both images
280 % have matte channels, direct and normal indexing is applied, which is rarely
281 % used.
282 %
283 % The format of the ClutImage method is:
284 %
285 % MagickBooleanType ClutImage(Image *image,Image *clut_image,
286 % const PixelInterpolateMethod method,ExceptionInfo *exception)
287 %
288 % A description of each parameter follows:
289 %
290 % o image: the image, which is replaced by indexed CLUT values
291 %
292 % o clut_image: the color lookup table image for replacement color values.
293 %
294 % o method: the pixel interpolation method.
295 %
296 % o exception: return any errors or warnings in this structure.
297 %
298 */
300  const PixelInterpolateMethod method,ExceptionInfo *exception)
301 {
302 #define ClutImageTag "Clut/Image"
303 
304  CacheView
305  *clut_view,
306  *image_view;
307 
309  status;
310 
312  progress;
313 
314  PixelInfo
315  *clut_map;
316 
317  register ssize_t
318  i;
319 
320  ssize_t adjust,
321  y;
322 
323  assert(image != (Image *) NULL);
324  assert(image->signature == MagickCoreSignature);
325  if (image->debug != MagickFalse)
326  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
327  assert(clut_image != (Image *) NULL);
328  assert(clut_image->signature == MagickCoreSignature);
329  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
330  return(MagickFalse);
331  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
332  (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
333  (void) SetImageColorspace(image,sRGBColorspace,exception);
334  clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
335  if (clut_map == (PixelInfo *) NULL)
336  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
337  image->filename);
338  /*
339  Clut image.
340  */
341  status=MagickTrue;
342  progress=0;
343  adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
344  clut_view=AcquireVirtualCacheView(clut_image,exception);
345  for (i=0; i <= (ssize_t) MaxMap; i++)
346  {
347  GetPixelInfo(clut_image,clut_map+i);
348  status=InterpolatePixelInfo(clut_image,clut_view,method,
349  (double) i*(clut_image->columns-adjust)/MaxMap,(double) i*
350  (clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
351  if (status == MagickFalse)
352  break;
353  }
354  clut_view=DestroyCacheView(clut_view);
355  image_view=AcquireAuthenticCacheView(image,exception);
356 #if defined(MAGICKCORE_OPENMP_SUPPORT)
357  #pragma omp parallel for schedule(static) shared(progress,status) \
358  magick_number_threads(image,image,image->rows,1)
359 #endif
360  for (y=0; y < (ssize_t) image->rows; y++)
361  {
362  PixelInfo
363  pixel;
364 
365  register Quantum
366  *magick_restrict q;
367 
368  register ssize_t
369  x;
370 
371  if (status == MagickFalse)
372  continue;
373  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
374  if (q == (Quantum *) NULL)
375  {
376  status=MagickFalse;
377  continue;
378  }
379  GetPixelInfo(image,&pixel);
380  for (x=0; x < (ssize_t) image->columns; x++)
381  {
382  PixelTrait
383  traits;
384 
385  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
386  {
387  q+=GetPixelChannels(image);
388  continue;
389  }
390  GetPixelInfoPixel(image,q,&pixel);
392  if ((traits & UpdatePixelTrait) != 0)
393  pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
394  pixel.red))].red;
396  if ((traits & UpdatePixelTrait) != 0)
397  pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
398  pixel.green))].green;
400  if ((traits & UpdatePixelTrait) != 0)
401  pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
402  pixel.blue))].blue;
404  if ((traits & UpdatePixelTrait) != 0)
405  pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
406  pixel.black))].black;
408  if ((traits & UpdatePixelTrait) != 0)
409  pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
410  pixel.alpha))].alpha;
411  SetPixelViaPixelInfo(image,&pixel,q);
412  q+=GetPixelChannels(image);
413  }
414  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
415  status=MagickFalse;
416  if (image->progress_monitor != (MagickProgressMonitor) NULL)
417  {
419  proceed;
420 
421 #if defined(MAGICKCORE_OPENMP_SUPPORT)
422  #pragma omp critical (MagickCore_ClutImage)
423 #endif
424  proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
425  if (proceed == MagickFalse)
426  status=MagickFalse;
427  }
428  }
429  image_view=DestroyCacheView(image_view);
430  clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
431  if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
432  ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
433  (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
434  return(status);
435 }
436 
437 /*
438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439 % %
440 % %
441 % %
442 % C o l o r D e c i s i o n L i s t I m a g e %
443 % %
444 % %
445 % %
446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447 %
448 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
449 % (CCC) file which solely contains one or more color corrections and applies
450 % the correction to the image. Here is a sample CCC file:
451 %
452 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
453 % <ColorCorrection id="cc03345">
454 % <SOPNode>
455 % <Slope> 0.9 1.2 0.5 </Slope>
456 % <Offset> 0.4 -0.5 0.6 </Offset>
457 % <Power> 1.0 0.8 1.5 </Power>
458 % </SOPNode>
459 % <SATNode>
460 % <Saturation> 0.85 </Saturation>
461 % </SATNode>
462 % </ColorCorrection>
463 % </ColorCorrectionCollection>
464 %
465 % which includes the slop, offset, and power for each of the RGB channels
466 % as well as the saturation.
467 %
468 % The format of the ColorDecisionListImage method is:
469 %
470 % MagickBooleanType ColorDecisionListImage(Image *image,
471 % const char *color_correction_collection,ExceptionInfo *exception)
472 %
473 % A description of each parameter follows:
474 %
475 % o image: the image.
476 %
477 % o color_correction_collection: the color correction collection in XML.
478 %
479 % o exception: return any errors or warnings in this structure.
480 %
481 */
483  const char *color_correction_collection,ExceptionInfo *exception)
484 {
485 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
486 
487  typedef struct _Correction
488  {
489  double
490  slope,
491  offset,
492  power;
493  } Correction;
494 
495  typedef struct _ColorCorrection
496  {
497  Correction
498  red,
499  green,
500  blue;
501 
502  double
503  saturation;
504  } ColorCorrection;
505 
506  CacheView
507  *image_view;
508 
509  char
510  token[MagickPathExtent];
511 
512  ColorCorrection
513  color_correction;
514 
515  const char
516  *content,
517  *p;
518 
520  status;
521 
523  progress;
524 
525  PixelInfo
526  *cdl_map;
527 
528  register ssize_t
529  i;
530 
531  ssize_t
532  y;
533 
535  *cc,
536  *ccc,
537  *sat,
538  *sop;
539 
540  /*
541  Allocate and initialize cdl maps.
542  */
543  assert(image != (Image *) NULL);
544  assert(image->signature == MagickCoreSignature);
545  if (image->debug != MagickFalse)
546  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
547  if (color_correction_collection == (const char *) NULL)
548  return(MagickFalse);
549  ccc=NewXMLTree((const char *) color_correction_collection,exception);
550  if (ccc == (XMLTreeInfo *) NULL)
551  return(MagickFalse);
552  cc=GetXMLTreeChild(ccc,"ColorCorrection");
553  if (cc == (XMLTreeInfo *) NULL)
554  {
555  ccc=DestroyXMLTree(ccc);
556  return(MagickFalse);
557  }
558  color_correction.red.slope=1.0;
559  color_correction.red.offset=0.0;
560  color_correction.red.power=1.0;
561  color_correction.green.slope=1.0;
562  color_correction.green.offset=0.0;
563  color_correction.green.power=1.0;
564  color_correction.blue.slope=1.0;
565  color_correction.blue.offset=0.0;
566  color_correction.blue.power=1.0;
567  color_correction.saturation=0.0;
568  sop=GetXMLTreeChild(cc,"SOPNode");
569  if (sop != (XMLTreeInfo *) NULL)
570  {
572  *offset,
573  *power,
574  *slope;
575 
576  slope=GetXMLTreeChild(sop,"Slope");
577  if (slope != (XMLTreeInfo *) NULL)
578  {
579  content=GetXMLTreeContent(slope);
580  p=(const char *) content;
581  for (i=0; (*p != '\0') && (i < 3); i++)
582  {
583  GetNextToken(p,&p,MagickPathExtent,token);
584  if (*token == ',')
585  GetNextToken(p,&p,MagickPathExtent,token);
586  switch (i)
587  {
588  case 0:
589  {
590  color_correction.red.slope=StringToDouble(token,(char **) NULL);
591  break;
592  }
593  case 1:
594  {
595  color_correction.green.slope=StringToDouble(token,
596  (char **) NULL);
597  break;
598  }
599  case 2:
600  {
601  color_correction.blue.slope=StringToDouble(token,
602  (char **) NULL);
603  break;
604  }
605  }
606  }
607  }
608  offset=GetXMLTreeChild(sop,"Offset");
609  if (offset != (XMLTreeInfo *) NULL)
610  {
611  content=GetXMLTreeContent(offset);
612  p=(const char *) content;
613  for (i=0; (*p != '\0') && (i < 3); i++)
614  {
615  GetNextToken(p,&p,MagickPathExtent,token);
616  if (*token == ',')
617  GetNextToken(p,&p,MagickPathExtent,token);
618  switch (i)
619  {
620  case 0:
621  {
622  color_correction.red.offset=StringToDouble(token,
623  (char **) NULL);
624  break;
625  }
626  case 1:
627  {
628  color_correction.green.offset=StringToDouble(token,
629  (char **) NULL);
630  break;
631  }
632  case 2:
633  {
634  color_correction.blue.offset=StringToDouble(token,
635  (char **) NULL);
636  break;
637  }
638  }
639  }
640  }
641  power=GetXMLTreeChild(sop,"Power");
642  if (power != (XMLTreeInfo *) NULL)
643  {
644  content=GetXMLTreeContent(power);
645  p=(const char *) content;
646  for (i=0; (*p != '\0') && (i < 3); i++)
647  {
648  GetNextToken(p,&p,MagickPathExtent,token);
649  if (*token == ',')
650  GetNextToken(p,&p,MagickPathExtent,token);
651  switch (i)
652  {
653  case 0:
654  {
655  color_correction.red.power=StringToDouble(token,(char **) NULL);
656  break;
657  }
658  case 1:
659  {
660  color_correction.green.power=StringToDouble(token,
661  (char **) NULL);
662  break;
663  }
664  case 2:
665  {
666  color_correction.blue.power=StringToDouble(token,
667  (char **) NULL);
668  break;
669  }
670  }
671  }
672  }
673  }
674  sat=GetXMLTreeChild(cc,"SATNode");
675  if (sat != (XMLTreeInfo *) NULL)
676  {
678  *saturation;
679 
680  saturation=GetXMLTreeChild(sat,"Saturation");
681  if (saturation != (XMLTreeInfo *) NULL)
682  {
683  content=GetXMLTreeContent(saturation);
684  p=(const char *) content;
685  GetNextToken(p,&p,MagickPathExtent,token);
686  color_correction.saturation=StringToDouble(token,(char **) NULL);
687  }
688  }
689  ccc=DestroyXMLTree(ccc);
690  if (image->debug != MagickFalse)
691  {
693  " Color Correction Collection:");
695  " color_correction.red.slope: %g",color_correction.red.slope);
697  " color_correction.red.offset: %g",color_correction.red.offset);
699  " color_correction.red.power: %g",color_correction.red.power);
701  " color_correction.green.slope: %g",color_correction.green.slope);
703  " color_correction.green.offset: %g",color_correction.green.offset);
705  " color_correction.green.power: %g",color_correction.green.power);
707  " color_correction.blue.slope: %g",color_correction.blue.slope);
709  " color_correction.blue.offset: %g",color_correction.blue.offset);
711  " color_correction.blue.power: %g",color_correction.blue.power);
713  " color_correction.saturation: %g",color_correction.saturation);
714  }
715  cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
716  if (cdl_map == (PixelInfo *) NULL)
717  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
718  image->filename);
719  for (i=0; i <= (ssize_t) MaxMap; i++)
720  {
721  cdl_map[i].red=(double) ScaleMapToQuantum((double)
722  (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
723  color_correction.red.offset,color_correction.red.power))));
724  cdl_map[i].green=(double) ScaleMapToQuantum((double)
725  (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
726  color_correction.green.offset,color_correction.green.power))));
727  cdl_map[i].blue=(double) ScaleMapToQuantum((double)
728  (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
729  color_correction.blue.offset,color_correction.blue.power))));
730  }
731  if (image->storage_class == PseudoClass)
732  for (i=0; i < (ssize_t) image->colors; i++)
733  {
734  /*
735  Apply transfer function to colormap.
736  */
737  double
738  luma;
739 
740  luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
741  0.07217f*image->colormap[i].blue;
742  image->colormap[i].red=luma+color_correction.saturation*cdl_map[
743  ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
744  image->colormap[i].green=luma+color_correction.saturation*cdl_map[
745  ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
746  image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
747  ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
748  }
749  /*
750  Apply transfer function to image.
751  */
752  status=MagickTrue;
753  progress=0;
754  image_view=AcquireAuthenticCacheView(image,exception);
755 #if defined(MAGICKCORE_OPENMP_SUPPORT)
756  #pragma omp parallel for schedule(static) shared(progress,status) \
757  magick_number_threads(image,image,image->rows,1)
758 #endif
759  for (y=0; y < (ssize_t) image->rows; y++)
760  {
761  double
762  luma;
763 
764  register Quantum
765  *magick_restrict q;
766 
767  register ssize_t
768  x;
769 
770  if (status == MagickFalse)
771  continue;
772  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
773  if (q == (Quantum *) NULL)
774  {
775  status=MagickFalse;
776  continue;
777  }
778  for (x=0; x < (ssize_t) image->columns; x++)
779  {
780  luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
781  0.07217f*GetPixelBlue(image,q);
782  SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
783  (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
784  SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
785  (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
786  SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
787  (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
788  q+=GetPixelChannels(image);
789  }
790  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
791  status=MagickFalse;
792  if (image->progress_monitor != (MagickProgressMonitor) NULL)
793  {
795  proceed;
796 
797 #if defined(MAGICKCORE_OPENMP_SUPPORT)
798  #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
799 #endif
801  progress++,image->rows);
802  if (proceed == MagickFalse)
803  status=MagickFalse;
804  }
805  }
806  image_view=DestroyCacheView(image_view);
807  cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
808  return(status);
809 }
810 
811 /*
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 % %
814 % %
815 % %
816 % C o n t r a s t I m a g e %
817 % %
818 % %
819 % %
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 %
822 % ContrastImage() enhances the intensity differences between the lighter and
823 % darker elements of the image. Set sharpen to a MagickTrue to increase the
824 % image contrast otherwise the contrast is reduced.
825 %
826 % The format of the ContrastImage method is:
827 %
828 % MagickBooleanType ContrastImage(Image *image,
829 % const MagickBooleanType sharpen,ExceptionInfo *exception)
830 %
831 % A description of each parameter follows:
832 %
833 % o image: the image.
834 %
835 % o sharpen: Increase or decrease image contrast.
836 %
837 % o exception: return any errors or warnings in this structure.
838 %
839 */
840 
841 static void Contrast(const int sign,double *red,double *green,double *blue)
842 {
843  double
844  brightness,
845  hue,
846  saturation;
847 
848  /*
849  Enhance contrast: dark color become darker, light color become lighter.
850  */
851  assert(red != (double *) NULL);
852  assert(green != (double *) NULL);
853  assert(blue != (double *) NULL);
854  hue=0.0;
855  saturation=0.0;
856  brightness=0.0;
857  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
858  brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
859  brightness);
860  if (brightness > 1.0)
861  brightness=1.0;
862  else
863  if (brightness < 0.0)
864  brightness=0.0;
865  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
866 }
867 
869  const MagickBooleanType sharpen,ExceptionInfo *exception)
870 {
871 #define ContrastImageTag "Contrast/Image"
872 
873  CacheView
874  *image_view;
875 
876  int
877  sign;
878 
880  status;
881 
883  progress;
884 
885  register ssize_t
886  i;
887 
888  ssize_t
889  y;
890 
891  assert(image != (Image *) NULL);
892  assert(image->signature == MagickCoreSignature);
893 #if defined(MAGICKCORE_OPENCL_SUPPORT)
894  if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
895  return(MagickTrue);
896 #endif
897  if (image->debug != MagickFalse)
898  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
899  sign=sharpen != MagickFalse ? 1 : -1;
900  if (image->storage_class == PseudoClass)
901  {
902  /*
903  Contrast enhance colormap.
904  */
905  for (i=0; i < (ssize_t) image->colors; i++)
906  {
907  double
908  blue,
909  green,
910  red;
911 
912  red=(double) image->colormap[i].red;
913  green=(double) image->colormap[i].green;
914  blue=(double) image->colormap[i].blue;
915  Contrast(sign,&red,&green,&blue);
916  image->colormap[i].red=(MagickRealType) red;
917  image->colormap[i].green=(MagickRealType) green;
918  image->colormap[i].blue=(MagickRealType) blue;
919  }
920  }
921  /*
922  Contrast enhance image.
923  */
924  status=MagickTrue;
925  progress=0;
926  image_view=AcquireAuthenticCacheView(image,exception);
927 #if defined(MAGICKCORE_OPENMP_SUPPORT)
928  #pragma omp parallel for schedule(static) shared(progress,status) \
929  magick_number_threads(image,image,image->rows,1)
930 #endif
931  for (y=0; y < (ssize_t) image->rows; y++)
932  {
933  double
934  blue,
935  green,
936  red;
937 
938  register Quantum
939  *magick_restrict q;
940 
941  register ssize_t
942  x;
943 
944  if (status == MagickFalse)
945  continue;
946  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
947  if (q == (Quantum *) NULL)
948  {
949  status=MagickFalse;
950  continue;
951  }
952  for (x=0; x < (ssize_t) image->columns; x++)
953  {
954  red=(double) GetPixelRed(image,q);
955  green=(double) GetPixelGreen(image,q);
956  blue=(double) GetPixelBlue(image,q);
957  Contrast(sign,&red,&green,&blue);
958  SetPixelRed(image,ClampToQuantum(red),q);
959  SetPixelGreen(image,ClampToQuantum(green),q);
960  SetPixelBlue(image,ClampToQuantum(blue),q);
961  q+=GetPixelChannels(image);
962  }
963  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
964  status=MagickFalse;
965  if (image->progress_monitor != (MagickProgressMonitor) NULL)
966  {
968  proceed;
969 
970 #if defined(MAGICKCORE_OPENMP_SUPPORT)
971  #pragma omp critical (MagickCore_ContrastImage)
972 #endif
973  proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
974  if (proceed == MagickFalse)
975  status=MagickFalse;
976  }
977  }
978  image_view=DestroyCacheView(image_view);
979  return(status);
980 }
981 
982 /*
983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 % %
985 % %
986 % %
987 % C o n t r a s t S t r e t c h I m a g e %
988 % %
989 % %
990 % %
991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
992 %
993 % ContrastStretchImage() is a simple image enhancement technique that attempts
994 % to improve the contrast in an image by 'stretching' the range of intensity
995 % values it contains to span a desired range of values. It differs from the
996 % more sophisticated histogram equalization in that it can only apply a
997 % linear scaling function to the image pixel values. As a result the
998 % 'enhancement' is less harsh.
999 %
1000 % The format of the ContrastStretchImage method is:
1001 %
1002 % MagickBooleanType ContrastStretchImage(Image *image,
1003 % const char *levels,ExceptionInfo *exception)
1004 %
1005 % A description of each parameter follows:
1006 %
1007 % o image: the image.
1008 %
1009 % o black_point: the black point.
1010 %
1011 % o white_point: the white point.
1012 %
1013 % o levels: Specify the levels where the black and white points have the
1014 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
1015 %
1016 % o exception: return any errors or warnings in this structure.
1017 %
1018 */
1020  const double black_point,const double white_point,ExceptionInfo *exception)
1021 {
1022 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
1023 #define ContrastStretchImageTag "ContrastStretch/Image"
1024 
1025  CacheView
1026  *image_view;
1027 
1028  double
1029  *black,
1030  *histogram,
1031  *stretch_map,
1032  *white;
1033 
1035  status;
1036 
1038  progress;
1039 
1040  register ssize_t
1041  i;
1042 
1043  ssize_t
1044  y;
1045 
1046  /*
1047  Allocate histogram and stretch map.
1048  */
1049  assert(image != (Image *) NULL);
1050  assert(image->signature == MagickCoreSignature);
1051  if (image->debug != MagickFalse)
1052  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1053  if (SetImageGray(image,exception) != MagickFalse)
1054  (void) SetImageColorspace(image,GRAYColorspace,exception);
1055  black=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*black));
1056  white=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*white));
1057  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1058  sizeof(*histogram));
1059  stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1060  sizeof(*stretch_map));
1061  if ((black == (double *) NULL) || (white == (double *) NULL) ||
1062  (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1063  {
1064  if (stretch_map != (double *) NULL)
1065  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1066  if (histogram != (double *) NULL)
1067  histogram=(double *) RelinquishMagickMemory(histogram);
1068  if (white != (double *) NULL)
1069  white=(double *) RelinquishMagickMemory(white);
1070  if (black != (double *) NULL)
1071  black=(double *) RelinquishMagickMemory(black);
1072  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1073  image->filename);
1074  }
1075  /*
1076  Form histogram.
1077  */
1078  status=MagickTrue;
1079  (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1080  sizeof(*histogram));
1081  image_view=AcquireVirtualCacheView(image,exception);
1082  for (y=0; y < (ssize_t) image->rows; y++)
1083  {
1084  register const Quantum
1085  *magick_restrict p;
1086 
1087  register ssize_t
1088  x;
1089 
1090  if (status == MagickFalse)
1091  continue;
1092  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1093  if (p == (const Quantum *) NULL)
1094  {
1095  status=MagickFalse;
1096  continue;
1097  }
1098  for (x=0; x < (ssize_t) image->columns; x++)
1099  {
1100  double
1101  pixel;
1102 
1103  pixel=GetPixelIntensity(image,p);
1104  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1105  {
1106  if (image->channel_mask != DefaultChannels)
1107  pixel=(double) p[i];
1108  histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1109  ClampToQuantum(pixel))+i]++;
1110  }
1111  p+=GetPixelChannels(image);
1112  }
1113  }
1114  image_view=DestroyCacheView(image_view);
1115  /*
1116  Find the histogram boundaries by locating the black/white levels.
1117  */
1118  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1119  {
1120  double
1121  intensity;
1122 
1123  register ssize_t
1124  j;
1125 
1126  black[i]=0.0;
1127  white[i]=MaxRange(QuantumRange);
1128  intensity=0.0;
1129  for (j=0; j <= (ssize_t) MaxMap; j++)
1130  {
1131  intensity+=histogram[GetPixelChannels(image)*j+i];
1132  if (intensity > black_point)
1133  break;
1134  }
1135  black[i]=(double) j;
1136  intensity=0.0;
1137  for (j=(ssize_t) MaxMap; j != 0; j--)
1138  {
1139  intensity+=histogram[GetPixelChannels(image)*j+i];
1140  if (intensity > ((double) image->columns*image->rows-white_point))
1141  break;
1142  }
1143  white[i]=(double) j;
1144  }
1145  histogram=(double *) RelinquishMagickMemory(histogram);
1146  /*
1147  Stretch the histogram to create the stretched image mapping.
1148  */
1149  (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1150  sizeof(*stretch_map));
1151  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1152  {
1153  register ssize_t
1154  j;
1155 
1156  for (j=0; j <= (ssize_t) MaxMap; j++)
1157  {
1158  double
1159  gamma;
1160 
1161  gamma=PerceptibleReciprocal(white[i]-black[i]);
1162  if (j < (ssize_t) black[i])
1163  stretch_map[GetPixelChannels(image)*j+i]=0.0;
1164  else
1165  if (j > (ssize_t) white[i])
1166  stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1167  else
1168  if (black[i] != white[i])
1169  stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1170  (double) (MaxMap*gamma*(j-black[i])));
1171  }
1172  }
1173  if (image->storage_class == PseudoClass)
1174  {
1175  register ssize_t
1176  j;
1177 
1178  /*
1179  Stretch-contrast colormap.
1180  */
1181  for (j=0; j < (ssize_t) image->colors; j++)
1182  {
1183  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1184  {
1186  image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1187  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1188  }
1189  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1190  {
1192  image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1193  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1194  }
1195  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1196  {
1198  image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1199  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1200  }
1201  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1202  {
1204  image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1205  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1206  }
1207  }
1208  }
1209  /*
1210  Stretch-contrast image.
1211  */
1212  status=MagickTrue;
1213  progress=0;
1214  image_view=AcquireAuthenticCacheView(image,exception);
1215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1216  #pragma omp parallel for schedule(static) shared(progress,status) \
1217  magick_number_threads(image,image,image->rows,1)
1218 #endif
1219  for (y=0; y < (ssize_t) image->rows; y++)
1220  {
1221  register Quantum
1222  *magick_restrict q;
1223 
1224  register ssize_t
1225  x;
1226 
1227  if (status == MagickFalse)
1228  continue;
1229  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1230  if (q == (Quantum *) NULL)
1231  {
1232  status=MagickFalse;
1233  continue;
1234  }
1235  for (x=0; x < (ssize_t) image->columns; x++)
1236  {
1237  register ssize_t
1238  j;
1239 
1240  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1241  {
1242  q+=GetPixelChannels(image);
1243  continue;
1244  }
1245  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1246  {
1247  PixelChannel channel = GetPixelChannelChannel(image,j);
1248  PixelTrait traits = GetPixelChannelTraits(image,channel);
1249  if ((traits & UpdatePixelTrait) == 0)
1250  continue;
1251  if (black[j] == white[j])
1252  continue;
1253  q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1254  ScaleQuantumToMap(q[j])+j]);
1255  }
1256  q+=GetPixelChannels(image);
1257  }
1258  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1259  status=MagickFalse;
1260  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1261  {
1263  proceed;
1264 
1265 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1266  #pragma omp critical (MagickCore_ContrastStretchImage)
1267 #endif
1268  proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1269  image->rows);
1270  if (proceed == MagickFalse)
1271  status=MagickFalse;
1272  }
1273  }
1274  image_view=DestroyCacheView(image_view);
1275  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1276  white=(double *) RelinquishMagickMemory(white);
1277  black=(double *) RelinquishMagickMemory(black);
1278  return(status);
1279 }
1280 
1281 /*
1282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1283 % %
1284 % %
1285 % %
1286 % E n h a n c e I m a g e %
1287 % %
1288 % %
1289 % %
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 %
1292 % EnhanceImage() applies a digital filter that improves the quality of a
1293 % noisy image.
1294 %
1295 % The format of the EnhanceImage method is:
1296 %
1297 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1298 %
1299 % A description of each parameter follows:
1300 %
1301 % o image: the image.
1302 %
1303 % o exception: return any errors or warnings in this structure.
1304 %
1305 */
1307 {
1308 #define EnhanceImageTag "Enhance/Image"
1309 #define EnhancePixel(weight) \
1310  mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1311  distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1312  distance_squared=(4.0+mean)*distance*distance; \
1313  mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1314  distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1315  distance_squared+=(7.0-mean)*distance*distance; \
1316  mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1317  distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1318  distance_squared+=(5.0-mean)*distance*distance; \
1319  mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1320  distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1321  distance_squared+=(5.0-mean)*distance*distance; \
1322  mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1323  distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1324  distance_squared+=(5.0-mean)*distance*distance; \
1325  if (distance_squared < 0.069) \
1326  { \
1327  aggregate.red+=(weight)*GetPixelRed(image,r); \
1328  aggregate.green+=(weight)*GetPixelGreen(image,r); \
1329  aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1330  aggregate.black+=(weight)*GetPixelBlack(image,r); \
1331  aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1332  total_weight+=(weight); \
1333  } \
1334  r+=GetPixelChannels(image);
1335 
1336  CacheView
1337  *enhance_view,
1338  *image_view;
1339 
1340  Image
1341  *enhance_image;
1342 
1344  status;
1345 
1347  progress;
1348 
1349  ssize_t
1350  y;
1351 
1352  /*
1353  Initialize enhanced image attributes.
1354  */
1355  assert(image != (const Image *) NULL);
1356  assert(image->signature == MagickCoreSignature);
1357  if (image->debug != MagickFalse)
1358  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1359  assert(exception != (ExceptionInfo *) NULL);
1360  assert(exception->signature == MagickCoreSignature);
1361  enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1362  exception);
1363  if (enhance_image == (Image *) NULL)
1364  return((Image *) NULL);
1365  if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1366  {
1367  enhance_image=DestroyImage(enhance_image);
1368  return((Image *) NULL);
1369  }
1370  /*
1371  Enhance image.
1372  */
1373  status=MagickTrue;
1374  progress=0;
1375  image_view=AcquireVirtualCacheView(image,exception);
1376  enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1377 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1378  #pragma omp parallel for schedule(static) shared(progress,status) \
1379  magick_number_threads(image,enhance_image,image->rows,1)
1380 #endif
1381  for (y=0; y < (ssize_t) image->rows; y++)
1382  {
1383  PixelInfo
1384  pixel;
1385 
1386  register const Quantum
1387  *magick_restrict p;
1388 
1389  register Quantum
1390  *magick_restrict q;
1391 
1392  register ssize_t
1393  x;
1394 
1395  ssize_t
1396  center;
1397 
1398  if (status == MagickFalse)
1399  continue;
1400  p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1401  q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1402  exception);
1403  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1404  {
1405  status=MagickFalse;
1406  continue;
1407  }
1408  center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1409  GetPixelInfo(image,&pixel);
1410  for (x=0; x < (ssize_t) image->columns; x++)
1411  {
1412  double
1413  distance,
1414  distance_squared,
1415  mean,
1416  total_weight;
1417 
1418  PixelInfo
1419  aggregate;
1420 
1421  register const Quantum
1422  *magick_restrict r;
1423 
1424  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
1425  {
1426  SetPixelBackgoundColor(enhance_image,q);
1427  p+=GetPixelChannels(image);
1428  q+=GetPixelChannels(enhance_image);
1429  continue;
1430  }
1431  GetPixelInfo(image,&aggregate);
1432  total_weight=0.0;
1433  GetPixelInfoPixel(image,p+center,&pixel);
1434  r=p;
1435  EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1436  EnhancePixel(8.0); EnhancePixel(5.0);
1437  r=p+GetPixelChannels(image)*(image->columns+4);
1438  EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1439  EnhancePixel(20.0); EnhancePixel(8.0);
1440  r=p+2*GetPixelChannels(image)*(image->columns+4);
1441  EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1442  EnhancePixel(40.0); EnhancePixel(10.0);
1443  r=p+3*GetPixelChannels(image)*(image->columns+4);
1444  EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1445  EnhancePixel(20.0); EnhancePixel(8.0);
1446  r=p+4*GetPixelChannels(image)*(image->columns+4);
1447  EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1448  EnhancePixel(8.0); EnhancePixel(5.0);
1449  if (total_weight > MagickEpsilon)
1450  {
1451  pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1452  pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1453  pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1454  pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1455  pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1456  }
1457  SetPixelViaPixelInfo(image,&pixel,q);
1458  p+=GetPixelChannels(image);
1459  q+=GetPixelChannels(enhance_image);
1460  }
1461  if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1462  status=MagickFalse;
1463  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1464  {
1466  proceed;
1467 
1468 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1469  #pragma omp critical (MagickCore_EnhanceImage)
1470 #endif
1471  proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1472  if (proceed == MagickFalse)
1473  status=MagickFalse;
1474  }
1475  }
1476  enhance_view=DestroyCacheView(enhance_view);
1477  image_view=DestroyCacheView(image_view);
1478  if (status == MagickFalse)
1479  enhance_image=DestroyImage(enhance_image);
1480  return(enhance_image);
1481 }
1482 
1483 /*
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485 % %
1486 % %
1487 % %
1488 % E q u a l i z e I m a g e %
1489 % %
1490 % %
1491 % %
1492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493 %
1494 % EqualizeImage() applies a histogram equalization to the image.
1495 %
1496 % The format of the EqualizeImage method is:
1497 %
1498 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1499 %
1500 % A description of each parameter follows:
1501 %
1502 % o image: the image.
1503 %
1504 % o exception: return any errors or warnings in this structure.
1505 %
1506 */
1508  ExceptionInfo *exception)
1509 {
1510 #define EqualizeImageTag "Equalize/Image"
1511 
1512  CacheView
1513  *image_view;
1514 
1515  double
1516  black[CompositePixelChannel+1],
1517  *equalize_map,
1518  *histogram,
1519  *map,
1520  white[CompositePixelChannel+1];
1521 
1523  status;
1524 
1526  progress;
1527 
1528  register ssize_t
1529  i;
1530 
1531  ssize_t
1532  y;
1533 
1534  /*
1535  Allocate and initialize histogram arrays.
1536  */
1537  assert(image != (Image *) NULL);
1538  assert(image->signature == MagickCoreSignature);
1539 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1540  if (AccelerateEqualizeImage(image,exception) != MagickFalse)
1541  return(MagickTrue);
1542 #endif
1543  if (image->debug != MagickFalse)
1544  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1545  equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1546  sizeof(*equalize_map));
1547  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1548  sizeof(*histogram));
1549  map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*sizeof(*map));
1550  if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1551  (map == (double *) NULL))
1552  {
1553  if (map != (double *) NULL)
1554  map=(double *) RelinquishMagickMemory(map);
1555  if (histogram != (double *) NULL)
1556  histogram=(double *) RelinquishMagickMemory(histogram);
1557  if (equalize_map != (double *) NULL)
1558  equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1559  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1560  image->filename);
1561  }
1562  /*
1563  Form histogram.
1564  */
1565  status=MagickTrue;
1566  (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1567  sizeof(*histogram));
1568  image_view=AcquireVirtualCacheView(image,exception);
1569  for (y=0; y < (ssize_t) image->rows; y++)
1570  {
1571  register const Quantum
1572  *magick_restrict p;
1573 
1574  register ssize_t
1575  x;
1576 
1577  if (status == MagickFalse)
1578  continue;
1579  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1580  if (p == (const Quantum *) NULL)
1581  {
1582  status=MagickFalse;
1583  continue;
1584  }
1585  for (x=0; x < (ssize_t) image->columns; x++)
1586  {
1587  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1588  {
1589  double
1590  intensity;
1591 
1592  intensity=(double) p[i];
1593  if ((image->channel_mask & SyncChannels) != 0)
1594  intensity=GetPixelIntensity(image,p);
1595  histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1596  ClampToQuantum(intensity))+i]++;
1597  }
1598  p+=GetPixelChannels(image);
1599  }
1600  }
1601  image_view=DestroyCacheView(image_view);
1602  /*
1603  Integrate the histogram to get the equalization map.
1604  */
1605  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1606  {
1607  double
1608  intensity;
1609 
1610  register ssize_t
1611  j;
1612 
1613  intensity=0.0;
1614  for (j=0; j <= (ssize_t) MaxMap; j++)
1615  {
1616  intensity+=histogram[GetPixelChannels(image)*j+i];
1617  map[GetPixelChannels(image)*j+i]=intensity;
1618  }
1619  }
1620  (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1621  sizeof(*equalize_map));
1622  (void) memset(black,0,sizeof(*black));
1623  (void) memset(white,0,sizeof(*white));
1624  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1625  {
1626  register ssize_t
1627  j;
1628 
1629  black[i]=map[i];
1630  white[i]=map[GetPixelChannels(image)*MaxMap+i];
1631  if (black[i] != white[i])
1632  for (j=0; j <= (ssize_t) MaxMap; j++)
1633  equalize_map[GetPixelChannels(image)*j+i]=(double)
1634  ScaleMapToQuantum((double) ((MaxMap*(map[
1635  GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1636  }
1637  histogram=(double *) RelinquishMagickMemory(histogram);
1638  map=(double *) RelinquishMagickMemory(map);
1639  if (image->storage_class == PseudoClass)
1640  {
1641  register ssize_t
1642  j;
1643 
1644  /*
1645  Equalize colormap.
1646  */
1647  for (j=0; j < (ssize_t) image->colors; j++)
1648  {
1649  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1650  {
1652  if (black[channel] != white[channel])
1653  image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1654  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+
1655  channel];
1656  }
1657  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1658  {
1659  PixelChannel channel = GetPixelChannelChannel(image,
1661  if (black[channel] != white[channel])
1662  image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1663  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+
1664  channel];
1665  }
1666  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1667  {
1669  if (black[channel] != white[channel])
1670  image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1671  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+
1672  channel];
1673  }
1674  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1675  {
1676  PixelChannel channel = GetPixelChannelChannel(image,
1678  if (black[channel] != white[channel])
1679  image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1680  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+
1681  channel];
1682  }
1683  }
1684  }
1685  /*
1686  Equalize image.
1687  */
1688  progress=0;
1689  image_view=AcquireAuthenticCacheView(image,exception);
1690 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1691  #pragma omp parallel for schedule(static) shared(progress,status) \
1692  magick_number_threads(image,image,image->rows,1)
1693 #endif
1694  for (y=0; y < (ssize_t) image->rows; y++)
1695  {
1696  register Quantum
1697  *magick_restrict q;
1698 
1699  register ssize_t
1700  x;
1701 
1702  if (status == MagickFalse)
1703  continue;
1704  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1705  if (q == (Quantum *) NULL)
1706  {
1707  status=MagickFalse;
1708  continue;
1709  }
1710  for (x=0; x < (ssize_t) image->columns; x++)
1711  {
1712  register ssize_t
1713  j;
1714 
1715  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1716  {
1717  q+=GetPixelChannels(image);
1718  continue;
1719  }
1720  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1721  {
1722  PixelChannel channel = GetPixelChannelChannel(image,j);
1723  PixelTrait traits = GetPixelChannelTraits(image,channel);
1724  if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
1725  continue;
1726  q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1727  ScaleQuantumToMap(q[j])+j]);
1728  }
1729  q+=GetPixelChannels(image);
1730  }
1731  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1732  status=MagickFalse;
1733  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1734  {
1736  proceed;
1737 
1738 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1739  #pragma omp critical (MagickCore_EqualizeImage)
1740 #endif
1741  proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1742  if (proceed == MagickFalse)
1743  status=MagickFalse;
1744  }
1745  }
1746  image_view=DestroyCacheView(image_view);
1747  equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1748  return(status);
1749 }
1750 
1751 /*
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753 % %
1754 % %
1755 % %
1756 % G a m m a I m a g e %
1757 % %
1758 % %
1759 % %
1760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1761 %
1762 % GammaImage() gamma-corrects a particular image channel. The same
1763 % image viewed on different devices will have perceptual differences in the
1764 % way the image's intensities are represented on the screen. Specify
1765 % individual gamma levels for the red, green, and blue channels, or adjust
1766 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1767 %
1768 % You can also reduce the influence of a particular channel with a gamma
1769 % value of 0.
1770 %
1771 % The format of the GammaImage method is:
1772 %
1773 % MagickBooleanType GammaImage(Image *image,const double gamma,
1774 % ExceptionInfo *exception)
1775 %
1776 % A description of each parameter follows:
1777 %
1778 % o image: the image.
1779 %
1780 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1781 %
1782 % o gamma: the image gamma.
1783 %
1784 */
1785 
1786 static inline double gamma_pow(const double value,const double gamma)
1787 {
1788  return(value < 0.0 ? value : pow(value,gamma));
1789 }
1790 
1792  ExceptionInfo *exception)
1793 {
1794 #define GammaCorrectImageTag "GammaCorrect/Image"
1795 
1796  CacheView
1797  *image_view;
1798 
1800  status;
1801 
1803  progress;
1804 
1805  Quantum
1806  *gamma_map;
1807 
1808  register ssize_t
1809  i;
1810 
1811  ssize_t
1812  y;
1813 
1814  /*
1815  Allocate and initialize gamma maps.
1816  */
1817  assert(image != (Image *) NULL);
1818  assert(image->signature == MagickCoreSignature);
1819  if (image->debug != MagickFalse)
1820  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1821  if (gamma == 1.0)
1822  return(MagickTrue);
1823  gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1824  if (gamma_map == (Quantum *) NULL)
1825  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1826  image->filename);
1827  (void) memset(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1828  if (gamma != 0.0)
1829  for (i=0; i <= (ssize_t) MaxMap; i++)
1830  gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1831  MaxMap,1.0/gamma)));
1832  if (image->storage_class == PseudoClass)
1833  for (i=0; i < (ssize_t) image->colors; i++)
1834  {
1835  /*
1836  Gamma-correct colormap.
1837  */
1838 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1839  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1840  image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
1841  ClampToQuantum(image->colormap[i].red))];
1842  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1843  image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
1844  ClampToQuantum(image->colormap[i].green))];
1845  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1846  image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
1847  ClampToQuantum(image->colormap[i].blue))];
1848  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1849  image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
1850  ClampToQuantum(image->colormap[i].alpha))];
1851 #else
1852  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1854  image->colormap[i].red,1.0/gamma);
1855  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1857  image->colormap[i].green,1.0/gamma);
1858  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1860  image->colormap[i].blue,1.0/gamma);
1861  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1863  image->colormap[i].alpha,1.0/gamma);
1864 #endif
1865  }
1866  /*
1867  Gamma-correct image.
1868  */
1869  status=MagickTrue;
1870  progress=0;
1871  image_view=AcquireAuthenticCacheView(image,exception);
1872 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1873  #pragma omp parallel for schedule(static) shared(progress,status) \
1874  magick_number_threads(image,image,image->rows,1)
1875 #endif
1876  for (y=0; y < (ssize_t) image->rows; y++)
1877  {
1878  register Quantum
1879  *magick_restrict q;
1880 
1881  register ssize_t
1882  x;
1883 
1884  if (status == MagickFalse)
1885  continue;
1886  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1887  if (q == (Quantum *) NULL)
1888  {
1889  status=MagickFalse;
1890  continue;
1891  }
1892  for (x=0; x < (ssize_t) image->columns; x++)
1893  {
1894  register ssize_t
1895  j;
1896 
1897  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1898  {
1899  q+=GetPixelChannels(image);
1900  continue;
1901  }
1902  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1903  {
1904  PixelChannel channel = GetPixelChannelChannel(image,j);
1905  PixelTrait traits = GetPixelChannelTraits(image,channel);
1906  if ((traits & UpdatePixelTrait) == 0)
1907  continue;
1908 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1909  q[j]=gamma_map[ScaleQuantumToMap(q[j])];
1910 #else
1911  q[j]=QuantumRange*gamma_pow(QuantumScale*q[j],1.0/gamma);
1912 #endif
1913  }
1914  q+=GetPixelChannels(image);
1915  }
1916  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1917  status=MagickFalse;
1918  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1919  {
1921  proceed;
1922 
1923 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1924  #pragma omp critical (MagickCore_GammaImage)
1925 #endif
1926  proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1927  image->rows);
1928  if (proceed == MagickFalse)
1929  status=MagickFalse;
1930  }
1931  }
1932  image_view=DestroyCacheView(image_view);
1933  gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1934  if (image->gamma != 0.0)
1935  image->gamma*=gamma;
1936  return(status);
1937 }
1938 
1939 /*
1940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941 % %
1942 % %
1943 % %
1944 % G r a y s c a l e I m a g e %
1945 % %
1946 % %
1947 % %
1948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1949 %
1950 % GrayscaleImage() converts the image to grayscale.
1951 %
1952 % The format of the GrayscaleImage method is:
1953 %
1954 % MagickBooleanType GrayscaleImage(Image *image,
1955 % const PixelIntensityMethod method ,ExceptionInfo *exception)
1956 %
1957 % A description of each parameter follows:
1958 %
1959 % o image: the image.
1960 %
1961 % o method: the pixel intensity method.
1962 %
1963 % o exception: return any errors or warnings in this structure.
1964 %
1965 */
1967  const PixelIntensityMethod method,ExceptionInfo *exception)
1968 {
1969 #define GrayscaleImageTag "Grayscale/Image"
1970 
1971  CacheView
1972  *image_view;
1973 
1975  status;
1976 
1978  progress;
1979 
1980  ssize_t
1981  y;
1982 
1983  assert(image != (Image *) NULL);
1984  assert(image->signature == MagickCoreSignature);
1985  if (image->debug != MagickFalse)
1986  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1987  if (image->storage_class == PseudoClass)
1988  {
1989  if (SyncImage(image,exception) == MagickFalse)
1990  return(MagickFalse);
1991  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1992  return(MagickFalse);
1993  }
1994 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1995  if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
1996  {
1997  image->intensity=method;
1998  image->type=GrayscaleType;
1999  if ((method == Rec601LuminancePixelIntensityMethod) ||
2001  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2002  return(SetImageColorspace(image,GRAYColorspace,exception));
2003  }
2004 #endif
2005  /*
2006  Grayscale image.
2007  */
2008  status=MagickTrue;
2009  progress=0;
2010  image_view=AcquireAuthenticCacheView(image,exception);
2011 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2012  #pragma omp parallel for schedule(static) shared(progress,status) \
2013  magick_number_threads(image,image,image->rows,1)
2014 #endif
2015  for (y=0; y < (ssize_t) image->rows; y++)
2016  {
2017  register Quantum
2018  *magick_restrict q;
2019 
2020  register ssize_t
2021  x;
2022 
2023  if (status == MagickFalse)
2024  continue;
2025  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2026  if (q == (Quantum *) NULL)
2027  {
2028  status=MagickFalse;
2029  continue;
2030  }
2031  for (x=0; x < (ssize_t) image->columns; x++)
2032  {
2034  blue,
2035  green,
2036  red,
2037  intensity;
2038 
2039  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2040  {
2041  q+=GetPixelChannels(image);
2042  continue;
2043  }
2044  red=(MagickRealType) GetPixelRed(image,q);
2045  green=(MagickRealType) GetPixelGreen(image,q);
2046  blue=(MagickRealType) GetPixelBlue(image,q);
2047  intensity=0.0;
2048  switch (method)
2049  {
2051  {
2052  intensity=(red+green+blue)/3.0;
2053  break;
2054  }
2056  {
2057  intensity=MagickMax(MagickMax(red,green),blue);
2058  break;
2059  }
2061  {
2062  intensity=(MagickMin(MagickMin(red,green),blue)+
2063  MagickMax(MagickMax(red,green),blue))/2.0;
2064  break;
2065  }
2067  {
2068  intensity=(MagickRealType) (((double) red*red+green*green+
2069  blue*blue)/3.0);
2070  break;
2071  }
2073  {
2074  if (image->colorspace == RGBColorspace)
2075  {
2076  red=EncodePixelGamma(red);
2077  green=EncodePixelGamma(green);
2078  blue=EncodePixelGamma(blue);
2079  }
2080  intensity=0.298839*red+0.586811*green+0.114350*blue;
2081  break;
2082  }
2084  {
2085  if (image->colorspace == sRGBColorspace)
2086  {
2087  red=DecodePixelGamma(red);
2088  green=DecodePixelGamma(green);
2089  blue=DecodePixelGamma(blue);
2090  }
2091  intensity=0.298839*red+0.586811*green+0.114350*blue;
2092  break;
2093  }
2095  default:
2096  {
2097  if (image->colorspace == RGBColorspace)
2098  {
2099  red=EncodePixelGamma(red);
2100  green=EncodePixelGamma(green);
2101  blue=EncodePixelGamma(blue);
2102  }
2103  intensity=0.212656*red+0.715158*green+0.072186*blue;
2104  break;
2105  }
2107  {
2108  if (image->colorspace == sRGBColorspace)
2109  {
2110  red=DecodePixelGamma(red);
2111  green=DecodePixelGamma(green);
2112  blue=DecodePixelGamma(blue);
2113  }
2114  intensity=0.212656*red+0.715158*green+0.072186*blue;
2115  break;
2116  }
2118  {
2119  intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2120  blue*blue)/sqrt(3.0));
2121  break;
2122  }
2123  }
2124  SetPixelGray(image,ClampToQuantum(intensity),q);
2125  q+=GetPixelChannels(image);
2126  }
2127  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2128  status=MagickFalse;
2129  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2130  {
2132  proceed;
2133 
2134 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2135  #pragma omp critical (MagickCore_GrayscaleImage)
2136 #endif
2137  proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2138  image->rows);
2139  if (proceed == MagickFalse)
2140  status=MagickFalse;
2141  }
2142  }
2143  image_view=DestroyCacheView(image_view);
2144  image->intensity=method;
2145  image->type=GrayscaleType;
2146  if ((method == Rec601LuminancePixelIntensityMethod) ||
2148  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2149  return(SetImageColorspace(image,GRAYColorspace,exception));
2150 }
2151 
2152 /*
2153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2154 % %
2155 % %
2156 % %
2157 % H a l d C l u t I m a g e %
2158 % %
2159 % %
2160 % %
2161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2162 %
2163 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2164 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2165 % Create it with the HALD coder. You can apply any color transformation to
2166 % the Hald image and then use this method to apply the transform to the
2167 % image.
2168 %
2169 % The format of the HaldClutImage method is:
2170 %
2171 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2172 % ExceptionInfo *exception)
2173 %
2174 % A description of each parameter follows:
2175 %
2176 % o image: the image, which is replaced by indexed CLUT values
2177 %
2178 % o hald_image: the color lookup table image for replacement color values.
2179 %
2180 % o exception: return any errors or warnings in this structure.
2181 %
2182 */
2184  const Image *hald_image,ExceptionInfo *exception)
2185 {
2186 #define HaldClutImageTag "Clut/Image"
2187 
2188  typedef struct _HaldInfo
2189  {
2190  double
2191  x,
2192  y,
2193  z;
2194  } HaldInfo;
2195 
2196  CacheView
2197  *hald_view,
2198  *image_view;
2199 
2200  double
2201  width;
2202 
2204  status;
2205 
2207  progress;
2208 
2209  PixelInfo
2210  zero;
2211 
2212  size_t
2213  cube_size,
2214  length,
2215  level;
2216 
2217  ssize_t
2218  y;
2219 
2220  assert(image != (Image *) NULL);
2221  assert(image->signature == MagickCoreSignature);
2222  if (image->debug != MagickFalse)
2223  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2224  assert(hald_image != (Image *) NULL);
2225  assert(hald_image->signature == MagickCoreSignature);
2226  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2227  return(MagickFalse);
2228  if (image->alpha_trait == UndefinedPixelTrait)
2229  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2230  /*
2231  Hald clut image.
2232  */
2233  status=MagickTrue;
2234  progress=0;
2235  length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2236  (MagickRealType) hald_image->rows);
2237  for (level=2; (level*level*level) < length; level++) ;
2238  level*=level;
2239  cube_size=level*level;
2240  width=(double) hald_image->columns;
2241  GetPixelInfo(hald_image,&zero);
2242  hald_view=AcquireVirtualCacheView(hald_image,exception);
2243  image_view=AcquireAuthenticCacheView(image,exception);
2244 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2245  #pragma omp parallel for schedule(static) shared(progress,status) \
2246  magick_number_threads(image,image,image->rows,1)
2247 #endif
2248  for (y=0; y < (ssize_t) image->rows; y++)
2249  {
2250  register Quantum
2251  *magick_restrict q;
2252 
2253  register ssize_t
2254  x;
2255 
2256  if (status == MagickFalse)
2257  continue;
2258  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2259  if (q == (Quantum *) NULL)
2260  {
2261  status=MagickFalse;
2262  continue;
2263  }
2264  for (x=0; x < (ssize_t) image->columns; x++)
2265  {
2266  double
2267  offset;
2268 
2269  HaldInfo
2270  point;
2271 
2272  PixelInfo
2273  pixel,
2274  pixel1,
2275  pixel2,
2276  pixel3,
2277  pixel4;
2278 
2279  point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2280  point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2281  point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2282  offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2283  point.x-=floor(point.x);
2284  point.y-=floor(point.y);
2285  point.z-=floor(point.z);
2286  pixel1=zero;
2287  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2288  fmod(offset,width),floor(offset/width),&pixel1,exception);
2289  if (status == MagickFalse)
2290  break;
2291  pixel2=zero;
2292  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2293  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2294  if (status == MagickFalse)
2295  break;
2296  pixel3=zero;
2297  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2298  point.y,&pixel3);
2299  offset+=cube_size;
2300  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2301  fmod(offset,width),floor(offset/width),&pixel1,exception);
2302  if (status == MagickFalse)
2303  break;
2304  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2305  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2306  if (status == MagickFalse)
2307  break;
2308  pixel4=zero;
2309  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2310  point.y,&pixel4);
2311  pixel=zero;
2312  CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2313  point.z,&pixel);
2314  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2315  SetPixelRed(image,ClampToQuantum(pixel.red),q);
2316  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2317  SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2318  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2319  SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2320  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2321  (image->colorspace == CMYKColorspace))
2322  SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2323  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2324  (image->alpha_trait != UndefinedPixelTrait))
2325  SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2326  q+=GetPixelChannels(image);
2327  }
2328  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2329  status=MagickFalse;
2330  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2331  {
2333  proceed;
2334 
2335 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2336  #pragma omp critical (MagickCore_HaldClutImage)
2337 #endif
2338  proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2339  if (proceed == MagickFalse)
2340  status=MagickFalse;
2341  }
2342  }
2343  hald_view=DestroyCacheView(hald_view);
2344  image_view=DestroyCacheView(image_view);
2345  return(status);
2346 }
2347 
2348 /*
2349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2350 % %
2351 % %
2352 % %
2353 % L e v e l I m a g e %
2354 % %
2355 % %
2356 % %
2357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2358 %
2359 % LevelImage() adjusts the levels of a particular image channel by
2360 % scaling the colors falling between specified white and black points to
2361 % the full available quantum range.
2362 %
2363 % The parameters provided represent the black, and white points. The black
2364 % point specifies the darkest color in the image. Colors darker than the
2365 % black point are set to zero. White point specifies the lightest color in
2366 % the image. Colors brighter than the white point are set to the maximum
2367 % quantum value.
2368 %
2369 % If a '!' flag is given, map black and white colors to the given levels
2370 % rather than mapping those levels to black and white. See
2371 % LevelizeImage() below.
2372 %
2373 % Gamma specifies a gamma correction to apply to the image.
2374 %
2375 % The format of the LevelImage method is:
2376 %
2377 % MagickBooleanType LevelImage(Image *image,const double black_point,
2378 % const double white_point,const double gamma,ExceptionInfo *exception)
2379 %
2380 % A description of each parameter follows:
2381 %
2382 % o image: the image.
2383 %
2384 % o black_point: The level to map zero (black) to.
2385 %
2386 % o white_point: The level to map QuantumRange (white) to.
2387 %
2388 % o exception: return any errors or warnings in this structure.
2389 %
2390 */
2391 
2392 static inline double LevelPixel(const double black_point,
2393  const double white_point,const double gamma,const double pixel)
2394 {
2395  double
2396  level_pixel,
2397  scale;
2398 
2399  if (fabs(white_point-black_point) < MagickEpsilon)
2400  return(pixel);
2401  scale=1.0/(white_point-black_point);
2402  level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2403  1.0/gamma);
2404  return(level_pixel);
2405 }
2406 
2407 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2408  const double white_point,const double gamma,ExceptionInfo *exception)
2409 {
2410 #define LevelImageTag "Level/Image"
2411 
2412  CacheView
2413  *image_view;
2414 
2416  status;
2417 
2419  progress;
2420 
2421  register ssize_t
2422  i;
2423 
2424  ssize_t
2425  y;
2426 
2427  /*
2428  Allocate and initialize levels map.
2429  */
2430  assert(image != (Image *) NULL);
2431  assert(image->signature == MagickCoreSignature);
2432  if (image->debug != MagickFalse)
2433  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2434  if (image->storage_class == PseudoClass)
2435  for (i=0; i < (ssize_t) image->colors; i++)
2436  {
2437  /*
2438  Level colormap.
2439  */
2440  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2441  image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2442  white_point,gamma,image->colormap[i].red));
2443  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2444  image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2445  white_point,gamma,image->colormap[i].green));
2446  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2447  image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2448  white_point,gamma,image->colormap[i].blue));
2449  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2450  image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2451  white_point,gamma,image->colormap[i].alpha));
2452  }
2453  /*
2454  Level image.
2455  */
2456  status=MagickTrue;
2457  progress=0;
2458  image_view=AcquireAuthenticCacheView(image,exception);
2459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2460  #pragma omp parallel for schedule(static) shared(progress,status) \
2461  magick_number_threads(image,image,image->rows,1)
2462 #endif
2463  for (y=0; y < (ssize_t) image->rows; y++)
2464  {
2465  register Quantum
2466  *magick_restrict q;
2467 
2468  register ssize_t
2469  x;
2470 
2471  if (status == MagickFalse)
2472  continue;
2473  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2474  if (q == (Quantum *) NULL)
2475  {
2476  status=MagickFalse;
2477  continue;
2478  }
2479  for (x=0; x < (ssize_t) image->columns; x++)
2480  {
2481  register ssize_t
2482  j;
2483 
2484  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2485  {
2486  q+=GetPixelChannels(image);
2487  continue;
2488  }
2489  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2490  {
2491  PixelChannel channel = GetPixelChannelChannel(image,j);
2492  PixelTrait traits = GetPixelChannelTraits(image,channel);
2493  if ((traits & UpdatePixelTrait) == 0)
2494  continue;
2495  q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2496  (double) q[j]));
2497  }
2498  q+=GetPixelChannels(image);
2499  }
2500  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2501  status=MagickFalse;
2502  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2503  {
2505  proceed;
2506 
2507 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2508  #pragma omp critical (MagickCore_LevelImage)
2509 #endif
2510  proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2511  if (proceed == MagickFalse)
2512  status=MagickFalse;
2513  }
2514  }
2515  image_view=DestroyCacheView(image_view);
2516  (void) ClampImage(image,exception);
2517  return(status);
2518 }
2519 
2520 /*
2521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2522 % %
2523 % %
2524 % %
2525 % L e v e l i z e I m a g e %
2526 % %
2527 % %
2528 % %
2529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2530 %
2531 % LevelizeImage() applies the reversed LevelImage() operation to just
2532 % the specific channels specified. It compresses the full range of color
2533 % values, so that they lie between the given black and white points. Gamma is
2534 % applied before the values are mapped.
2535 %
2536 % LevelizeImage() can be called with by using a +level command line
2537 % API option, or using a '!' on a -level or LevelImage() geometry string.
2538 %
2539 % It can be used to de-contrast a greyscale image to the exact levels
2540 % specified. Or by using specific levels for each channel of an image you
2541 % can convert a gray-scale image to any linear color gradient, according to
2542 % those levels.
2543 %
2544 % The format of the LevelizeImage method is:
2545 %
2546 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2547 % const double white_point,const double gamma,ExceptionInfo *exception)
2548 %
2549 % A description of each parameter follows:
2550 %
2551 % o image: the image.
2552 %
2553 % o black_point: The level to map zero (black) to.
2554 %
2555 % o white_point: The level to map QuantumRange (white) to.
2556 %
2557 % o gamma: adjust gamma by this factor before mapping values.
2558 %
2559 % o exception: return any errors or warnings in this structure.
2560 %
2561 */
2563  const double black_point,const double white_point,const double gamma,
2564  ExceptionInfo *exception)
2565 {
2566 #define LevelizeImageTag "Levelize/Image"
2567 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2568  (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2569 
2570  CacheView
2571  *image_view;
2572 
2574  status;
2575 
2577  progress;
2578 
2579  register ssize_t
2580  i;
2581 
2582  ssize_t
2583  y;
2584 
2585  /*
2586  Allocate and initialize levels map.
2587  */
2588  assert(image != (Image *) NULL);
2589  assert(image->signature == MagickCoreSignature);
2590  if (image->debug != MagickFalse)
2591  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2592  if (image->storage_class == PseudoClass)
2593  for (i=0; i < (ssize_t) image->colors; i++)
2594  {
2595  /*
2596  Level colormap.
2597  */
2598  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2599  image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2600  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2601  image->colormap[i].green=(double) LevelizeValue(
2602  image->colormap[i].green);
2603  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2604  image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2605  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2606  image->colormap[i].alpha=(double) LevelizeValue(
2607  image->colormap[i].alpha);
2608  }
2609  /*
2610  Level image.
2611  */
2612  status=MagickTrue;
2613  progress=0;
2614  image_view=AcquireAuthenticCacheView(image,exception);
2615 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2616  #pragma omp parallel for schedule(static) shared(progress,status) \
2617  magick_number_threads(image,image,image->rows,1)
2618 #endif
2619  for (y=0; y < (ssize_t) image->rows; y++)
2620  {
2621  register Quantum
2622  *magick_restrict q;
2623 
2624  register ssize_t
2625  x;
2626 
2627  if (status == MagickFalse)
2628  continue;
2629  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2630  if (q == (Quantum *) NULL)
2631  {
2632  status=MagickFalse;
2633  continue;
2634  }
2635  for (x=0; x < (ssize_t) image->columns; x++)
2636  {
2637  register ssize_t
2638  j;
2639 
2640  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2641  {
2642  q+=GetPixelChannels(image);
2643  continue;
2644  }
2645  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2646  {
2647  PixelChannel channel = GetPixelChannelChannel(image,j);
2648  PixelTrait traits = GetPixelChannelTraits(image,channel);
2649  if ((traits & UpdatePixelTrait) == 0)
2650  continue;
2651  q[j]=LevelizeValue(q[j]);
2652  }
2653  q+=GetPixelChannels(image);
2654  }
2655  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2656  status=MagickFalse;
2657  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2658  {
2660  proceed;
2661 
2662 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2663  #pragma omp critical (MagickCore_LevelizeImage)
2664 #endif
2665  proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2666  if (proceed == MagickFalse)
2667  status=MagickFalse;
2668  }
2669  }
2670  image_view=DestroyCacheView(image_view);
2671  return(status);
2672 }
2673 
2674 /*
2675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2676 % %
2677 % %
2678 % %
2679 % L e v e l I m a g e C o l o r s %
2680 % %
2681 % %
2682 % %
2683 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2684 %
2685 % LevelImageColors() maps the given color to "black" and "white" values,
2686 % linearly spreading out the colors, and level values on a channel by channel
2687 % bases, as per LevelImage(). The given colors allows you to specify
2688 % different level ranges for each of the color channels separately.
2689 %
2690 % If the boolean 'invert' is set true the image values will modifyed in the
2691 % reverse direction. That is any existing "black" and "white" colors in the
2692 % image will become the color values given, with all other values compressed
2693 % appropriatally. This effectivally maps a greyscale gradient into the given
2694 % color gradient.
2695 %
2696 % The format of the LevelImageColors method is:
2697 %
2698 % MagickBooleanType LevelImageColors(Image *image,
2699 % const PixelInfo *black_color,const PixelInfo *white_color,
2700 % const MagickBooleanType invert,ExceptionInfo *exception)
2701 %
2702 % A description of each parameter follows:
2703 %
2704 % o image: the image.
2705 %
2706 % o black_color: The color to map black to/from
2707 %
2708 % o white_point: The color to map white to/from
2709 %
2710 % o invert: if true map the colors (levelize), rather than from (level)
2711 %
2712 % o exception: return any errors or warnings in this structure.
2713 %
2714 */
2716  const PixelInfo *black_color,const PixelInfo *white_color,
2717  const MagickBooleanType invert,ExceptionInfo *exception)
2718 {
2719  ChannelType
2720  channel_mask;
2721 
2723  status;
2724 
2725  /*
2726  Allocate and initialize levels map.
2727  */
2728  assert(image != (Image *) NULL);
2729  assert(image->signature == MagickCoreSignature);
2730  if (image->debug != MagickFalse)
2731  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2732  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
2733  ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
2734  (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
2735  (void) SetImageColorspace(image,sRGBColorspace,exception);
2736  status=MagickTrue;
2737  if (invert == MagickFalse)
2738  {
2739  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2740  {
2741  channel_mask=SetImageChannelMask(image,RedChannel);
2742  status&=LevelImage(image,black_color->red,white_color->red,1.0,
2743  exception);
2744  (void) SetImageChannelMask(image,channel_mask);
2745  }
2746  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2747  {
2748  channel_mask=SetImageChannelMask(image,GreenChannel);
2749  status&=LevelImage(image,black_color->green,white_color->green,1.0,
2750  exception);
2751  (void) SetImageChannelMask(image,channel_mask);
2752  }
2753  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2754  {
2755  channel_mask=SetImageChannelMask(image,BlueChannel);
2756  status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2757  exception);
2758  (void) SetImageChannelMask(image,channel_mask);
2759  }
2760  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2761  (image->colorspace == CMYKColorspace))
2762  {
2763  channel_mask=SetImageChannelMask(image,BlackChannel);
2764  status&=LevelImage(image,black_color->black,white_color->black,1.0,
2765  exception);
2766  (void) SetImageChannelMask(image,channel_mask);
2767  }
2768  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2769  (image->alpha_trait != UndefinedPixelTrait))
2770  {
2771  channel_mask=SetImageChannelMask(image,AlphaChannel);
2772  status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2773  exception);
2774  (void) SetImageChannelMask(image,channel_mask);
2775  }
2776  }
2777  else
2778  {
2779  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2780  {
2781  channel_mask=SetImageChannelMask(image,RedChannel);
2782  status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2783  exception);
2784  (void) SetImageChannelMask(image,channel_mask);
2785  }
2786  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2787  {
2788  channel_mask=SetImageChannelMask(image,GreenChannel);
2789  status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2790  exception);
2791  (void) SetImageChannelMask(image,channel_mask);
2792  }
2793  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2794  {
2795  channel_mask=SetImageChannelMask(image,BlueChannel);
2796  status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2797  exception);
2798  (void) SetImageChannelMask(image,channel_mask);
2799  }
2800  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2801  (image->colorspace == CMYKColorspace))
2802  {
2803  channel_mask=SetImageChannelMask(image,BlackChannel);
2804  status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2805  exception);
2806  (void) SetImageChannelMask(image,channel_mask);
2807  }
2808  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2809  (image->alpha_trait != UndefinedPixelTrait))
2810  {
2811  channel_mask=SetImageChannelMask(image,AlphaChannel);
2812  status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2813  exception);
2814  (void) SetImageChannelMask(image,channel_mask);
2815  }
2816  }
2817  return(status != 0 ? MagickTrue : MagickFalse);
2818 }
2819 
2820 /*
2821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2822 % %
2823 % %
2824 % %
2825 % L i n e a r S t r e t c h I m a g e %
2826 % %
2827 % %
2828 % %
2829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2830 %
2831 % LinearStretchImage() discards any pixels below the black point and above
2832 % the white point and levels the remaining pixels.
2833 %
2834 % The format of the LinearStretchImage method is:
2835 %
2836 % MagickBooleanType LinearStretchImage(Image *image,
2837 % const double black_point,const double white_point,
2838 % ExceptionInfo *exception)
2839 %
2840 % A description of each parameter follows:
2841 %
2842 % o image: the image.
2843 %
2844 % o black_point: the black point.
2845 %
2846 % o white_point: the white point.
2847 %
2848 % o exception: return any errors or warnings in this structure.
2849 %
2850 */
2852  const double black_point,const double white_point,ExceptionInfo *exception)
2853 {
2854 #define LinearStretchImageTag "LinearStretch/Image"
2855 
2856  CacheView
2857  *image_view;
2858 
2859  double
2860  *histogram,
2861  intensity;
2862 
2864  status;
2865 
2866  ssize_t
2867  black,
2868  white,
2869  y;
2870 
2871  /*
2872  Allocate histogram and linear map.
2873  */
2874  assert(image != (Image *) NULL);
2875  assert(image->signature == MagickCoreSignature);
2876  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2877  if (histogram == (double *) NULL)
2878  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2879  image->filename);
2880  /*
2881  Form histogram.
2882  */
2883  (void) memset(histogram,0,(MaxMap+1)*sizeof(*histogram));
2884  image_view=AcquireVirtualCacheView(image,exception);
2885  for (y=0; y < (ssize_t) image->rows; y++)
2886  {
2887  register const Quantum
2888  *magick_restrict p;
2889 
2890  register ssize_t
2891  x;
2892 
2893  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2894  if (p == (const Quantum *) NULL)
2895  break;
2896  for (x=0; x < (ssize_t) image->columns; x++)
2897  {
2898  intensity=GetPixelIntensity(image,p);
2899  histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2900  p+=GetPixelChannels(image);
2901  }
2902  }
2903  image_view=DestroyCacheView(image_view);
2904  /*
2905  Find the histogram boundaries by locating the black and white point levels.
2906  */
2907  intensity=0.0;
2908  for (black=0; black < (ssize_t) MaxMap; black++)
2909  {
2910  intensity+=histogram[black];
2911  if (intensity >= black_point)
2912  break;
2913  }
2914  intensity=0.0;
2915  for (white=(ssize_t) MaxMap; white != 0; white--)
2916  {
2917  intensity+=histogram[white];
2918  if (intensity >= white_point)
2919  break;
2920  }
2921  histogram=(double *) RelinquishMagickMemory(histogram);
2922  status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
2923  (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
2924  return(status);
2925 }
2926 
2927 
2928 /*
2929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2930 % %
2931 % %
2932 % %
2933 % M o d u l a t e I m a g e %
2934 % %
2935 % %
2936 % %
2937 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2938 %
2939 % ModulateImage() lets you control the brightness, saturation, and hue
2940 % of an image. Modulate represents the brightness, saturation, and hue
2941 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2942 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2943 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2944 %
2945 % The format of the ModulateImage method is:
2946 %
2947 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2948 % ExceptionInfo *exception)
2949 %
2950 % A description of each parameter follows:
2951 %
2952 % o image: the image.
2953 %
2954 % o modulate: Define the percent change in brightness, saturation, and hue.
2955 %
2956 % o exception: return any errors or warnings in this structure.
2957 %
2958 */
2959 
2960 static inline void ModulateHCL(const double percent_hue,
2961  const double percent_chroma,const double percent_luma,double *red,
2962  double *green,double *blue)
2963 {
2964  double
2965  hue,
2966  luma,
2967  chroma;
2968 
2969  /*
2970  Increase or decrease color luma, chroma, or hue.
2971  */
2972  ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2973  hue+=fmod((percent_hue-100.0),200.0)/200.0;
2974  chroma*=0.01*percent_chroma;
2975  luma*=0.01*percent_luma;
2976  ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2977 }
2978 
2979 static inline void ModulateHCLp(const double percent_hue,
2980  const double percent_chroma,const double percent_luma,double *red,
2981  double *green,double *blue)
2982 {
2983  double
2984  hue,
2985  luma,
2986  chroma;
2987 
2988  /*
2989  Increase or decrease color luma, chroma, or hue.
2990  */
2991  ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2992  hue+=fmod((percent_hue-100.0),200.0)/200.0;
2993  chroma*=0.01*percent_chroma;
2994  luma*=0.01*percent_luma;
2995  ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2996 }
2997 
2998 static inline void ModulateHSB(const double percent_hue,
2999  const double percent_saturation,const double percent_brightness,double *red,
3000  double *green,double *blue)
3001 {
3002  double
3003  brightness,
3004  hue,
3005  saturation;
3006 
3007  /*
3008  Increase or decrease color brightness, saturation, or hue.
3009  */
3010  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3011  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3012  saturation*=0.01*percent_saturation;
3013  brightness*=0.01*percent_brightness;
3014  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3015 }
3016 
3017 static inline void ModulateHSI(const double percent_hue,
3018  const double percent_saturation,const double percent_intensity,double *red,
3019  double *green,double *blue)
3020 {
3021  double
3022  intensity,
3023  hue,
3024  saturation;
3025 
3026  /*
3027  Increase or decrease color intensity, saturation, or hue.
3028  */
3029  ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3030  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3031  saturation*=0.01*percent_saturation;
3032  intensity*=0.01*percent_intensity;
3033  ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3034 }
3035 
3036 static inline void ModulateHSL(const double percent_hue,
3037  const double percent_saturation,const double percent_lightness,double *red,
3038  double *green,double *blue)
3039 {
3040  double
3041  hue,
3042  lightness,
3043  saturation;
3044 
3045  /*
3046  Increase or decrease color lightness, saturation, or hue.
3047  */
3048  ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3049  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3050  saturation*=0.01*percent_saturation;
3051  lightness*=0.01*percent_lightness;
3052  ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3053 }
3054 
3055 static inline void ModulateHSV(const double percent_hue,
3056  const double percent_saturation,const double percent_value,double *red,
3057  double *green,double *blue)
3058 {
3059  double
3060  hue,
3061  saturation,
3062  value;
3063 
3064  /*
3065  Increase or decrease color value, saturation, or hue.
3066  */
3067  ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3068  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3069  saturation*=0.01*percent_saturation;
3070  value*=0.01*percent_value;
3071  ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3072 }
3073 
3074 static inline void ModulateHWB(const double percent_hue,
3075  const double percent_whiteness,const double percent_blackness,double *red,
3076  double *green,double *blue)
3077 {
3078  double
3079  blackness,
3080  hue,
3081  whiteness;
3082 
3083  /*
3084  Increase or decrease color blackness, whiteness, or hue.
3085  */
3086  ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3087  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3088  blackness*=0.01*percent_blackness;
3089  whiteness*=0.01*percent_whiteness;
3090  ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3091 }
3092 
3093 static inline void ModulateLCHab(const double percent_luma,
3094  const double percent_chroma,const double percent_hue,double *red,
3095  double *green,double *blue)
3096 {
3097  double
3098  hue,
3099  luma,
3100  chroma;
3101 
3102  /*
3103  Increase or decrease color luma, chroma, or hue.
3104  */
3105  ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3106  luma*=0.01*percent_luma;
3107  chroma*=0.01*percent_chroma;
3108  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3109  ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3110 }
3111 
3112 static inline void ModulateLCHuv(const double percent_luma,
3113  const double percent_chroma,const double percent_hue,double *red,
3114  double *green,double *blue)
3115 {
3116  double
3117  hue,
3118  luma,
3119  chroma;
3120 
3121  /*
3122  Increase or decrease color luma, chroma, or hue.
3123  */
3124  ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3125  luma*=0.01*percent_luma;
3126  chroma*=0.01*percent_chroma;
3127  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3128  ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3129 }
3130 
3132  ExceptionInfo *exception)
3133 {
3134 #define ModulateImageTag "Modulate/Image"
3135 
3136  CacheView
3137  *image_view;
3138 
3140  colorspace;
3141 
3142  const char
3143  *artifact;
3144 
3145  double
3146  percent_brightness,
3147  percent_hue,
3148  percent_saturation;
3149 
3150  GeometryInfo
3151  geometry_info;
3152 
3154  status;
3155 
3157  progress;
3158 
3160  flags;
3161 
3162  register ssize_t
3163  i;
3164 
3165  ssize_t
3166  y;
3167 
3168  /*
3169  Initialize modulate table.
3170  */
3171  assert(image != (Image *) NULL);
3172  assert(image->signature == MagickCoreSignature);
3173  if (image->debug != MagickFalse)
3174  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3175  if (modulate == (char *) NULL)
3176  return(MagickFalse);
3178  (void) SetImageColorspace(image,sRGBColorspace,exception);
3179  flags=ParseGeometry(modulate,&geometry_info);
3180  percent_brightness=geometry_info.rho;
3181  percent_saturation=geometry_info.sigma;
3182  if ((flags & SigmaValue) == 0)
3183  percent_saturation=100.0;
3184  percent_hue=geometry_info.xi;
3185  if ((flags & XiValue) == 0)
3186  percent_hue=100.0;
3187  colorspace=UndefinedColorspace;
3188  artifact=GetImageArtifact(image,"modulate:colorspace");
3189  if (artifact != (const char *) NULL)
3191  MagickFalse,artifact);
3192  if (image->storage_class == PseudoClass)
3193  for (i=0; i < (ssize_t) image->colors; i++)
3194  {
3195  double
3196  blue,
3197  green,
3198  red;
3199 
3200  /*
3201  Modulate image colormap.
3202  */
3203  red=(double) image->colormap[i].red;
3204  green=(double) image->colormap[i].green;
3205  blue=(double) image->colormap[i].blue;
3206  switch (colorspace)
3207  {
3208  case HCLColorspace:
3209  {
3210  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3211  &red,&green,&blue);
3212  break;
3213  }
3214  case HCLpColorspace:
3215  {
3216  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3217  &red,&green,&blue);
3218  break;
3219  }
3220  case HSBColorspace:
3221  {
3222  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3223  &red,&green,&blue);
3224  break;
3225  }
3226  case HSIColorspace:
3227  {
3228  ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3229  &red,&green,&blue);
3230  break;
3231  }
3232  case HSLColorspace:
3233  default:
3234  {
3235  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3236  &red,&green,&blue);
3237  break;
3238  }
3239  case HSVColorspace:
3240  {
3241  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3242  &red,&green,&blue);
3243  break;
3244  }
3245  case HWBColorspace:
3246  {
3247  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3248  &red,&green,&blue);
3249  break;
3250  }
3251  case LCHColorspace:
3252  case LCHabColorspace:
3253  {
3254  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3255  &red,&green,&blue);
3256  break;
3257  }
3258  case LCHuvColorspace:
3259  {
3260  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3261  &red,&green,&blue);
3262  break;
3263  }
3264  }
3265  image->colormap[i].red=red;
3266  image->colormap[i].green=green;
3267  image->colormap[i].blue=blue;
3268  }
3269  /*
3270  Modulate image.
3271  */
3272 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3273  if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3274  percent_saturation,colorspace,exception) != MagickFalse)
3275  return(MagickTrue);
3276 #endif
3277  status=MagickTrue;
3278  progress=0;
3279  image_view=AcquireAuthenticCacheView(image,exception);
3280 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3281  #pragma omp parallel for schedule(static) shared(progress,status) \
3282  magick_number_threads(image,image,image->rows,1)
3283 #endif
3284  for (y=0; y < (ssize_t) image->rows; y++)
3285  {
3286  register Quantum
3287  *magick_restrict q;
3288 
3289  register ssize_t
3290  x;
3291 
3292  if (status == MagickFalse)
3293  continue;
3294  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3295  if (q == (Quantum *) NULL)
3296  {
3297  status=MagickFalse;
3298  continue;
3299  }
3300  for (x=0; x < (ssize_t) image->columns; x++)
3301  {
3302  double
3303  blue,
3304  green,
3305  red;
3306 
3307  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3308  {
3309  q+=GetPixelChannels(image);
3310  continue;
3311  }
3312  red=(double) GetPixelRed(image,q);
3313  green=(double) GetPixelGreen(image,q);
3314  blue=(double) GetPixelBlue(image,q);
3315  switch (colorspace)
3316  {
3317  case HCLColorspace:
3318  {
3319  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3320  &red,&green,&blue);
3321  break;
3322  }
3323  case HCLpColorspace:
3324  {
3325  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3326  &red,&green,&blue);
3327  break;
3328  }
3329  case HSBColorspace:
3330  {
3331  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3332  &red,&green,&blue);
3333  break;
3334  }
3335  case HSLColorspace:
3336  default:
3337  {
3338  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3339  &red,&green,&blue);
3340  break;
3341  }
3342  case HSVColorspace:
3343  {
3344  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3345  &red,&green,&blue);
3346  break;
3347  }
3348  case HWBColorspace:
3349  {
3350  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3351  &red,&green,&blue);
3352  break;
3353  }
3354  case LCHabColorspace:
3355  {
3356  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3357  &red,&green,&blue);
3358  break;
3359  }
3360  case LCHColorspace:
3361  case LCHuvColorspace:
3362  {
3363  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3364  &red,&green,&blue);
3365  break;
3366  }
3367  }
3368  SetPixelRed(image,ClampToQuantum(red),q);
3369  SetPixelGreen(image,ClampToQuantum(green),q);
3370  SetPixelBlue(image,ClampToQuantum(blue),q);
3371  q+=GetPixelChannels(image);
3372  }
3373  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3374  status=MagickFalse;
3375  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3376  {
3378  proceed;
3379 
3380 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3381  #pragma omp critical (MagickCore_ModulateImage)
3382 #endif
3383  proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3384  if (proceed == MagickFalse)
3385  status=MagickFalse;
3386  }
3387  }
3388  image_view=DestroyCacheView(image_view);
3389  return(status);
3390 }
3391 
3392 /*
3393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3394 % %
3395 % %
3396 % %
3397 % N e g a t e I m a g e %
3398 % %
3399 % %
3400 % %
3401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3402 %
3403 % NegateImage() negates the colors in the reference image. The grayscale
3404 % option means that only grayscale values within the image are negated.
3405 %
3406 % The format of the NegateImage method is:
3407 %
3408 % MagickBooleanType NegateImage(Image *image,
3409 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3410 %
3411 % A description of each parameter follows:
3412 %
3413 % o image: the image.
3414 %
3415 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3416 %
3417 % o exception: return any errors or warnings in this structure.
3418 %
3419 */
3421  const MagickBooleanType grayscale,ExceptionInfo *exception)
3422 {
3423 #define NegateImageTag "Negate/Image"
3424 
3425  CacheView
3426  *image_view;
3427 
3429  status;
3430 
3432  progress;
3433 
3434  register ssize_t
3435  i;
3436 
3437  ssize_t
3438  y;
3439 
3440  assert(image != (Image *) NULL);
3441  assert(image->signature == MagickCoreSignature);
3442  if (image->debug != MagickFalse)
3443  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3444  if (image->storage_class == PseudoClass)
3445  for (i=0; i < (ssize_t) image->colors; i++)
3446  {
3447  /*
3448  Negate colormap.
3449  */
3450  if( grayscale != MagickFalse )
3451  if ((image->colormap[i].red != image->colormap[i].green) ||
3452  (image->colormap[i].green != image->colormap[i].blue))
3453  continue;
3454  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3455  image->colormap[i].red=QuantumRange-image->colormap[i].red;
3456  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3457  image->colormap[i].green=QuantumRange-image->colormap[i].green;
3458  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3459  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3460  }
3461  /*
3462  Negate image.
3463  */
3464  status=MagickTrue;
3465  progress=0;
3466  image_view=AcquireAuthenticCacheView(image,exception);
3467  if( grayscale != MagickFalse )
3468  {
3469  for (y=0; y < (ssize_t) image->rows; y++)
3470  {
3472  sync;
3473 
3474  register Quantum
3475  *magick_restrict q;
3476 
3477  register ssize_t
3478  x;
3479 
3480  if (status == MagickFalse)
3481  continue;
3482  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3483  exception);
3484  if (q == (Quantum *) NULL)
3485  {
3486  status=MagickFalse;
3487  continue;
3488  }
3489  for (x=0; x < (ssize_t) image->columns; x++)
3490  {
3491  register ssize_t
3492  j;
3493 
3494  if ((GetPixelWriteMask(image,q) <= (QuantumRange/2)) ||
3495  IsPixelGray(image,q) != MagickFalse)
3496  {
3497  q+=GetPixelChannels(image);
3498  continue;
3499  }
3500  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3501  {
3502  PixelChannel channel = GetPixelChannelChannel(image,j);
3503  PixelTrait traits = GetPixelChannelTraits(image,channel);
3504  if ((traits & UpdatePixelTrait) == 0)
3505  continue;
3506  q[j]=QuantumRange-q[j];
3507  }
3508  q+=GetPixelChannels(image);
3509  }
3510  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3511  if (sync == MagickFalse)
3512  status=MagickFalse;
3513  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3514  {
3516  proceed;
3517 
3518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3519  #pragma omp critical (MagickCore_NegateImage)
3520 #endif
3521  proceed=SetImageProgress(image,NegateImageTag,progress++,
3522  image->rows);
3523  if (proceed == MagickFalse)
3524  status=MagickFalse;
3525  }
3526  }
3527  image_view=DestroyCacheView(image_view);
3528  return(MagickTrue);
3529  }
3530  /*
3531  Negate image.
3532  */
3533 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3534  #pragma omp parallel for schedule(static) shared(progress,status) \
3535  magick_number_threads(image,image,image->rows,1)
3536 #endif
3537  for (y=0; y < (ssize_t) image->rows; y++)
3538  {
3539  register Quantum
3540  *magick_restrict q;
3541 
3542  register ssize_t
3543  x;
3544 
3545  if (status == MagickFalse)
3546  continue;
3547  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3548  if (q == (Quantum *) NULL)
3549  {
3550  status=MagickFalse;
3551  continue;
3552  }
3553  for (x=0; x < (ssize_t) image->columns; x++)
3554  {
3555  register ssize_t
3556  j;
3557 
3558  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3559  {
3560  q+=GetPixelChannels(image);
3561  continue;
3562  }
3563  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3564  {
3565  PixelChannel channel = GetPixelChannelChannel(image,j);
3566  PixelTrait traits = GetPixelChannelTraits(image,channel);
3567  if ((traits & UpdatePixelTrait) == 0)
3568  continue;
3569  q[j]=QuantumRange-q[j];
3570  }
3571  q+=GetPixelChannels(image);
3572  }
3573  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3574  status=MagickFalse;
3575  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3576  {
3578  proceed;
3579 
3580 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3581  #pragma omp critical (MagickCore_NegateImage)
3582 #endif
3583  proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3584  if (proceed == MagickFalse)
3585  status=MagickFalse;
3586  }
3587  }
3588  image_view=DestroyCacheView(image_view);
3589  return(status);
3590 }
3591 
3592 /*
3593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3594 % %
3595 % %
3596 % %
3597 % N o r m a l i z e I m a g e %
3598 % %
3599 % %
3600 % %
3601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3602 %
3603 % The NormalizeImage() method enhances the contrast of a color image by
3604 % mapping the darkest 2 percent of all pixel to black and the brightest
3605 % 1 percent to white.
3606 %
3607 % The format of the NormalizeImage method is:
3608 %
3609 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3610 %
3611 % A description of each parameter follows:
3612 %
3613 % o image: the image.
3614 %
3615 % o exception: return any errors or warnings in this structure.
3616 %
3617 */
3619  ExceptionInfo *exception)
3620 {
3621  double
3622  black_point,
3623  white_point;
3624 
3625  black_point=(double) image->columns*image->rows*0.0015;
3626  white_point=(double) image->columns*image->rows*0.9995;
3627  return(ContrastStretchImage(image,black_point,white_point,exception));
3628 }
3629 
3630 /*
3631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3632 % %
3633 % %
3634 % %
3635 % S i g m o i d a l C o n t r a s t I m a g e %
3636 % %
3637 % %
3638 % %
3639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3640 %
3641 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3642 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3643 % sigmoidal transfer function without saturating highlights or shadows.
3644 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3645 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3646 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3647 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3648 % is reduced.
3649 %
3650 % The format of the SigmoidalContrastImage method is:
3651 %
3652 % MagickBooleanType SigmoidalContrastImage(Image *image,
3653 % const MagickBooleanType sharpen,const char *levels,
3654 % ExceptionInfo *exception)
3655 %
3656 % A description of each parameter follows:
3657 %
3658 % o image: the image.
3659 %
3660 % o sharpen: Increase or decrease image contrast.
3661 %
3662 % o contrast: strength of the contrast, the larger the number the more
3663 % 'threshold-like' it becomes.
3664 %
3665 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3666 %
3667 % o exception: return any errors or warnings in this structure.
3668 %
3669 */
3670 
3671 /*
3672  ImageMagick 6 has a version of this function which uses LUTs.
3673 */
3674 
3675 /*
3676  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3677  constant" set to a.
3678 
3679  The first version, based on the hyperbolic tangent tanh, when combined with
3680  the scaling step, is an exact arithmetic clone of the the sigmoid function
3681  based on the logistic curve. The equivalence is based on the identity
3682 
3683  1/(1+exp(-t)) = (1+tanh(t/2))/2
3684 
3685  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3686  scaled sigmoidal derivation is invariant under affine transformations of
3687  the ordinate.
3688 
3689  The tanh version is almost certainly more accurate and cheaper. The 0.5
3690  factor in the argument is to clone the legacy ImageMagick behavior. The
3691  reason for making the define depend on atanh even though it only uses tanh
3692  has to do with the construction of the inverse of the scaled sigmoidal.
3693 */
3694 #if defined(MAGICKCORE_HAVE_ATANH)
3695 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3696 #else
3697 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3698 #endif
3699 /*
3700  Scaled sigmoidal function:
3701 
3702  ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3703  ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3704 
3705  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3706  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3707  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3708  zero. This is fixed below by exiting immediately when contrast is small,
3709  leaving the image (or colormap) unmodified. This appears to be safe because
3710  the series expansion of the logistic sigmoidal function around x=b is
3711 
3712  1/2-a*(b-x)/4+...
3713 
3714  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3715 */
3716 #define ScaledSigmoidal(a,b,x) ( \
3717  (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3718  (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3719 /*
3720  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3721  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3722  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3723  when creating a LUT from in gamut values, hence the branching. In
3724  addition, HDRI may have out of gamut values.
3725  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3726  It is only a right inverse. This is unavoidable.
3727 */
3728 static inline double InverseScaledSigmoidal(const double a,const double b,
3729  const double x)
3730 {
3731  const double sig0=Sigmoidal(a,b,0.0);
3732  const double sig1=Sigmoidal(a,b,1.0);
3733  const double argument=(sig1-sig0)*x+sig0;
3734  const double clamped=
3735  (
3736 #if defined(MAGICKCORE_HAVE_ATANH)
3737  argument < -1+MagickEpsilon
3738  ?
3739  -1+MagickEpsilon
3740  :
3741  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3742  );
3743  return(b+(2.0/a)*atanh(clamped));
3744 #else
3745  argument < MagickEpsilon
3746  ?
3748  :
3749  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3750  );
3751  return(b-log(1.0/clamped-1.0)/a);
3752 #endif
3753 }
3754 
3756  const MagickBooleanType sharpen,const double contrast,const double midpoint,
3757  ExceptionInfo *exception)
3758 {
3759 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3760 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3761  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3762 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3763  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3764 
3765  CacheView
3766  *image_view;
3767 
3769  status;
3770 
3772  progress;
3773 
3774  ssize_t
3775  y;
3776 
3777  /*
3778  Convenience macros.
3779  */
3780  assert(image != (Image *) NULL);
3781  assert(image->signature == MagickCoreSignature);
3782  if (image->debug != MagickFalse)
3783  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3784  /*
3785  Side effect: may clamp values unless contrast<MagickEpsilon, in which
3786  case nothing is done.
3787  */
3788  if (contrast < MagickEpsilon)
3789  return(MagickTrue);
3790  /*
3791  Sigmoidal-contrast enhance colormap.
3792  */
3793  if (image->storage_class == PseudoClass)
3794  {
3795  register ssize_t
3796  i;
3797 
3798  if( sharpen != MagickFalse )
3799  for (i=0; i < (ssize_t) image->colors; i++)
3800  {
3801  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3802  image->colormap[i].red=(MagickRealType) ScaledSig(
3803  image->colormap[i].red);
3804  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3805  image->colormap[i].green=(MagickRealType) ScaledSig(
3806  image->colormap[i].green);
3807  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3808  image->colormap[i].blue=(MagickRealType) ScaledSig(
3809  image->colormap[i].blue);
3810  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3811  image->colormap[i].alpha=(MagickRealType) ScaledSig(
3812  image->colormap[i].alpha);
3813  }
3814  else
3815  for (i=0; i < (ssize_t) image->colors; i++)
3816  {
3817  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3819  image->colormap[i].red);
3820  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3822  image->colormap[i].green);
3823  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3825  image->colormap[i].blue);
3826  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3828  image->colormap[i].alpha);
3829  }
3830  }
3831  /*
3832  Sigmoidal-contrast enhance image.
3833  */
3834  status=MagickTrue;
3835  progress=0;
3836  image_view=AcquireAuthenticCacheView(image,exception);
3837 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3838  #pragma omp parallel for schedule(static) shared(progress,status) \
3839  magick_number_threads(image,image,image->rows,1)
3840 #endif
3841  for (y=0; y < (ssize_t) image->rows; y++)
3842  {
3843  register Quantum
3844  *magick_restrict q;
3845 
3846  register ssize_t
3847  x;
3848 
3849  if (status == MagickFalse)
3850  continue;
3851  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3852  if (q == (Quantum *) NULL)
3853  {
3854  status=MagickFalse;
3855  continue;
3856  }
3857  for (x=0; x < (ssize_t) image->columns; x++)
3858  {
3859  register ssize_t
3860  i;
3861 
3862  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3863  {
3864  q+=GetPixelChannels(image);
3865  continue;
3866  }
3867  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3868  {
3869  PixelChannel channel = GetPixelChannelChannel(image,i);
3870  PixelTrait traits = GetPixelChannelTraits(image,channel);
3871  if ((traits & UpdatePixelTrait) == 0)
3872  continue;
3873  if( sharpen != MagickFalse )
3874  q[i]=ScaledSig(q[i]);
3875  else
3876  q[i]=InverseScaledSig(q[i]);
3877  }
3878  q+=GetPixelChannels(image);
3879  }
3880  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3881  status=MagickFalse;
3882  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3883  {
3885  proceed;
3886 
3887 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3888  #pragma omp critical (MagickCore_SigmoidalContrastImage)
3889 #endif
3890  proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3891  image->rows);
3892  if (proceed == MagickFalse)
3893  status=MagickFalse;
3894  }
3895  }
3896  image_view=DestroyCacheView(image_view);
3897  return(status);
3898 }
size_t rows
Definition: image.h:172
#define magick_restrict
Definition: MagickCore.h:41
MagickExport MagickRealType EncodePixelGamma(const MagickRealType pixel)
Definition: pixel.c:446
MagickDoubleType MagickRealType
Definition: magick-type.h:118
MagickExport MagickBooleanType NegateImage(Image *image, const MagickBooleanType grayscale, ExceptionInfo *exception)
Definition: enhance.c:3420
PixelIntensityMethod intensity
Definition: image.h:222
MagickExport MagickBooleanType LevelizeImage(Image *image, const double black_point, const double white_point, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:2562
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
MagickExport MagickBooleanType LinearStretchImage(Image *image, const double black_point, const double white_point, ExceptionInfo *exception)
Definition: enhance.c:2851
MagickPrivate void ConvertLCHabToRGB(const double, const double, const double, double *, double *, double *)
MagickExport MagickBooleanType AutoGammaImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:111
static ssize_t GetPixelChannelOffset(const Image *magick_restrict image, const PixelChannel channel)
static void ModulateHWB(const double percent_hue, const double percent_whiteness, const double percent_blackness, double *red, double *green, double *blue)
Definition: enhance.c:3074
static MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
PixelInfo * colormap
Definition: image.h:179
MagickExport void ConvertRGBToHSL(const double red, const double green, const double blue, double *hue, double *saturation, double *lightness)
Definition: gem.c:1099
MagickPrivate void ConvertHSIToRGB(const double, const double, const double, double *, double *, double *)
MagickExport XMLTreeInfo * DestroyXMLTree(XMLTreeInfo *xml_info)
Definition: xml-tree.c:558
MagickProgressMonitor progress_monitor
Definition: image.h:303
ImageType type
Definition: image.h:264
static PixelTrait GetPixelBlackTraits(const Image *magick_restrict image)
MagickExport MagickBooleanType SyncImage(Image *image, ExceptionInfo *exception)
Definition: image.c:3800
static void SetPixelBackgoundColor(const Image *magick_restrict image, Quantum *magick_restrict pixel)
static PixelTrait GetPixelRedTraits(const Image *magick_restrict image)
#define ContrastImageTag
#define Sigmoidal(a, b, x)
Definition: enhance.c:3697
static PixelTrait GetPixelAlphaTraits(const Image *magick_restrict image)
static Quantum GetPixelRed(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport ssize_t ParseCommandOption(const CommandOption option, const MagickBooleanType list, const char *options)
Definition: option.c:2956
MagickExport MagickBooleanType FunctionImage(Image *image, const MagickFunction function, const size_t number_parameters, const double *parameters, ExceptionInfo *exception)
Definition: statistic.c:1017
static void ModulateHCLp(const double percent_hue, const double percent_chroma, const double percent_luma, double *red, double *green, double *blue)
Definition: enhance.c:2979
PixelInterpolateMethod
Definition: pixel.h:108
PixelInterpolateMethod interpolate
Definition: image.h:255
size_t signature
Definition: exception.h:123
double rho
Definition: geometry.h:106
static double LevelPixel(const double black_point, const double white_point, const double gamma, const double pixel)
Definition: enhance.c:2392
#define ModulateImageTag
PixelIntensityMethod
Definition: pixel.h:94
static void SetPixelGray(const Image *magick_restrict image, const Quantum gray, Quantum *magick_restrict pixel)
MagickExport const char * GetImageArtifact(const Image *image, const char *artifact)
Definition: artifact.c:273
MagickRealType red
Definition: pixel.h:188
static double StringToDouble(const char *magick_restrict string, char **magick_restrict sentinal)
static PixelTrait GetPixelChannelTraits(const Image *magick_restrict image, const PixelChannel channel)
#define MagickPI
Definition: image-private.h:30
MagickExport MagickBooleanType EqualizeImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:1507
MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image, const CacheView_ *image_view, const PixelInterpolateMethod method, const double x, const double y, PixelInfo *pixel, ExceptionInfo *exception)
Definition: pixel.c:5473
static void SetPixelViaPixelInfo(const Image *magick_restrict image, const PixelInfo *magick_restrict pixel_info, Quantum *magick_restrict pixel)
static MagickBooleanType IsGrayColorspace(const ColorspaceType colorspace)
static void ModulateHSL(const double percent_hue, const double percent_saturation, const double percent_lightness, double *red, double *green, double *blue)
Definition: enhance.c:3036
MagickExport const Quantum * GetCacheViewVirtualPixels(const CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:651
static double gamma_pow(const double value, const double gamma)
Definition: enhance.c:1786
MagickExport MagickBooleanType GrayscaleImage(Image *image, const PixelIntensityMethod method, ExceptionInfo *exception)
Definition: enhance.c:1966
MagickRealType alpha
Definition: pixel.h:188
#define MagickEpsilon
Definition: magick-type.h:110
MagickExport XMLTreeInfo * GetXMLTreeChild(XMLTreeInfo *xml_info, const char *tag)
Definition: xml-tree.c:896
double sigma
Definition: geometry.h:106
ClassType storage_class
Definition: image.h:154
#define LevelImageTag
#define ThrowBinaryException(severity, tag, context)
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:127
MagickExport void GetPixelInfo(const Image *image, PixelInfo *pixel)
Definition: pixel.c:2170
Definition: image.h:151
MagickExport MagickBooleanType ColorDecisionListImage(Image *image, const char *color_correction_collection, ExceptionInfo *exception)
Definition: enhance.c:482
MagickPrivate void ConvertRGBToHSB(const double, const double, const double, double *, double *, double *)
MagickExport MagickBooleanType SetImageGray(Image *image, ExceptionInfo *exception)
Definition: colorspace.c:1214
#define LevelizeImageTag
MagickExport MagickRealType DecodePixelGamma(const MagickRealType pixel)
Definition: pixel.c:319
MagickExport MagickBooleanType ContrastImage(Image *image, const MagickBooleanType sharpen, ExceptionInfo *exception)
Definition: enhance.c:868
#define MagickCoreSignature
MagickExport Quantum * GetCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:299
#define HaldClutImageTag
MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, const AlphaChannelOption alpha_type, ExceptionInfo *exception)
Definition: channel.c:967
MagickBooleanType
Definition: magick-type.h:156
unsigned int MagickStatusType
Definition: magick-type.h:119
static double PerceptibleReciprocal(const double x)
MagickExport Image * EnhanceImage(const Image *image, ExceptionInfo *exception)
Definition: enhance.c:1306
#define GrayscaleImageTag
#define ContrastStretchImageTag
static MagickBooleanType IssRGBCompatibleColorspace(const ColorspaceType colorspace)
static Quantum GetPixelWriteMask(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport MagickBooleanType NormalizeImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:3618
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:533
static void ModulateLCHuv(const double percent_luma, const double percent_chroma, const double percent_hue, double *red, double *green, double *blue)
Definition: enhance.c:3112
MagickExport MagickBooleanType ModulateImage(Image *image, const char *modulate, ExceptionInfo *exception)
Definition: enhance.c:3131
static void Contrast(const int sign, double *red, double *green, double *blue)
Definition: enhance.c:841
static void ModulateLCHab(const double percent_luma, const double percent_chroma, const double percent_hue, double *red, double *green, double *blue)
Definition: enhance.c:3093
ChannelType channel_mask
Definition: image.h:288
#define GammaCorrectImageTag
MagickPrivate void ConvertLCHuvToRGB(const double, const double, const double, double *, double *, double *)
MagickExport const char * GetXMLTreeContent(XMLTreeInfo *xml_info)
Definition: xml-tree.c:936
#define MagickPathExtent
static Quantum GetPixelGreen(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
static void CompositePixelInfoAreaBlend(const PixelInfo *p, const double alpha, const PixelInfo *q, const double beta, const double area, PixelInfo *composite)
static void GetPixelInfoPixel(const Image *magick_restrict image, const Quantum *magick_restrict pixel, PixelInfo *magick_restrict pixel_info)
PixelTrait alpha_trait
Definition: image.h:280
MagickPrivate void ConvertRGBToHSV(const double, const double, const double, double *, double *, double *)
MagickRealType blue
Definition: pixel.h:188
#define EnhanceImageTag
#define EnhancePixel(weight)
MagickExport ChannelType SetImageChannelMask(Image *image, const ChannelType channel_mask)
Definition: image.c:2486
#define EqualizeImageTag
MagickExport Quantum * QueueCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:977
static void ModulateHCL(const double percent_hue, const double percent_chroma, const double percent_luma, double *red, double *green, double *blue)
Definition: enhance.c:2960
MagickPrivate void ConvertRGBToHSI(const double, const double, const double, double *, double *, double *)
static void ModulateHSI(const double percent_hue, const double percent_saturation, const double percent_intensity, double *red, double *green, double *blue)
Definition: enhance.c:3017
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1397
size_t signature
Definition: image.h:354
MagickExport MagickBooleanType LevelImageColors(Image *image, const PixelInfo *black_color, const PixelInfo *white_color, const MagickBooleanType invert, ExceptionInfo *exception)
Definition: enhance.c:2715
size_t columns
Definition: image.h:172
#define QuantumScale
Definition: magick-type.h:113
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
static MagickBooleanType IsPixelGray(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
#define LevelizeValue(x)
static PixelTrait GetPixelGreenTraits(const Image *magick_restrict image)
static void SetPixelBlue(const Image *magick_restrict image, const Quantum blue, Quantum *magick_restrict pixel)
ChannelType
Definition: pixel.h:33
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2602
MagickExport void ConvertHSLToRGB(const double hue, const double saturation, const double lightness, double *red, double *green, double *blue)
Definition: gem.c:462
PixelChannel
Definition: pixel.h:66
MagickExport MagickBooleanType GetImageMean(const Image *image, double *mean, double *standard_deviation, ExceptionInfo *exception)
Definition: statistic.c:1288
#define MaxMap
Definition: magick-type.h:75
#define MagickMax(x, y)
Definition: image-private.h:26
MagickExport MagickBooleanType ClutImage(Image *image, const Image *clut_image, const PixelInterpolateMethod method, ExceptionInfo *exception)
Definition: enhance.c:299
size_t colors
Definition: image.h:172
static size_t GetPixelChannels(const Image *magick_restrict image)
MagickPrivate void ConvertHWBToRGB(const double, const double, const double, double *, double *, double *)
char filename[MagickPathExtent]
Definition: image.h:319
#define SigmoidalContrastImageTag
#define GetMagickModule()
Definition: log.h:28
#define InverseScaledSig(x)
static Quantum ClampToQuantum(const MagickRealType value)
Definition: quantum.h:84
static PixelChannel GetPixelChannelChannel(const Image *magick_restrict image, const ssize_t offset)
MagickExport CacheView * AcquireVirtualCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:149
MagickExport void GetNextToken(const char *start, const char **end, const size_t extent, char *token)
Definition: token.c:171
MagickExport MagickBooleanType MinMaxStretchImage(Image *image, const double black, const double white, const double gamma, ExceptionInfo *exception)
Definition: histogram.c:899
unsigned short Quantum
Definition: magick-type.h:82
double xi
Definition: geometry.h:106
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1134
MagickExport MagickBooleanType LevelImage(Image *image, const double black_point, const double white_point, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:2407
#define NegateImageTag
MagickRealType black
Definition: pixel.h:188
MagickPrivate void ConvertHSVToRGB(const double, const double, const double, double *, double *, double *)
MagickExport MagickStatusType ParseGeometry(const char *geometry, GeometryInfo *geometry_info)
Definition: geometry.c:852
#define MaxRange(color)
static void ModulateHSV(const double percent_hue, const double percent_saturation, const double percent_value, double *red, double *green, double *blue)
Definition: enhance.c:3055
static void ModulateHSB(const double percent_hue, const double percent_saturation, const double percent_brightness, double *red, double *green, double *blue)
Definition: enhance.c:2998
#define MagickMin(x, y)
Definition: image-private.h:27
static void SetPixelAlpha(const Image *magick_restrict image, const Quantum alpha, Quantum *magick_restrict pixel)
ColorspaceType
Definition: colorspace.h:25
MagickPrivate void ConvertHCLToRGB(const double, const double, const double, double *, double *, double *)
MagickPrivate void ConvertRGBToHWB(const double, const double, const double, double *, double *, double *)
MagickPrivate void ConvertHCLpToRGB(const double, const double, const double, double *, double *, double *)
#define ScaledSig(x)
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1027
#define MaxPixelChannels
Definition: pixel.h:27
MagickExport MagickBooleanType HaldClutImage(Image *image, const Image *hald_image, ExceptionInfo *exception)
Definition: enhance.c:2183
MagickExport MagickBooleanType GammaImage(Image *image, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:1791
MagickExport MagickBooleanType ClampImage(Image *image, ExceptionInfo *exception)
Definition: threshold.c:1093
MagickRealType green
Definition: pixel.h:188
MagickPrivate void ConvertRGBToLCHab(const double, const double, const double, double *, double *, double *)
static void SetPixelRed(const Image *magick_restrict image, const Quantum red, Quantum *magick_restrict pixel)
MagickExport MagickBooleanType AutoLevelImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:185
#define ClutImageTag
#define MagickExport
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
MagickPrivate void ConvertHSBToRGB(const double, const double, const double, double *, double *, double *)
MagickExport CacheView * AcquireAuthenticCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:112
MagickExport MagickBooleanType SigmoidalContrastImage(Image *image, const MagickBooleanType sharpen, const double contrast, const double midpoint, ExceptionInfo *exception)
Definition: enhance.c:3755
ColorspaceType colorspace
Definition: pixel.h:173
MagickPrivate void ConvertRGBToHCL(const double, const double, const double, double *, double *, double *)
static void SetPixelBlack(const Image *magick_restrict image, const Quantum black, Quantum *magick_restrict pixel)
static Quantum GetPixelBlue(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
PixelTrait
Definition: pixel.h:132
static double InverseScaledSigmoidal(const double a, const double b, const double x)
Definition: enhance.c:3728
MagickPrivate void ConvertRGBToLCHuv(const double, const double, const double, double *, double *, double *)
Definition: gem.c:1375
MagickExport MagickRealType GetPixelIntensity(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
Definition: pixel.c:2358
MagickExport MagickBooleanType ContrastStretchImage(Image *image, const double black_point, const double white_point, ExceptionInfo *exception)
Definition: enhance.c:1019
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1183
#define ColorDecisionListCorrectImageTag
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:800
double gamma
Definition: image.h:186
ColorspaceType colorspace
Definition: image.h:157
MagickExport XMLTreeInfo * NewXMLTree(const char *xml, ExceptionInfo *exception)
Definition: xml-tree.c:1952
#define QuantumRange
Definition: magick-type.h:83
MagickPrivate void ConvertRGBToHCLp(const double, const double, const double, double *, double *, double *)
MagickBooleanType debug
Definition: image.h:334
static void SetPixelGreen(const Image *magick_restrict image, const Quantum green, Quantum *magick_restrict pixel)
MagickExport MagickBooleanType BrightnessContrastImage(Image *image, const double brightness, const double contrast, ExceptionInfo *exception)
Definition: enhance.c:222
static PixelTrait GetPixelBlueTraits(const Image *magick_restrict image)