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