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