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,
3641  percent_hue,
3642  percent_saturation;
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  percent_brightness=geometry_info.rho;
3678  percent_saturation=geometry_info.sigma;
3679  if ((flags & SigmaValue) == 0)
3680  percent_saturation=100.0;
3681  percent_hue=geometry_info.xi;
3682  if ((flags & XiValue) == 0)
3683  percent_hue=100.0;
3684  artifact=GetImageArtifact(image,"modulate:colorspace");
3685  if (artifact != (const char *) NULL)
3686  {
3688  MagickFalse,artifact);
3689  if ((ssize_t) illuminant < 0)
3690  colorspace=UndefinedColorspace;
3691  }
3692  artifact=GetImageArtifact(image,"color:illuminant");
3693  if (artifact != (const char *) NULL)
3694  {
3696  MagickFalse,artifact);
3697  if ((ssize_t) illuminant < 0)
3698  illuminant=UndefinedIlluminant;
3699  }
3700  if (image->storage_class == PseudoClass)
3701  for (i=0; i < (ssize_t) image->colors; i++)
3702  {
3703  double
3704  blue,
3705  green,
3706  red;
3707 
3708  /*
3709  Modulate image colormap.
3710  */
3711  red=(double) image->colormap[i].red;
3712  green=(double) image->colormap[i].green;
3713  blue=(double) image->colormap[i].blue;
3714  switch (colorspace)
3715  {
3716  case HCLColorspace:
3717  {
3718  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3719  &red,&green,&blue);
3720  break;
3721  }
3722  case HCLpColorspace:
3723  {
3724  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3725  &red,&green,&blue);
3726  break;
3727  }
3728  case HSBColorspace:
3729  {
3730  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3731  &red,&green,&blue);
3732  break;
3733  }
3734  case HSIColorspace:
3735  {
3736  ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3737  &red,&green,&blue);
3738  break;
3739  }
3740  case HSLColorspace:
3741  default:
3742  {
3743  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3744  &red,&green,&blue);
3745  break;
3746  }
3747  case HSVColorspace:
3748  {
3749  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3750  &red,&green,&blue);
3751  break;
3752  }
3753  case HWBColorspace:
3754  {
3755  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3756  &red,&green,&blue);
3757  break;
3758  }
3759  case LCHColorspace:
3760  case LCHabColorspace:
3761  {
3762  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3763  illuminant,&red,&green,&blue);
3764  break;
3765  }
3766  case LCHuvColorspace:
3767  {
3768  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3769  illuminant,&red,&green,&blue);
3770  break;
3771  }
3772  }
3773  image->colormap[i].red=red;
3774  image->colormap[i].green=green;
3775  image->colormap[i].blue=blue;
3776  }
3777  /*
3778  Modulate image.
3779  */
3780 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3781  if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3782  percent_saturation,colorspace,exception) != MagickFalse)
3783  return(MagickTrue);
3784 #endif
3785  status=MagickTrue;
3786  progress=0;
3787  image_view=AcquireAuthenticCacheView(image,exception);
3788 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3789  #pragma omp parallel for schedule(static) shared(progress,status) \
3790  magick_number_threads(image,image,image->rows,1)
3791 #endif
3792  for (y=0; y < (ssize_t) image->rows; y++)
3793  {
3794  Quantum
3795  *magick_restrict q;
3796 
3797  ssize_t
3798  x;
3799 
3800  if (status == MagickFalse)
3801  continue;
3802  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3803  if (q == (Quantum *) NULL)
3804  {
3805  status=MagickFalse;
3806  continue;
3807  }
3808  for (x=0; x < (ssize_t) image->columns; x++)
3809  {
3810  double
3811  blue,
3812  green,
3813  red;
3814 
3815  red=(double) GetPixelRed(image,q);
3816  green=(double) GetPixelGreen(image,q);
3817  blue=(double) GetPixelBlue(image,q);
3818  switch (colorspace)
3819  {
3820  case HCLColorspace:
3821  {
3822  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3823  &red,&green,&blue);
3824  break;
3825  }
3826  case HCLpColorspace:
3827  {
3828  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3829  &red,&green,&blue);
3830  break;
3831  }
3832  case HSBColorspace:
3833  {
3834  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3835  &red,&green,&blue);
3836  break;
3837  }
3838  case HSLColorspace:
3839  default:
3840  {
3841  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3842  &red,&green,&blue);
3843  break;
3844  }
3845  case HSVColorspace:
3846  {
3847  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3848  &red,&green,&blue);
3849  break;
3850  }
3851  case HWBColorspace:
3852  {
3853  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3854  &red,&green,&blue);
3855  break;
3856  }
3857  case LCHabColorspace:
3858  {
3859  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3860  illuminant,&red,&green,&blue);
3861  break;
3862  }
3863  case LCHColorspace:
3864  case LCHuvColorspace:
3865  {
3866  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3867  illuminant,&red,&green,&blue);
3868  break;
3869  }
3870  }
3871  SetPixelRed(image,ClampToQuantum(red),q);
3872  SetPixelGreen(image,ClampToQuantum(green),q);
3873  SetPixelBlue(image,ClampToQuantum(blue),q);
3874  q+=GetPixelChannels(image);
3875  }
3876  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3877  status=MagickFalse;
3878  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3879  {
3881  proceed;
3882 
3883 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3884  #pragma omp atomic
3885 #endif
3886  progress++;
3887  proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3888  if (proceed == MagickFalse)
3889  status=MagickFalse;
3890  }
3891  }
3892  image_view=DestroyCacheView(image_view);
3893  return(status);
3894 }
3895 
3896 /*
3897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3898 % %
3899 % %
3900 % %
3901 % N e g a t e I m a g e %
3902 % %
3903 % %
3904 % %
3905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3906 %
3907 % NegateImage() negates the colors in the reference image. The grayscale
3908 % option means that only grayscale values within the image are negated.
3909 %
3910 % The format of the NegateImage method is:
3911 %
3912 % MagickBooleanType NegateImage(Image *image,
3913 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3914 %
3915 % A description of each parameter follows:
3916 %
3917 % o image: the image.
3918 %
3919 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3920 %
3921 % o exception: return any errors or warnings in this structure.
3922 %
3923 */
3925  const MagickBooleanType grayscale,ExceptionInfo *exception)
3926 {
3927 #define NegateImageTag "Negate/Image"
3928 
3929  CacheView
3930  *image_view;
3931 
3933  status;
3934 
3936  progress;
3937 
3938  ssize_t
3939  i;
3940 
3941  ssize_t
3942  y;
3943 
3944  assert(image != (Image *) NULL);
3945  assert(image->signature == MagickCoreSignature);
3946  if (image->debug != MagickFalse)
3947  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3948  if (image->storage_class == PseudoClass)
3949  for (i=0; i < (ssize_t) image->colors; i++)
3950  {
3951  /*
3952  Negate colormap.
3953  */
3954  if (grayscale != MagickFalse)
3955  if ((image->colormap[i].red != image->colormap[i].green) ||
3956  (image->colormap[i].green != image->colormap[i].blue))
3957  continue;
3958  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3959  image->colormap[i].red=QuantumRange-image->colormap[i].red;
3960  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3961  image->colormap[i].green=QuantumRange-image->colormap[i].green;
3962  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3963  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3964  }
3965  /*
3966  Negate image.
3967  */
3968  status=MagickTrue;
3969  progress=0;
3970  image_view=AcquireAuthenticCacheView(image,exception);
3971  if( grayscale != MagickFalse )
3972  {
3973  for (y=0; y < (ssize_t) image->rows; y++)
3974  {
3976  sync;
3977 
3978  Quantum
3979  *magick_restrict q;
3980 
3981  ssize_t
3982  x;
3983 
3984  if (status == MagickFalse)
3985  continue;
3986  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3987  exception);
3988  if (q == (Quantum *) NULL)
3989  {
3990  status=MagickFalse;
3991  continue;
3992  }
3993  for (x=0; x < (ssize_t) image->columns; x++)
3994  {
3995  ssize_t
3996  j;
3997 
3998  if (IsPixelGray(image,q) == MagickFalse)
3999  {
4000  q+=GetPixelChannels(image);
4001  continue;
4002  }
4003  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4004  {
4005  PixelChannel channel = GetPixelChannelChannel(image,j);
4006  PixelTrait traits = GetPixelChannelTraits(image,channel);
4007  if ((traits & UpdatePixelTrait) == 0)
4008  continue;
4009  q[j]=QuantumRange-q[j];
4010  }
4011  q+=GetPixelChannels(image);
4012  }
4013  sync=SyncCacheViewAuthenticPixels(image_view,exception);
4014  if (sync == MagickFalse)
4015  status=MagickFalse;
4016  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4017  {
4019  proceed;
4020 
4021  progress++;
4022  proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4023  if (proceed == MagickFalse)
4024  status=MagickFalse;
4025  }
4026  }
4027  image_view=DestroyCacheView(image_view);
4028  return(MagickTrue);
4029  }
4030  /*
4031  Negate image.
4032  */
4033 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4034  #pragma omp parallel for schedule(static) shared(progress,status) \
4035  magick_number_threads(image,image,image->rows,1)
4036 #endif
4037  for (y=0; y < (ssize_t) image->rows; y++)
4038  {
4039  Quantum
4040  *magick_restrict q;
4041 
4042  ssize_t
4043  x;
4044 
4045  if (status == MagickFalse)
4046  continue;
4047  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4048  if (q == (Quantum *) NULL)
4049  {
4050  status=MagickFalse;
4051  continue;
4052  }
4053  for (x=0; x < (ssize_t) image->columns; x++)
4054  {
4055  ssize_t
4056  j;
4057 
4058  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4059  {
4060  PixelChannel channel = GetPixelChannelChannel(image,j);
4061  PixelTrait traits = GetPixelChannelTraits(image,channel);
4062  if ((traits & UpdatePixelTrait) == 0)
4063  continue;
4064  q[j]=QuantumRange-q[j];
4065  }
4066  q+=GetPixelChannels(image);
4067  }
4068  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4069  status=MagickFalse;
4070  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4071  {
4073  proceed;
4074 
4075 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4076  #pragma omp atomic
4077 #endif
4078  progress++;
4079  proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4080  if (proceed == MagickFalse)
4081  status=MagickFalse;
4082  }
4083  }
4084  image_view=DestroyCacheView(image_view);
4085  return(status);
4086 }
4087 
4088 /*
4089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4090 % %
4091 % %
4092 % %
4093 % N o r m a l i z e I m a g e %
4094 % %
4095 % %
4096 % %
4097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4098 %
4099 % The NormalizeImage() method enhances the contrast of a color image by
4100 % mapping the darkest 2 percent of all pixel to black and the brightest
4101 % 1 percent to white.
4102 %
4103 % The format of the NormalizeImage method is:
4104 %
4105 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
4106 %
4107 % A description of each parameter follows:
4108 %
4109 % o image: the image.
4110 %
4111 % o exception: return any errors or warnings in this structure.
4112 %
4113 */
4115  ExceptionInfo *exception)
4116 {
4117  double
4118  black_point,
4119  white_point;
4120 
4121  black_point=(double) image->columns*image->rows*0.0015;
4122  white_point=(double) image->columns*image->rows*0.9995;
4123  return(ContrastStretchImage(image,black_point,white_point,exception));
4124 }
4125 
4126 /*
4127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4128 % %
4129 % %
4130 % %
4131 % S i g m o i d a l C o n t r a s t I m a g e %
4132 % %
4133 % %
4134 % %
4135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4136 %
4137 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
4138 % sigmoidal contrast algorithm. Increase the contrast of the image using a
4139 % sigmoidal transfer function without saturating highlights or shadows.
4140 % Contrast indicates how much to increase the contrast (0 is none; 3 is
4141 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
4142 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
4143 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
4144 % is reduced.
4145 %
4146 % The format of the SigmoidalContrastImage method is:
4147 %
4148 % MagickBooleanType SigmoidalContrastImage(Image *image,
4149 % const MagickBooleanType sharpen,const char *levels,
4150 % ExceptionInfo *exception)
4151 %
4152 % A description of each parameter follows:
4153 %
4154 % o image: the image.
4155 %
4156 % o sharpen: Increase or decrease image contrast.
4157 %
4158 % o contrast: strength of the contrast, the larger the number the more
4159 % 'threshold-like' it becomes.
4160 %
4161 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
4162 %
4163 % o exception: return any errors or warnings in this structure.
4164 %
4165 */
4166 
4167 /*
4168  ImageMagick 6 has a version of this function which uses LUTs.
4169 */
4170 
4171 /*
4172  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
4173  constant" set to a.
4174 
4175  The first version, based on the hyperbolic tangent tanh, when combined with
4176  the scaling step, is an exact arithmetic clone of the sigmoid function
4177  based on the logistic curve. The equivalence is based on the identity
4178 
4179  1/(1+exp(-t)) = (1+tanh(t/2))/2
4180 
4181  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
4182  scaled sigmoidal derivation is invariant under affine transformations of
4183  the ordinate.
4184 
4185  The tanh version is almost certainly more accurate and cheaper. The 0.5
4186  factor in the argument is to clone the legacy ImageMagick behavior. The
4187  reason for making the define depend on atanh even though it only uses tanh
4188  has to do with the construction of the inverse of the scaled sigmoidal.
4189 */
4190 #if defined(MAGICKCORE_HAVE_ATANH)
4191 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4192 #else
4193 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4194 #endif
4195 /*
4196  Scaled sigmoidal function:
4197 
4198  ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
4199  ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
4200 
4201  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
4202  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
4203  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
4204  zero. This is fixed below by exiting immediately when contrast is small,
4205  leaving the image (or colormap) unmodified. This appears to be safe because
4206  the series expansion of the logistic sigmoidal function around x=b is
4207 
4208  1/2-a*(b-x)/4+...
4209 
4210  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
4211 */
4212 #define ScaledSigmoidal(a,b,x) ( \
4213  (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4214  (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4215 /*
4216  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
4217  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
4218  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
4219  when creating a LUT from in gamut values, hence the branching. In
4220  addition, HDRI may have out of gamut values.
4221  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
4222  It is only a right inverse. This is unavoidable.
4223 */
4224 static inline double InverseScaledSigmoidal(const double a,const double b,
4225  const double x)
4226 {
4227  const double sig0=Sigmoidal(a,b,0.0);
4228  const double sig1=Sigmoidal(a,b,1.0);
4229  const double argument=(sig1-sig0)*x+sig0;
4230  const double clamped=
4231  (
4232 #if defined(MAGICKCORE_HAVE_ATANH)
4233  argument < -1+MagickEpsilon
4234  ?
4235  -1+MagickEpsilon
4236  :
4237  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4238  );
4239  return(b+(2.0/a)*atanh(clamped));
4240 #else
4241  argument < MagickEpsilon
4242  ?
4244  :
4245  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4246  );
4247  return(b-log(1.0/clamped-1.0)/a);
4248 #endif
4249 }
4250 
4252  const MagickBooleanType sharpen,const double contrast,const double midpoint,
4253  ExceptionInfo *exception)
4254 {
4255 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4256 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
4257  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4258 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
4259  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4260 
4261  CacheView
4262  *image_view;
4263 
4265  status;
4266 
4268  progress;
4269 
4270  ssize_t
4271  y;
4272 
4273  /*
4274  Convenience macros.
4275  */
4276  assert(image != (Image *) NULL);
4277  assert(image->signature == MagickCoreSignature);
4278  if (image->debug != MagickFalse)
4279  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4280  /*
4281  Side effect: may clamp values unless contrast<MagickEpsilon, in which
4282  case nothing is done.
4283  */
4284  if (contrast < MagickEpsilon)
4285  return(MagickTrue);
4286  /*
4287  Sigmoidal-contrast enhance colormap.
4288  */
4289  if (image->storage_class == PseudoClass)
4290  {
4291  ssize_t
4292  i;
4293 
4294  if( sharpen != MagickFalse )
4295  for (i=0; i < (ssize_t) image->colors; i++)
4296  {
4297  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4298  image->colormap[i].red=(MagickRealType) ScaledSig(
4299  image->colormap[i].red);
4300  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4301  image->colormap[i].green=(MagickRealType) ScaledSig(
4302  image->colormap[i].green);
4303  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4304  image->colormap[i].blue=(MagickRealType) ScaledSig(
4305  image->colormap[i].blue);
4306  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4307  image->colormap[i].alpha=(MagickRealType) ScaledSig(
4308  image->colormap[i].alpha);
4309  }
4310  else
4311  for (i=0; i < (ssize_t) image->colors; i++)
4312  {
4313  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4315  image->colormap[i].red);
4316  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4318  image->colormap[i].green);
4319  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4321  image->colormap[i].blue);
4322  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4324  image->colormap[i].alpha);
4325  }
4326  }
4327  /*
4328  Sigmoidal-contrast enhance image.
4329  */
4330  status=MagickTrue;
4331  progress=0;
4332  image_view=AcquireAuthenticCacheView(image,exception);
4333 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4334  #pragma omp parallel for schedule(static) shared(progress,status) \
4335  magick_number_threads(image,image,image->rows,1)
4336 #endif
4337  for (y=0; y < (ssize_t) image->rows; y++)
4338  {
4339  Quantum
4340  *magick_restrict q;
4341 
4342  ssize_t
4343  x;
4344 
4345  if (status == MagickFalse)
4346  continue;
4347  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4348  if (q == (Quantum *) NULL)
4349  {
4350  status=MagickFalse;
4351  continue;
4352  }
4353  for (x=0; x < (ssize_t) image->columns; x++)
4354  {
4355  ssize_t
4356  i;
4357 
4358  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4359  {
4360  PixelChannel channel = GetPixelChannelChannel(image,i);
4361  PixelTrait traits = GetPixelChannelTraits(image,channel);
4362  if ((traits & UpdatePixelTrait) == 0)
4363  continue;
4364  if( sharpen != MagickFalse )
4365  q[i]=ScaledSig(q[i]);
4366  else
4367  q[i]=InverseScaledSig(q[i]);
4368  }
4369  q+=GetPixelChannels(image);
4370  }
4371  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4372  status=MagickFalse;
4373  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4374  {
4376  proceed;
4377 
4378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4379  #pragma omp atomic
4380 #endif
4381  progress++;
4382  proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4383  image->rows);
4384  if (proceed == MagickFalse)
4385  status=MagickFalse;
4386  }
4387  }
4388  image_view=DestroyCacheView(image_view);
4389  return(status);
4390 }
4391 
4392 /*
4393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4394 % %
4395 % %
4396 % %
4397 % W h i t e B a l a n c e I m a g e %
4398 % %
4399 % %
4400 % %
4401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4402 %
4403 % WhiteBalanceImage() applies white balancing to an image according to a
4404 % grayworld assumption in the LAB colorspace.
4405 %
4406 % The format of the WhiteBalanceImage method is:
4407 %
4408 % MagickBooleanType WhiteBalanceImage(Image *image,
4409 % ExceptionInfo *exception)
4410 %
4411 % A description of each parameter follows:
4412 %
4413 % o image: The image to auto-level
4414 %
4415 % o exception: return any errors or warnings in this structure.
4416 %
4417 */
4419  ExceptionInfo *exception)
4420 {
4421 #define WhiteBalanceImageTag "WhiteBalance/Image"
4422 
4423  CacheView
4424  *image_view;
4425 
4426  const char
4427  *artifact;
4428 
4429  double
4430  a_mean,
4431  b_mean;
4432 
4434  progress;
4435 
4437  status;
4438 
4439  ssize_t
4440  y;
4441 
4442  /*
4443  White balance image.
4444  */
4445  assert(image != (Image *) NULL);
4446  assert(image->signature == MagickCoreSignature);
4447  if (image->debug != MagickFalse)
4448  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4449  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4450  return(MagickFalse);
4451  status=TransformImageColorspace(image,LabColorspace,exception);
4452  a_mean=0.0;
4453  b_mean=0.0;
4454  image_view=AcquireAuthenticCacheView(image,exception);
4455  for (y=0; y < (ssize_t) image->rows; y++)
4456  {
4457  const Quantum
4458  *magick_restrict p;
4459 
4460  ssize_t
4461  x;
4462 
4463  if (status == MagickFalse)
4464  continue;
4465  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4466  if (p == (Quantum *) NULL)
4467  {
4468  status=MagickFalse;
4469  continue;
4470  }
4471  for (x=0; x < (ssize_t) image->columns; x++)
4472  {
4473  a_mean+=QuantumScale*GetPixela(image,p)-0.5;
4474  b_mean+=QuantumScale*GetPixelb(image,p)-0.5;
4475  p+=GetPixelChannels(image);
4476  }
4477  }
4478  a_mean/=((double) image->columns*image->rows);
4479  b_mean/=((double) image->columns*image->rows);
4480  progress=0;
4481 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4482  #pragma omp parallel for schedule(static) shared(progress,status) \
4483  magick_number_threads(image,image,image->rows,1)
4484 #endif
4485  for (y=0; y < (ssize_t) image->rows; y++)
4486  {
4487  Quantum
4488  *magick_restrict q;
4489 
4490  ssize_t
4491  x;
4492 
4493  if (status == MagickFalse)
4494  continue;
4495  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4496  if (q == (Quantum *) NULL)
4497  {
4498  status=MagickFalse;
4499  continue;
4500  }
4501  for (x=0; x < (ssize_t) image->columns; x++)
4502  {
4503  double
4504  a,
4505  b;
4506 
4507  /*
4508  Scale the chroma distance shifted according to amount of luminance.
4509  */
4510  a=(double) GetPixela(image,q)-1.1*GetPixelL(image,q)*a_mean;
4511  b=(double) GetPixelb(image,q)-1.1*GetPixelL(image,q)*b_mean;
4512  SetPixela(image,ClampToQuantum(a),q);
4513  SetPixelb(image,ClampToQuantum(b),q);
4514  q+=GetPixelChannels(image);
4515  }
4516  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4517  status=MagickFalse;
4518  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4519  {
4521  proceed;
4522 
4523 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4524  #pragma omp atomic
4525 #endif
4526  progress++;
4527  proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4528  if (proceed == MagickFalse)
4529  status=MagickFalse;
4530  }
4531  }
4532  image_view=DestroyCacheView(image_view);
4533  artifact=GetImageArtifact(image,"white-balance:vibrance");
4534  if (artifact != (const char *) NULL)
4535  {
4536  ChannelType
4537  channel_mask;
4538 
4539  double
4540  black_point;
4541 
4542  GeometryInfo
4543  geometry_info;
4544 
4546  flags;
4547 
4548  /*
4549  Level the a & b channels.
4550  */
4551  flags=ParseGeometry(artifact,&geometry_info);
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:3924
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:1105
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:3897
static PixelTrait GetPixelRedTraits(const Image *magick_restrict image)
MagickExport MagickBooleanType TransformImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1609
#define ContrastImageTag
#define Sigmoidal(a, b, x)
Definition: enhance.c:4193
ssize_t y
Definition: geometry.h:117
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:107
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:1424
#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:5474
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:107
ClassType storage_class
Definition: image.h:154
size_t width
Definition: geometry.h:131
#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:4418
MagickExport Quantum * GetCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:299
#define HaldClutImageTag
#define NumberCLAHEGrays
MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, const AlphaChannelOption alpha_type, ExceptionInfo *exception)
Definition: channel.c:974
MagickBooleanType
Definition: magick-type.h:165
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:4114
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:2498
#define EqualizeImageTag
MagickExport Quantum * QueueCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:977
static void ModulateHCL(const double percent_hue, const double percent_chroma, const double percent_luma, double *red, double *green, double *blue)
Definition: enhance.c: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:1660
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:135
static MagickBooleanType IsPixelGray(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
#define LevelizeValue(x)
size_t height
Definition: geometry.h:131
MagickExport ImageType IdentifyImageType(const Image *image, ExceptionInfo *exception)
Definition: attribute.c:1724
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:2614
struct _RangeInfo RangeInfo
MagickExport void ConvertHSLToRGB(const double hue, const double saturation, const double lightness, double *red, double *green, double *blue)
Definition: gem.c:462
PixelChannel
Definition: pixel.h: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:107
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1420
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:860
#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:117
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:1089
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:135
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:4251
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:4224
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:1177
#define ColorDecisionListCorrectImageTag
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:788
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)