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  if ((method == Rec601LuminancePixelIntensityMethod) ||
1995  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
1996  return(SetImageColorspace(image,GRAYColorspace,exception));
1997  }
1998 #endif
1999  /*
2000  Grayscale image.
2001  */
2002  status=MagickTrue;
2003  progress=0;
2004  image_view=AcquireAuthenticCacheView(image,exception);
2005 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2006  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2007  magick_number_threads(image,image,image->rows,1)
2008 #endif
2009  for (y=0; y < (ssize_t) image->rows; y++)
2010  {
2011  register Quantum
2012  *magick_restrict q;
2013 
2014  register ssize_t
2015  x;
2016 
2017  if (status == MagickFalse)
2018  continue;
2019  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2020  if (q == (Quantum *) NULL)
2021  {
2022  status=MagickFalse;
2023  continue;
2024  }
2025  for (x=0; x < (ssize_t) image->columns; x++)
2026  {
2028  blue,
2029  green,
2030  red,
2031  intensity;
2032 
2033  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2034  {
2035  q+=GetPixelChannels(image);
2036  continue;
2037  }
2038  red=(MagickRealType) GetPixelRed(image,q);
2039  green=(MagickRealType) GetPixelGreen(image,q);
2040  blue=(MagickRealType) GetPixelBlue(image,q);
2041  intensity=0.0;
2042  switch (method)
2043  {
2045  {
2046  intensity=(red+green+blue)/3.0;
2047  break;
2048  }
2050  {
2051  intensity=MagickMax(MagickMax(red,green),blue);
2052  break;
2053  }
2055  {
2056  intensity=(MagickMin(MagickMin(red,green),blue)+
2057  MagickMax(MagickMax(red,green),blue))/2.0;
2058  break;
2059  }
2061  {
2062  intensity=(MagickRealType) (((double) red*red+green*green+
2063  blue*blue)/3.0);
2064  break;
2065  }
2067  {
2068  if (image->colorspace == RGBColorspace)
2069  {
2070  red=EncodePixelGamma(red);
2071  green=EncodePixelGamma(green);
2072  blue=EncodePixelGamma(blue);
2073  }
2074  intensity=0.298839*red+0.586811*green+0.114350*blue;
2075  break;
2076  }
2078  {
2079  if (image->colorspace == sRGBColorspace)
2080  {
2081  red=DecodePixelGamma(red);
2082  green=DecodePixelGamma(green);
2083  blue=DecodePixelGamma(blue);
2084  }
2085  intensity=0.298839*red+0.586811*green+0.114350*blue;
2086  break;
2087  }
2089  default:
2090  {
2091  if (image->colorspace == RGBColorspace)
2092  {
2093  red=EncodePixelGamma(red);
2094  green=EncodePixelGamma(green);
2095  blue=EncodePixelGamma(blue);
2096  }
2097  intensity=0.212656*red+0.715158*green+0.072186*blue;
2098  break;
2099  }
2101  {
2102  if (image->colorspace == sRGBColorspace)
2103  {
2104  red=DecodePixelGamma(red);
2105  green=DecodePixelGamma(green);
2106  blue=DecodePixelGamma(blue);
2107  }
2108  intensity=0.212656*red+0.715158*green+0.072186*blue;
2109  break;
2110  }
2112  {
2113  intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2114  blue*blue)/sqrt(3.0));
2115  break;
2116  }
2117  }
2118  SetPixelGray(image,ClampToQuantum(intensity),q);
2119  q+=GetPixelChannels(image);
2120  }
2121  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2122  status=MagickFalse;
2123  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2124  {
2126  proceed;
2127 
2128 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2129  #pragma omp critical (MagickCore_GrayscaleImage)
2130 #endif
2131  proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2132  image->rows);
2133  if (proceed == MagickFalse)
2134  status=MagickFalse;
2135  }
2136  }
2137  image_view=DestroyCacheView(image_view);
2138  image->intensity=method;
2139  image->type=GrayscaleType;
2140  if ((method == Rec601LuminancePixelIntensityMethod) ||
2142  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2143  return(SetImageColorspace(image,GRAYColorspace,exception));
2144 }
2145 
2146 /*
2147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2148 % %
2149 % %
2150 % %
2151 % H a l d C l u t I m a g e %
2152 % %
2153 % %
2154 % %
2155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2156 %
2157 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2158 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2159 % Create it with the HALD coder. You can apply any color transformation to
2160 % the Hald image and then use this method to apply the transform to the
2161 % image.
2162 %
2163 % The format of the HaldClutImage method is:
2164 %
2165 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2166 % ExceptionInfo *exception)
2167 %
2168 % A description of each parameter follows:
2169 %
2170 % o image: the image, which is replaced by indexed CLUT values
2171 %
2172 % o hald_image: the color lookup table image for replacement color values.
2173 %
2174 % o exception: return any errors or warnings in this structure.
2175 %
2176 */
2178  const Image *hald_image,ExceptionInfo *exception)
2179 {
2180 #define HaldClutImageTag "Clut/Image"
2181 
2182  typedef struct _HaldInfo
2183  {
2184  double
2185  x,
2186  y,
2187  z;
2188  } HaldInfo;
2189 
2190  CacheView
2191  *hald_view,
2192  *image_view;
2193 
2194  double
2195  width;
2196 
2198  status;
2199 
2201  progress;
2202 
2203  PixelInfo
2204  zero;
2205 
2206  size_t
2207  cube_size,
2208  length,
2209  level;
2210 
2211  ssize_t
2212  y;
2213 
2214  assert(image != (Image *) NULL);
2215  assert(image->signature == MagickCoreSignature);
2216  if (image->debug != MagickFalse)
2217  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2218  assert(hald_image != (Image *) NULL);
2219  assert(hald_image->signature == MagickCoreSignature);
2220  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2221  return(MagickFalse);
2222  if (image->alpha_trait == UndefinedPixelTrait)
2223  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2224  /*
2225  Hald clut image.
2226  */
2227  status=MagickTrue;
2228  progress=0;
2229  length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2230  (MagickRealType) hald_image->rows);
2231  for (level=2; (level*level*level) < length; level++) ;
2232  level*=level;
2233  cube_size=level*level;
2234  width=(double) hald_image->columns;
2235  GetPixelInfo(hald_image,&zero);
2236  hald_view=AcquireVirtualCacheView(hald_image,exception);
2237  image_view=AcquireAuthenticCacheView(image,exception);
2238 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2239  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2240  magick_number_threads(image,image,image->rows,1)
2241 #endif
2242  for (y=0; y < (ssize_t) image->rows; y++)
2243  {
2244  register Quantum
2245  *magick_restrict q;
2246 
2247  register ssize_t
2248  x;
2249 
2250  if (status == MagickFalse)
2251  continue;
2252  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2253  if (q == (Quantum *) NULL)
2254  {
2255  status=MagickFalse;
2256  continue;
2257  }
2258  for (x=0; x < (ssize_t) image->columns; x++)
2259  {
2260  double
2261  offset;
2262 
2263  HaldInfo
2264  point;
2265 
2266  PixelInfo
2267  pixel,
2268  pixel1,
2269  pixel2,
2270  pixel3,
2271  pixel4;
2272 
2273  point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2274  point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2275  point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2276  offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2277  point.x-=floor(point.x);
2278  point.y-=floor(point.y);
2279  point.z-=floor(point.z);
2280  pixel1=zero;
2281  (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2282  fmod(offset,width),floor(offset/width),&pixel1,exception);
2283  pixel2=zero;
2284  (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2285  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2286  pixel3=zero;
2287  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2288  point.y,&pixel3);
2289  offset+=cube_size;
2290  (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2291  fmod(offset,width),floor(offset/width),&pixel1,exception);
2292  (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2293  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2294  pixel4=zero;
2295  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2296  point.y,&pixel4);
2297  pixel=zero;
2298  CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2299  point.z,&pixel);
2300  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2301  SetPixelRed(image,ClampToQuantum(pixel.red),q);
2302  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2303  SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2304  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2305  SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2306  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2307  (image->colorspace == CMYKColorspace))
2308  SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2309  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2310  (image->alpha_trait != UndefinedPixelTrait))
2311  SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2312  q+=GetPixelChannels(image);
2313  }
2314  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2315  status=MagickFalse;
2316  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2317  {
2319  proceed;
2320 
2321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2322  #pragma omp critical (MagickCore_HaldClutImage)
2323 #endif
2324  proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2325  if (proceed == MagickFalse)
2326  status=MagickFalse;
2327  }
2328  }
2329  hald_view=DestroyCacheView(hald_view);
2330  image_view=DestroyCacheView(image_view);
2331  return(status);
2332 }
2333 
2334 /*
2335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2336 % %
2337 % %
2338 % %
2339 % L e v e l I m a g e %
2340 % %
2341 % %
2342 % %
2343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2344 %
2345 % LevelImage() adjusts the levels of a particular image channel by
2346 % scaling the colors falling between specified white and black points to
2347 % the full available quantum range.
2348 %
2349 % The parameters provided represent the black, and white points. The black
2350 % point specifies the darkest color in the image. Colors darker than the
2351 % black point are set to zero. White point specifies the lightest color in
2352 % the image. Colors brighter than the white point are set to the maximum
2353 % quantum value.
2354 %
2355 % If a '!' flag is given, map black and white colors to the given levels
2356 % rather than mapping those levels to black and white. See
2357 % LevelizeImage() below.
2358 %
2359 % Gamma specifies a gamma correction to apply to the image.
2360 %
2361 % The format of the LevelImage method is:
2362 %
2363 % MagickBooleanType LevelImage(Image *image,const double black_point,
2364 % const double white_point,const double gamma,ExceptionInfo *exception)
2365 %
2366 % A description of each parameter follows:
2367 %
2368 % o image: the image.
2369 %
2370 % o black_point: The level to map zero (black) to.
2371 %
2372 % o white_point: The level to map QuantumRange (white) to.
2373 %
2374 % o exception: return any errors or warnings in this structure.
2375 %
2376 */
2377 
2378 static inline double LevelPixel(const double black_point,
2379  const double white_point,const double gamma,const double pixel)
2380 {
2381  double
2382  level_pixel,
2383  scale;
2384 
2385  if (fabs(white_point-black_point) < MagickEpsilon)
2386  return(pixel);
2387  scale=1.0/(white_point-black_point);
2388  level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2389  1.0/gamma);
2390  return(level_pixel);
2391 }
2392 
2393 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2394  const double white_point,const double gamma,ExceptionInfo *exception)
2395 {
2396 #define LevelImageTag "Level/Image"
2397 
2398  CacheView
2399  *image_view;
2400 
2402  status;
2403 
2405  progress;
2406 
2407  register ssize_t
2408  i;
2409 
2410  ssize_t
2411  y;
2412 
2413  /*
2414  Allocate and initialize levels map.
2415  */
2416  assert(image != (Image *) NULL);
2417  assert(image->signature == MagickCoreSignature);
2418  if (image->debug != MagickFalse)
2419  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2420  if (image->storage_class == PseudoClass)
2421  for (i=0; i < (ssize_t) image->colors; i++)
2422  {
2423  /*
2424  Level colormap.
2425  */
2426  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2427  image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2428  white_point,gamma,image->colormap[i].red));
2429  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2430  image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2431  white_point,gamma,image->colormap[i].green));
2432  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2433  image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2434  white_point,gamma,image->colormap[i].blue));
2435  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2436  image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2437  white_point,gamma,image->colormap[i].alpha));
2438  }
2439  /*
2440  Level image.
2441  */
2442  status=MagickTrue;
2443  progress=0;
2444  image_view=AcquireAuthenticCacheView(image,exception);
2445 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2446  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2447  magick_number_threads(image,image,image->rows,1)
2448 #endif
2449  for (y=0; y < (ssize_t) image->rows; y++)
2450  {
2451  register Quantum
2452  *magick_restrict q;
2453 
2454  register ssize_t
2455  x;
2456 
2457  if (status == MagickFalse)
2458  continue;
2459  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2460  if (q == (Quantum *) NULL)
2461  {
2462  status=MagickFalse;
2463  continue;
2464  }
2465  for (x=0; x < (ssize_t) image->columns; x++)
2466  {
2467  register ssize_t
2468  j;
2469 
2470  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2471  {
2472  q+=GetPixelChannels(image);
2473  continue;
2474  }
2475  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2476  {
2477  PixelChannel channel = GetPixelChannelChannel(image,j);
2478  PixelTrait traits = GetPixelChannelTraits(image,channel);
2479  if ((traits & UpdatePixelTrait) == 0)
2480  continue;
2481  q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2482  (double) q[j]));
2483  }
2484  q+=GetPixelChannels(image);
2485  }
2486  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2487  status=MagickFalse;
2488  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2489  {
2491  proceed;
2492 
2493 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2494  #pragma omp critical (MagickCore_LevelImage)
2495 #endif
2496  proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2497  if (proceed == MagickFalse)
2498  status=MagickFalse;
2499  }
2500  }
2501  image_view=DestroyCacheView(image_view);
2502  (void) ClampImage(image,exception);
2503  return(status);
2504 }
2505 
2506 /*
2507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2508 % %
2509 % %
2510 % %
2511 % L e v e l i z e I m a g e %
2512 % %
2513 % %
2514 % %
2515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2516 %
2517 % LevelizeImage() applies the reversed LevelImage() operation to just
2518 % the specific channels specified. It compresses the full range of color
2519 % values, so that they lie between the given black and white points. Gamma is
2520 % applied before the values are mapped.
2521 %
2522 % LevelizeImage() can be called with by using a +level command line
2523 % API option, or using a '!' on a -level or LevelImage() geometry string.
2524 %
2525 % It can be used to de-contrast a greyscale image to the exact levels
2526 % specified. Or by using specific levels for each channel of an image you
2527 % can convert a gray-scale image to any linear color gradient, according to
2528 % those levels.
2529 %
2530 % The format of the LevelizeImage method is:
2531 %
2532 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2533 % const double white_point,const double gamma,ExceptionInfo *exception)
2534 %
2535 % A description of each parameter follows:
2536 %
2537 % o image: the image.
2538 %
2539 % o black_point: The level to map zero (black) to.
2540 %
2541 % o white_point: The level to map QuantumRange (white) to.
2542 %
2543 % o gamma: adjust gamma by this factor before mapping values.
2544 %
2545 % o exception: return any errors or warnings in this structure.
2546 %
2547 */
2549  const double black_point,const double white_point,const double gamma,
2550  ExceptionInfo *exception)
2551 {
2552 #define LevelizeImageTag "Levelize/Image"
2553 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2554  (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2555 
2556  CacheView
2557  *image_view;
2558 
2560  status;
2561 
2563  progress;
2564 
2565  register ssize_t
2566  i;
2567 
2568  ssize_t
2569  y;
2570 
2571  /*
2572  Allocate and initialize levels map.
2573  */
2574  assert(image != (Image *) NULL);
2575  assert(image->signature == MagickCoreSignature);
2576  if (image->debug != MagickFalse)
2577  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2578  if (image->storage_class == PseudoClass)
2579  for (i=0; i < (ssize_t) image->colors; i++)
2580  {
2581  /*
2582  Level colormap.
2583  */
2584  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2585  image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2586  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2587  image->colormap[i].green=(double) LevelizeValue(
2588  image->colormap[i].green);
2589  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2590  image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2591  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2592  image->colormap[i].alpha=(double) LevelizeValue(
2593  image->colormap[i].alpha);
2594  }
2595  /*
2596  Level image.
2597  */
2598  status=MagickTrue;
2599  progress=0;
2600  image_view=AcquireAuthenticCacheView(image,exception);
2601 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2602  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2603  magick_number_threads(image,image,image->rows,1)
2604 #endif
2605  for (y=0; y < (ssize_t) image->rows; y++)
2606  {
2607  register Quantum
2608  *magick_restrict q;
2609 
2610  register ssize_t
2611  x;
2612 
2613  if (status == MagickFalse)
2614  continue;
2615  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2616  if (q == (Quantum *) NULL)
2617  {
2618  status=MagickFalse;
2619  continue;
2620  }
2621  for (x=0; x < (ssize_t) image->columns; x++)
2622  {
2623  register ssize_t
2624  j;
2625 
2626  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2627  {
2628  q+=GetPixelChannels(image);
2629  continue;
2630  }
2631  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2632  {
2633  PixelChannel channel = GetPixelChannelChannel(image,j);
2634  PixelTrait traits = GetPixelChannelTraits(image,channel);
2635  if ((traits & UpdatePixelTrait) == 0)
2636  continue;
2637  q[j]=LevelizeValue(q[j]);
2638  }
2639  q+=GetPixelChannels(image);
2640  }
2641  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2642  status=MagickFalse;
2643  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2644  {
2646  proceed;
2647 
2648 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2649  #pragma omp critical (MagickCore_LevelizeImage)
2650 #endif
2651  proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2652  if (proceed == MagickFalse)
2653  status=MagickFalse;
2654  }
2655  }
2656  image_view=DestroyCacheView(image_view);
2657  return(status);
2658 }
2659 
2660 /*
2661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2662 % %
2663 % %
2664 % %
2665 % L e v e l I m a g e C o l o r s %
2666 % %
2667 % %
2668 % %
2669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2670 %
2671 % LevelImageColors() maps the given color to "black" and "white" values,
2672 % linearly spreading out the colors, and level values on a channel by channel
2673 % bases, as per LevelImage(). The given colors allows you to specify
2674 % different level ranges for each of the color channels separately.
2675 %
2676 % If the boolean 'invert' is set true the image values will modifyed in the
2677 % reverse direction. That is any existing "black" and "white" colors in the
2678 % image will become the color values given, with all other values compressed
2679 % appropriatally. This effectivally maps a greyscale gradient into the given
2680 % color gradient.
2681 %
2682 % The format of the LevelImageColors method is:
2683 %
2684 % MagickBooleanType LevelImageColors(Image *image,
2685 % const PixelInfo *black_color,const PixelInfo *white_color,
2686 % const MagickBooleanType invert,ExceptionInfo *exception)
2687 %
2688 % A description of each parameter follows:
2689 %
2690 % o image: the image.
2691 %
2692 % o black_color: The color to map black to/from
2693 %
2694 % o white_point: The color to map white to/from
2695 %
2696 % o invert: if true map the colors (levelize), rather than from (level)
2697 %
2698 % o exception: return any errors or warnings in this structure.
2699 %
2700 */
2702  const PixelInfo *black_color,const PixelInfo *white_color,
2703  const MagickBooleanType invert,ExceptionInfo *exception)
2704 {
2705  ChannelType
2706  channel_mask;
2707 
2709  status;
2710 
2711  /*
2712  Allocate and initialize levels map.
2713  */
2714  assert(image != (Image *) NULL);
2715  assert(image->signature == MagickCoreSignature);
2716  if (image->debug != MagickFalse)
2717  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2718  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
2719  ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
2720  (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
2721  (void) SetImageColorspace(image,sRGBColorspace,exception);
2722  status=MagickTrue;
2723  if (invert == MagickFalse)
2724  {
2725  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2726  {
2727  channel_mask=SetImageChannelMask(image,RedChannel);
2728  status&=LevelImage(image,black_color->red,white_color->red,1.0,
2729  exception);
2730  (void) SetImageChannelMask(image,channel_mask);
2731  }
2732  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2733  {
2734  channel_mask=SetImageChannelMask(image,GreenChannel);
2735  status&=LevelImage(image,black_color->green,white_color->green,1.0,
2736  exception);
2737  (void) SetImageChannelMask(image,channel_mask);
2738  }
2739  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2740  {
2741  channel_mask=SetImageChannelMask(image,BlueChannel);
2742  status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2743  exception);
2744  (void) SetImageChannelMask(image,channel_mask);
2745  }
2746  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2747  (image->colorspace == CMYKColorspace))
2748  {
2749  channel_mask=SetImageChannelMask(image,BlackChannel);
2750  status&=LevelImage(image,black_color->black,white_color->black,1.0,
2751  exception);
2752  (void) SetImageChannelMask(image,channel_mask);
2753  }
2754  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2755  (image->alpha_trait != UndefinedPixelTrait))
2756  {
2757  channel_mask=SetImageChannelMask(image,AlphaChannel);
2758  status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2759  exception);
2760  (void) SetImageChannelMask(image,channel_mask);
2761  }
2762  }
2763  else
2764  {
2765  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2766  {
2767  channel_mask=SetImageChannelMask(image,RedChannel);
2768  status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2769  exception);
2770  (void) SetImageChannelMask(image,channel_mask);
2771  }
2772  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2773  {
2774  channel_mask=SetImageChannelMask(image,GreenChannel);
2775  status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2776  exception);
2777  (void) SetImageChannelMask(image,channel_mask);
2778  }
2779  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2780  {
2781  channel_mask=SetImageChannelMask(image,BlueChannel);
2782  status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2783  exception);
2784  (void) SetImageChannelMask(image,channel_mask);
2785  }
2786  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2787  (image->colorspace == CMYKColorspace))
2788  {
2789  channel_mask=SetImageChannelMask(image,BlackChannel);
2790  status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2791  exception);
2792  (void) SetImageChannelMask(image,channel_mask);
2793  }
2794  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2795  (image->alpha_trait != UndefinedPixelTrait))
2796  {
2797  channel_mask=SetImageChannelMask(image,AlphaChannel);
2798  status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2799  exception);
2800  (void) SetImageChannelMask(image,channel_mask);
2801  }
2802  }
2803  return(status != 0 ? MagickTrue : MagickFalse);
2804 }
2805 
2806 /*
2807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2808 % %
2809 % %
2810 % %
2811 % L i n e a r S t r e t c h I m a g e %
2812 % %
2813 % %
2814 % %
2815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2816 %
2817 % LinearStretchImage() discards any pixels below the black point and above
2818 % the white point and levels the remaining pixels.
2819 %
2820 % The format of the LinearStretchImage method is:
2821 %
2822 % MagickBooleanType LinearStretchImage(Image *image,
2823 % const double black_point,const double white_point,
2824 % ExceptionInfo *exception)
2825 %
2826 % A description of each parameter follows:
2827 %
2828 % o image: the image.
2829 %
2830 % o black_point: the black point.
2831 %
2832 % o white_point: the white point.
2833 %
2834 % o exception: return any errors or warnings in this structure.
2835 %
2836 */
2838  const double black_point,const double white_point,ExceptionInfo *exception)
2839 {
2840 #define LinearStretchImageTag "LinearStretch/Image"
2841 
2842  CacheView
2843  *image_view;
2844 
2845  double
2846  *histogram,
2847  intensity;
2848 
2850  status;
2851 
2852  ssize_t
2853  black,
2854  white,
2855  y;
2856 
2857  /*
2858  Allocate histogram and linear map.
2859  */
2860  assert(image != (Image *) NULL);
2861  assert(image->signature == MagickCoreSignature);
2862  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2863  if (histogram == (double *) NULL)
2864  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2865  image->filename);
2866  /*
2867  Form histogram.
2868  */
2869  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2870  image_view=AcquireVirtualCacheView(image,exception);
2871  for (y=0; y < (ssize_t) image->rows; y++)
2872  {
2873  register const Quantum
2874  *magick_restrict p;
2875 
2876  register ssize_t
2877  x;
2878 
2879  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2880  if (p == (const Quantum *) NULL)
2881  break;
2882  for (x=0; x < (ssize_t) image->columns; x++)
2883  {
2884  intensity=GetPixelIntensity(image,p);
2885  histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2886  p+=GetPixelChannels(image);
2887  }
2888  }
2889  image_view=DestroyCacheView(image_view);
2890  /*
2891  Find the histogram boundaries by locating the black and white point levels.
2892  */
2893  intensity=0.0;
2894  for (black=0; black < (ssize_t) MaxMap; black++)
2895  {
2896  intensity+=histogram[black];
2897  if (intensity >= black_point)
2898  break;
2899  }
2900  intensity=0.0;
2901  for (white=(ssize_t) MaxMap; white != 0; white--)
2902  {
2903  intensity+=histogram[white];
2904  if (intensity >= white_point)
2905  break;
2906  }
2907  histogram=(double *) RelinquishMagickMemory(histogram);
2908  status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
2909  (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
2910  return(status);
2911 }
2912 
2913 
2914 /*
2915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2916 % %
2917 % %
2918 % %
2919 % M o d u l a t e I m a g e %
2920 % %
2921 % %
2922 % %
2923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2924 %
2925 % ModulateImage() lets you control the brightness, saturation, and hue
2926 % of an image. Modulate represents the brightness, saturation, and hue
2927 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2928 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2929 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2930 %
2931 % The format of the ModulateImage method is:
2932 %
2933 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2934 % ExceptionInfo *exception)
2935 %
2936 % A description of each parameter follows:
2937 %
2938 % o image: the image.
2939 %
2940 % o modulate: Define the percent change in brightness, saturation, and hue.
2941 %
2942 % o exception: return any errors or warnings in this structure.
2943 %
2944 */
2945 
2946 static inline void ModulateHCL(const double percent_hue,
2947  const double percent_chroma,const double percent_luma,double *red,
2948  double *green,double *blue)
2949 {
2950  double
2951  hue,
2952  luma,
2953  chroma;
2954 
2955  /*
2956  Increase or decrease color luma, chroma, or hue.
2957  */
2958  ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2959  hue+=fmod((percent_hue-100.0),200.0)/200.0;
2960  chroma*=0.01*percent_chroma;
2961  luma*=0.01*percent_luma;
2962  ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2963 }
2964 
2965 static inline void ModulateHCLp(const double percent_hue,
2966  const double percent_chroma,const double percent_luma,double *red,
2967  double *green,double *blue)
2968 {
2969  double
2970  hue,
2971  luma,
2972  chroma;
2973 
2974  /*
2975  Increase or decrease color luma, chroma, or hue.
2976  */
2977  ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2978  hue+=fmod((percent_hue-100.0),200.0)/200.0;
2979  chroma*=0.01*percent_chroma;
2980  luma*=0.01*percent_luma;
2981  ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2982 }
2983 
2984 static inline void ModulateHSB(const double percent_hue,
2985  const double percent_saturation,const double percent_brightness,double *red,
2986  double *green,double *blue)
2987 {
2988  double
2989  brightness,
2990  hue,
2991  saturation;
2992 
2993  /*
2994  Increase or decrease color brightness, saturation, or hue.
2995  */
2996  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2997  hue+=fmod((percent_hue-100.0),200.0)/200.0;
2998  saturation*=0.01*percent_saturation;
2999  brightness*=0.01*percent_brightness;
3000  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3001 }
3002 
3003 static inline void ModulateHSI(const double percent_hue,
3004  const double percent_saturation,const double percent_intensity,double *red,
3005  double *green,double *blue)
3006 {
3007  double
3008  intensity,
3009  hue,
3010  saturation;
3011 
3012  /*
3013  Increase or decrease color intensity, saturation, or hue.
3014  */
3015  ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3016  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3017  saturation*=0.01*percent_saturation;
3018  intensity*=0.01*percent_intensity;
3019  ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3020 }
3021 
3022 static inline void ModulateHSL(const double percent_hue,
3023  const double percent_saturation,const double percent_lightness,double *red,
3024  double *green,double *blue)
3025 {
3026  double
3027  hue,
3028  lightness,
3029  saturation;
3030 
3031  /*
3032  Increase or decrease color lightness, saturation, or hue.
3033  */
3034  ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3035  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3036  saturation*=0.01*percent_saturation;
3037  lightness*=0.01*percent_lightness;
3038  ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3039 }
3040 
3041 static inline void ModulateHSV(const double percent_hue,
3042  const double percent_saturation,const double percent_value,double *red,
3043  double *green,double *blue)
3044 {
3045  double
3046  hue,
3047  saturation,
3048  value;
3049 
3050  /*
3051  Increase or decrease color value, saturation, or hue.
3052  */
3053  ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3054  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3055  saturation*=0.01*percent_saturation;
3056  value*=0.01*percent_value;
3057  ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3058 }
3059 
3060 static inline void ModulateHWB(const double percent_hue,
3061  const double percent_whiteness,const double percent_blackness,double *red,
3062  double *green,double *blue)
3063 {
3064  double
3065  blackness,
3066  hue,
3067  whiteness;
3068 
3069  /*
3070  Increase or decrease color blackness, whiteness, or hue.
3071  */
3072  ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3073  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3074  blackness*=0.01*percent_blackness;
3075  whiteness*=0.01*percent_whiteness;
3076  ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3077 }
3078 
3079 static inline void ModulateLCHab(const double percent_luma,
3080  const double percent_chroma,const double percent_hue,double *red,
3081  double *green,double *blue)
3082 {
3083  double
3084  hue,
3085  luma,
3086  chroma;
3087 
3088  /*
3089  Increase or decrease color luma, chroma, or hue.
3090  */
3091  ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3092  luma*=0.01*percent_luma;
3093  chroma*=0.01*percent_chroma;
3094  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3095  ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3096 }
3097 
3098 static inline void ModulateLCHuv(const double percent_luma,
3099  const double percent_chroma,const double percent_hue,double *red,
3100  double *green,double *blue)
3101 {
3102  double
3103  hue,
3104  luma,
3105  chroma;
3106 
3107  /*
3108  Increase or decrease color luma, chroma, or hue.
3109  */
3110  ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3111  luma*=0.01*percent_luma;
3112  chroma*=0.01*percent_chroma;
3113  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3114  ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3115 }
3116 
3118  ExceptionInfo *exception)
3119 {
3120 #define ModulateImageTag "Modulate/Image"
3121 
3122  CacheView
3123  *image_view;
3124 
3126  colorspace;
3127 
3128  const char
3129  *artifact;
3130 
3131  double
3132  percent_brightness,
3133  percent_hue,
3134  percent_saturation;
3135 
3136  GeometryInfo
3137  geometry_info;
3138 
3140  status;
3141 
3143  progress;
3144 
3146  flags;
3147 
3148  register ssize_t
3149  i;
3150 
3151  ssize_t
3152  y;
3153 
3154  /*
3155  Initialize modulate table.
3156  */
3157  assert(image != (Image *) NULL);
3158  assert(image->signature == MagickCoreSignature);
3159  if (image->debug != MagickFalse)
3160  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3161  if (modulate == (char *) NULL)
3162  return(MagickFalse);
3164  (void) SetImageColorspace(image,sRGBColorspace,exception);
3165  flags=ParseGeometry(modulate,&geometry_info);
3166  percent_brightness=geometry_info.rho;
3167  percent_saturation=geometry_info.sigma;
3168  if ((flags & SigmaValue) == 0)
3169  percent_saturation=100.0;
3170  percent_hue=geometry_info.xi;
3171  if ((flags & XiValue) == 0)
3172  percent_hue=100.0;
3173  colorspace=UndefinedColorspace;
3174  artifact=GetImageArtifact(image,"modulate:colorspace");
3175  if (artifact != (const char *) NULL)
3177  MagickFalse,artifact);
3178  if (image->storage_class == PseudoClass)
3179  for (i=0; i < (ssize_t) image->colors; i++)
3180  {
3181  double
3182  blue,
3183  green,
3184  red;
3185 
3186  /*
3187  Modulate image colormap.
3188  */
3189  red=(double) image->colormap[i].red;
3190  green=(double) image->colormap[i].green;
3191  blue=(double) image->colormap[i].blue;
3192  switch (colorspace)
3193  {
3194  case HCLColorspace:
3195  {
3196  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3197  &red,&green,&blue);
3198  break;
3199  }
3200  case HCLpColorspace:
3201  {
3202  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3203  &red,&green,&blue);
3204  break;
3205  }
3206  case HSBColorspace:
3207  {
3208  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3209  &red,&green,&blue);
3210  break;
3211  }
3212  case HSIColorspace:
3213  {
3214  ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3215  &red,&green,&blue);
3216  break;
3217  }
3218  case HSLColorspace:
3219  default:
3220  {
3221  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3222  &red,&green,&blue);
3223  break;
3224  }
3225  case HSVColorspace:
3226  {
3227  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3228  &red,&green,&blue);
3229  break;
3230  }
3231  case HWBColorspace:
3232  {
3233  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3234  &red,&green,&blue);
3235  break;
3236  }
3237  case LCHColorspace:
3238  case LCHabColorspace:
3239  {
3240  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3241  &red,&green,&blue);
3242  break;
3243  }
3244  case LCHuvColorspace:
3245  {
3246  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3247  &red,&green,&blue);
3248  break;
3249  }
3250  }
3251  image->colormap[i].red=red;
3252  image->colormap[i].green=green;
3253  image->colormap[i].blue=blue;
3254  }
3255  /*
3256  Modulate image.
3257  */
3258 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3259  if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3260  percent_saturation,colorspace,exception) != MagickFalse)
3261  return(MagickTrue);
3262 #endif
3263  status=MagickTrue;
3264  progress=0;
3265  image_view=AcquireAuthenticCacheView(image,exception);
3266 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3267  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3268  magick_number_threads(image,image,image->rows,1)
3269 #endif
3270  for (y=0; y < (ssize_t) image->rows; y++)
3271  {
3272  register Quantum
3273  *magick_restrict q;
3274 
3275  register ssize_t
3276  x;
3277 
3278  if (status == MagickFalse)
3279  continue;
3280  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3281  if (q == (Quantum *) NULL)
3282  {
3283  status=MagickFalse;
3284  continue;
3285  }
3286  for (x=0; x < (ssize_t) image->columns; x++)
3287  {
3288  double
3289  blue,
3290  green,
3291  red;
3292 
3293  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3294  {
3295  q+=GetPixelChannels(image);
3296  continue;
3297  }
3298  red=(double) GetPixelRed(image,q);
3299  green=(double) GetPixelGreen(image,q);
3300  blue=(double) GetPixelBlue(image,q);
3301  switch (colorspace)
3302  {
3303  case HCLColorspace:
3304  {
3305  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3306  &red,&green,&blue);
3307  break;
3308  }
3309  case HCLpColorspace:
3310  {
3311  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3312  &red,&green,&blue);
3313  break;
3314  }
3315  case HSBColorspace:
3316  {
3317  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3318  &red,&green,&blue);
3319  break;
3320  }
3321  case HSLColorspace:
3322  default:
3323  {
3324  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3325  &red,&green,&blue);
3326  break;
3327  }
3328  case HSVColorspace:
3329  {
3330  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3331  &red,&green,&blue);
3332  break;
3333  }
3334  case HWBColorspace:
3335  {
3336  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3337  &red,&green,&blue);
3338  break;
3339  }
3340  case LCHabColorspace:
3341  {
3342  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3343  &red,&green,&blue);
3344  break;
3345  }
3346  case LCHColorspace:
3347  case LCHuvColorspace:
3348  {
3349  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3350  &red,&green,&blue);
3351  break;
3352  }
3353  }
3354  SetPixelRed(image,ClampToQuantum(red),q);
3355  SetPixelGreen(image,ClampToQuantum(green),q);
3356  SetPixelBlue(image,ClampToQuantum(blue),q);
3357  q+=GetPixelChannels(image);
3358  }
3359  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3360  status=MagickFalse;
3361  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3362  {
3364  proceed;
3365 
3366 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3367  #pragma omp critical (MagickCore_ModulateImage)
3368 #endif
3369  proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3370  if (proceed == MagickFalse)
3371  status=MagickFalse;
3372  }
3373  }
3374  image_view=DestroyCacheView(image_view);
3375  return(status);
3376 }
3377 
3378 /*
3379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3380 % %
3381 % %
3382 % %
3383 % N e g a t e I m a g e %
3384 % %
3385 % %
3386 % %
3387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3388 %
3389 % NegateImage() negates the colors in the reference image. The grayscale
3390 % option means that only grayscale values within the image are negated.
3391 %
3392 % The format of the NegateImage method is:
3393 %
3394 % MagickBooleanType NegateImage(Image *image,
3395 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3396 %
3397 % A description of each parameter follows:
3398 %
3399 % o image: the image.
3400 %
3401 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3402 %
3403 % o exception: return any errors or warnings in this structure.
3404 %
3405 */
3407  const MagickBooleanType grayscale,ExceptionInfo *exception)
3408 {
3409 #define NegateImageTag "Negate/Image"
3410 
3411  CacheView
3412  *image_view;
3413 
3415  status;
3416 
3418  progress;
3419 
3420  register ssize_t
3421  i;
3422 
3423  ssize_t
3424  y;
3425 
3426  assert(image != (Image *) NULL);
3427  assert(image->signature == MagickCoreSignature);
3428  if (image->debug != MagickFalse)
3429  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3430  if (image->storage_class == PseudoClass)
3431  for (i=0; i < (ssize_t) image->colors; i++)
3432  {
3433  /*
3434  Negate colormap.
3435  */
3436  if( grayscale != MagickFalse )
3437  if ((image->colormap[i].red != image->colormap[i].green) ||
3438  (image->colormap[i].green != image->colormap[i].blue))
3439  continue;
3440  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3441  image->colormap[i].red=QuantumRange-image->colormap[i].red;
3442  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3443  image->colormap[i].green=QuantumRange-image->colormap[i].green;
3444  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3445  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3446  }
3447  /*
3448  Negate image.
3449  */
3450  status=MagickTrue;
3451  progress=0;
3452  image_view=AcquireAuthenticCacheView(image,exception);
3453  if( grayscale != MagickFalse )
3454  {
3455  for (y=0; y < (ssize_t) image->rows; y++)
3456  {
3458  sync;
3459 
3460  register Quantum
3461  *magick_restrict q;
3462 
3463  register ssize_t
3464  x;
3465 
3466  if (status == MagickFalse)
3467  continue;
3468  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3469  exception);
3470  if (q == (Quantum *) NULL)
3471  {
3472  status=MagickFalse;
3473  continue;
3474  }
3475  for (x=0; x < (ssize_t) image->columns; x++)
3476  {
3477  register ssize_t
3478  j;
3479 
3480  if ((GetPixelWriteMask(image,q) <= (QuantumRange/2)) ||
3481  IsPixelGray(image,q) != MagickFalse)
3482  {
3483  q+=GetPixelChannels(image);
3484  continue;
3485  }
3486  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3487  {
3488  PixelChannel channel = GetPixelChannelChannel(image,j);
3489  PixelTrait traits = GetPixelChannelTraits(image,channel);
3490  if ((traits & UpdatePixelTrait) == 0)
3491  continue;
3492  q[j]=QuantumRange-q[j];
3493  }
3494  q+=GetPixelChannels(image);
3495  }
3496  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3497  if (sync == MagickFalse)
3498  status=MagickFalse;
3499  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3500  {
3502  proceed;
3503 
3504 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3505  #pragma omp critical (MagickCore_NegateImage)
3506 #endif
3507  proceed=SetImageProgress(image,NegateImageTag,progress++,
3508  image->rows);
3509  if (proceed == MagickFalse)
3510  status=MagickFalse;
3511  }
3512  }
3513  image_view=DestroyCacheView(image_view);
3514  return(MagickTrue);
3515  }
3516  /*
3517  Negate image.
3518  */
3519 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3520  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3521  magick_number_threads(image,image,image->rows,1)
3522 #endif
3523  for (y=0; y < (ssize_t) image->rows; y++)
3524  {
3525  register Quantum
3526  *magick_restrict q;
3527 
3528  register ssize_t
3529  x;
3530 
3531  if (status == MagickFalse)
3532  continue;
3533  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3534  if (q == (Quantum *) NULL)
3535  {
3536  status=MagickFalse;
3537  continue;
3538  }
3539  for (x=0; x < (ssize_t) image->columns; x++)
3540  {
3541  register ssize_t
3542  j;
3543 
3544  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3545  {
3546  q+=GetPixelChannels(image);
3547  continue;
3548  }
3549  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3550  {
3551  PixelChannel channel = GetPixelChannelChannel(image,j);
3552  PixelTrait traits = GetPixelChannelTraits(image,channel);
3553  if ((traits & UpdatePixelTrait) == 0)
3554  continue;
3555  q[j]=QuantumRange-q[j];
3556  }
3557  q+=GetPixelChannels(image);
3558  }
3559  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3560  status=MagickFalse;
3561  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3562  {
3564  proceed;
3565 
3566 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3567  #pragma omp critical (MagickCore_NegateImage)
3568 #endif
3569  proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3570  if (proceed == MagickFalse)
3571  status=MagickFalse;
3572  }
3573  }
3574  image_view=DestroyCacheView(image_view);
3575  return(status);
3576 }
3577 
3578 /*
3579 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3580 % %
3581 % %
3582 % %
3583 % N o r m a l i z e I m a g e %
3584 % %
3585 % %
3586 % %
3587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3588 %
3589 % The NormalizeImage() method enhances the contrast of a color image by
3590 % mapping the darkest 2 percent of all pixel to black and the brightest
3591 % 1 percent to white.
3592 %
3593 % The format of the NormalizeImage method is:
3594 %
3595 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3596 %
3597 % A description of each parameter follows:
3598 %
3599 % o image: the image.
3600 %
3601 % o exception: return any errors or warnings in this structure.
3602 %
3603 */
3605  ExceptionInfo *exception)
3606 {
3607  double
3608  black_point,
3609  white_point;
3610 
3611  black_point=(double) image->columns*image->rows*0.0015;
3612  white_point=(double) image->columns*image->rows*0.9995;
3613  return(ContrastStretchImage(image,black_point,white_point,exception));
3614 }
3615 
3616 /*
3617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3618 % %
3619 % %
3620 % %
3621 % S i g m o i d a l C o n t r a s t I m a g e %
3622 % %
3623 % %
3624 % %
3625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3626 %
3627 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3628 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3629 % sigmoidal transfer function without saturating highlights or shadows.
3630 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3631 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3632 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3633 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3634 % is reduced.
3635 %
3636 % The format of the SigmoidalContrastImage method is:
3637 %
3638 % MagickBooleanType SigmoidalContrastImage(Image *image,
3639 % const MagickBooleanType sharpen,const char *levels,
3640 % ExceptionInfo *exception)
3641 %
3642 % A description of each parameter follows:
3643 %
3644 % o image: the image.
3645 %
3646 % o sharpen: Increase or decrease image contrast.
3647 %
3648 % o contrast: strength of the contrast, the larger the number the more
3649 % 'threshold-like' it becomes.
3650 %
3651 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3652 %
3653 % o exception: return any errors or warnings in this structure.
3654 %
3655 */
3656 
3657 /*
3658  ImageMagick 6 has a version of this function which uses LUTs.
3659 */
3660 
3661 /*
3662  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3663  constant" set to a.
3664 
3665  The first version, based on the hyperbolic tangent tanh, when combined with
3666  the scaling step, is an exact arithmetic clone of the the sigmoid function
3667  based on the logistic curve. The equivalence is based on the identity
3668 
3669  1/(1+exp(-t)) = (1+tanh(t/2))/2
3670 
3671  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3672  scaled sigmoidal derivation is invariant under affine transformations of
3673  the ordinate.
3674 
3675  The tanh version is almost certainly more accurate and cheaper. The 0.5
3676  factor in the argument is to clone the legacy ImageMagick behavior. The
3677  reason for making the define depend on atanh even though it only uses tanh
3678  has to do with the construction of the inverse of the scaled sigmoidal.
3679 */
3680 #if defined(MAGICKCORE_HAVE_ATANH)
3681 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3682 #else
3683 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3684 #endif
3685 /*
3686  Scaled sigmoidal function:
3687 
3688  ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3689  ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3690 
3691  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3692  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3693  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3694  zero. This is fixed below by exiting immediately when contrast is small,
3695  leaving the image (or colormap) unmodified. This appears to be safe because
3696  the series expansion of the logistic sigmoidal function around x=b is
3697 
3698  1/2-a*(b-x)/4+...
3699 
3700  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3701 */
3702 #define ScaledSigmoidal(a,b,x) ( \
3703  (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3704  (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3705 /*
3706  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3707  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3708  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3709  when creating a LUT from in gamut values, hence the branching. In
3710  addition, HDRI may have out of gamut values.
3711  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3712  It is only a right inverse. This is unavoidable.
3713 */
3714 static inline double InverseScaledSigmoidal(const double a,const double b,
3715  const double x)
3716 {
3717  const double sig0=Sigmoidal(a,b,0.0);
3718  const double sig1=Sigmoidal(a,b,1.0);
3719  const double argument=(sig1-sig0)*x+sig0;
3720  const double clamped=
3721  (
3722 #if defined(MAGICKCORE_HAVE_ATANH)
3723  argument < -1+MagickEpsilon
3724  ?
3725  -1+MagickEpsilon
3726  :
3727  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3728  );
3729  return(b+(2.0/a)*atanh(clamped));
3730 #else
3731  argument < MagickEpsilon
3732  ?
3734  :
3735  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3736  );
3737  return(b-log(1.0/clamped-1.0)/a);
3738 #endif
3739 }
3740 
3742  const MagickBooleanType sharpen,const double contrast,const double midpoint,
3743  ExceptionInfo *exception)
3744 {
3745 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3746 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3747  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3748 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3749  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3750 
3751  CacheView
3752  *image_view;
3753 
3755  status;
3756 
3758  progress;
3759 
3760  ssize_t
3761  y;
3762 
3763  /*
3764  Convenience macros.
3765  */
3766  assert(image != (Image *) NULL);
3767  assert(image->signature == MagickCoreSignature);
3768  if (image->debug != MagickFalse)
3769  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3770  /*
3771  Side effect: may clamp values unless contrast<MagickEpsilon, in which
3772  case nothing is done.
3773  */
3774  if (contrast < MagickEpsilon)
3775  return(MagickTrue);
3776  /*
3777  Sigmoidal-contrast enhance colormap.
3778  */
3779  if (image->storage_class == PseudoClass)
3780  {
3781  register ssize_t
3782  i;
3783 
3784  if( sharpen != MagickFalse )
3785  for (i=0; i < (ssize_t) image->colors; i++)
3786  {
3787  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3788  image->colormap[i].red=(MagickRealType) ScaledSig(
3789  image->colormap[i].red);
3790  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3791  image->colormap[i].green=(MagickRealType) ScaledSig(
3792  image->colormap[i].green);
3793  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3794  image->colormap[i].blue=(MagickRealType) ScaledSig(
3795  image->colormap[i].blue);
3796  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3797  image->colormap[i].alpha=(MagickRealType) ScaledSig(
3798  image->colormap[i].alpha);
3799  }
3800  else
3801  for (i=0; i < (ssize_t) image->colors; i++)
3802  {
3803  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3805  image->colormap[i].red);
3806  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3808  image->colormap[i].green);
3809  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3811  image->colormap[i].blue);
3812  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3814  image->colormap[i].alpha);
3815  }
3816  }
3817  /*
3818  Sigmoidal-contrast enhance image.
3819  */
3820  status=MagickTrue;
3821  progress=0;
3822  image_view=AcquireAuthenticCacheView(image,exception);
3823 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3824  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3825  magick_number_threads(image,image,image->rows,1)
3826 #endif
3827  for (y=0; y < (ssize_t) image->rows; y++)
3828  {
3829  register Quantum
3830  *magick_restrict q;
3831 
3832  register ssize_t
3833  x;
3834 
3835  if (status == MagickFalse)
3836  continue;
3837  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3838  if (q == (Quantum *) NULL)
3839  {
3840  status=MagickFalse;
3841  continue;
3842  }
3843  for (x=0; x < (ssize_t) image->columns; x++)
3844  {
3845  register ssize_t
3846  i;
3847 
3848  if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
3849  {
3850  q+=GetPixelChannels(image);
3851  continue;
3852  }
3853  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3854  {
3855  PixelChannel channel = GetPixelChannelChannel(image,i);
3856  PixelTrait traits = GetPixelChannelTraits(image,channel);
3857  if ((traits & UpdatePixelTrait) == 0)
3858  continue;
3859  if( sharpen != MagickFalse )
3860  q[i]=ScaledSig(q[i]);
3861  else
3862  q[i]=InverseScaledSig(q[i]);
3863  }
3864  q+=GetPixelChannels(image);
3865  }
3866  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3867  status=MagickFalse;
3868  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3869  {
3871  proceed;
3872 
3873 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3874  #pragma omp critical (MagickCore_SigmoidalContrastImage)
3875 #endif
3876  proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3877  image->rows);
3878  if (proceed == MagickFalse)
3879  status=MagickFalse;
3880  }
3881  }
3882  image_view=DestroyCacheView(image_view);
3883  return(status);
3884 }
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:3406
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:2548
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:2837
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:3060
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:3683
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:2954
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:2965
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:2378
#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:5479
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:3022
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:2170
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:1214
#define LevelizeImageTag
MagickExport MagickRealType DecodePixelGamma(const MagickRealType pixel)
Definition: pixel.c:319
MagickExport MagickBooleanType ContrastImage(Image *image, const MagickBooleanType sharpen, ExceptionInfo *exception)
Definition: enhance.c: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:967
MagickBooleanType
Definition: magick-type.h:156
unsigned int MagickStatusType
Definition: magick-type.h:119
static double PerceptibleReciprocal(const double x)
MagickExport Image * EnhanceImage(const Image *image, ExceptionInfo *exception)
Definition: enhance.c: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:3604
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:3098
MagickExport MagickBooleanType ModulateImage(Image *image, const char *modulate, ExceptionInfo *exception)
Definition: enhance.c:3117
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:3079
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:2946
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:3003
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:2701
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:1134
MagickExport MagickBooleanType LevelImage(Image *image, const double black_point, const double white_point, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:2393
#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:3041
static void ModulateHSB(const double percent_hue, const double percent_saturation, const double percent_brightness, double *red, double *green, double *blue)
Definition: enhance.c:2984
#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:2177
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:3741
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:3714
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:2364
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)