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