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