MagickCore  7.0.10
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 
1567  status;
1568 
1570  progress;
1571 
1572  ssize_t
1573  i;
1574 
1575  ssize_t
1576  y;
1577 
1578  /*
1579  Allocate histogram and stretch map.
1580  */
1581  assert(image != (Image *) NULL);
1582  assert(image->signature == MagickCoreSignature);
1583  if (image->debug != MagickFalse)
1584  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1585  if (SetImageGray(image,exception) != MagickFalse)
1586  (void) SetImageColorspace(image,GRAYColorspace,exception);
1587  black=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*black));
1588  white=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*white));
1589  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1590  sizeof(*histogram));
1591  stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1592  sizeof(*stretch_map));
1593  if ((black == (double *) NULL) || (white == (double *) NULL) ||
1594  (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1595  {
1596  if (stretch_map != (double *) NULL)
1597  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1598  if (histogram != (double *) NULL)
1599  histogram=(double *) RelinquishMagickMemory(histogram);
1600  if (white != (double *) NULL)
1601  white=(double *) RelinquishMagickMemory(white);
1602  if (black != (double *) NULL)
1603  black=(double *) RelinquishMagickMemory(black);
1604  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1605  image->filename);
1606  }
1607  /*
1608  Form histogram.
1609  */
1610  status=MagickTrue;
1611  (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1612  sizeof(*histogram));
1613  image_view=AcquireVirtualCacheView(image,exception);
1614  for (y=0; y < (ssize_t) image->rows; y++)
1615  {
1616  const Quantum
1617  *magick_restrict p;
1618 
1619  ssize_t
1620  x;
1621 
1622  if (status == MagickFalse)
1623  continue;
1624  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1625  if (p == (const Quantum *) NULL)
1626  {
1627  status=MagickFalse;
1628  continue;
1629  }
1630  for (x=0; x < (ssize_t) image->columns; x++)
1631  {
1632  double
1633  pixel;
1634 
1635  pixel=GetPixelIntensity(image,p);
1636  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1637  {
1638  if (image->channel_mask != DefaultChannels)
1639  pixel=(double) p[i];
1640  histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1641  ClampToQuantum(pixel))+i]++;
1642  }
1643  p+=GetPixelChannels(image);
1644  }
1645  }
1646  image_view=DestroyCacheView(image_view);
1647  /*
1648  Find the histogram boundaries by locating the black/white levels.
1649  */
1650  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1651  {
1652  double
1653  intensity;
1654 
1655  ssize_t
1656  j;
1657 
1658  black[i]=0.0;
1659  white[i]=MaxRange(QuantumRange);
1660  intensity=0.0;
1661  for (j=0; j <= (ssize_t) MaxMap; j++)
1662  {
1663  intensity+=histogram[GetPixelChannels(image)*j+i];
1664  if (intensity > black_point)
1665  break;
1666  }
1667  black[i]=(double) j;
1668  intensity=0.0;
1669  for (j=(ssize_t) MaxMap; j != 0; j--)
1670  {
1671  intensity+=histogram[GetPixelChannels(image)*j+i];
1672  if (intensity > ((double) image->columns*image->rows-white_point))
1673  break;
1674  }
1675  white[i]=(double) j;
1676  }
1677  histogram=(double *) RelinquishMagickMemory(histogram);
1678  /*
1679  Stretch the histogram to create the stretched image mapping.
1680  */
1681  (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1682  sizeof(*stretch_map));
1683  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1684  {
1685  ssize_t
1686  j;
1687 
1688  for (j=0; j <= (ssize_t) MaxMap; j++)
1689  {
1690  double
1691  gamma;
1692 
1693  gamma=PerceptibleReciprocal(white[i]-black[i]);
1694  if (j < (ssize_t) black[i])
1695  stretch_map[GetPixelChannels(image)*j+i]=0.0;
1696  else
1697  if (j > (ssize_t) white[i])
1698  stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1699  else
1700  if (black[i] != white[i])
1701  stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1702  (double) (MaxMap*gamma*(j-black[i])));
1703  }
1704  }
1705  if (image->storage_class == PseudoClass)
1706  {
1707  ssize_t
1708  j;
1709 
1710  /*
1711  Stretch-contrast colormap.
1712  */
1713  for (j=0; j < (ssize_t) image->colors; j++)
1714  {
1715  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1716  {
1718  image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1719  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1720  }
1721  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1722  {
1724  image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1725  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1726  }
1727  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1728  {
1730  image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1731  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1732  }
1733  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1734  {
1736  image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1737  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1738  }
1739  }
1740  }
1741  /*
1742  Stretch-contrast image.
1743  */
1744  status=MagickTrue;
1745  progress=0;
1746  image_view=AcquireAuthenticCacheView(image,exception);
1747 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1748  #pragma omp parallel for schedule(static) shared(progress,status) \
1749  magick_number_threads(image,image,image->rows,1)
1750 #endif
1751  for (y=0; y < (ssize_t) image->rows; y++)
1752  {
1753  Quantum
1754  *magick_restrict q;
1755 
1756  ssize_t
1757  x;
1758 
1759  if (status == MagickFalse)
1760  continue;
1761  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1762  if (q == (Quantum *) NULL)
1763  {
1764  status=MagickFalse;
1765  continue;
1766  }
1767  for (x=0; x < (ssize_t) image->columns; x++)
1768  {
1769  ssize_t
1770  j;
1771 
1772  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1773  {
1774  PixelChannel channel = GetPixelChannelChannel(image,j);
1775  PixelTrait traits = GetPixelChannelTraits(image,channel);
1776  if ((traits & UpdatePixelTrait) == 0)
1777  continue;
1778  if (black[j] == white[j])
1779  continue;
1780  q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1781  ScaleQuantumToMap(q[j])+j]);
1782  }
1783  q+=GetPixelChannels(image);
1784  }
1785  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1786  status=MagickFalse;
1787  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1788  {
1790  proceed;
1791 
1792 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1793  #pragma omp atomic
1794 #endif
1795  progress++;
1796  proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1797  image->rows);
1798  if (proceed == MagickFalse)
1799  status=MagickFalse;
1800  }
1801  }
1802  image_view=DestroyCacheView(image_view);
1803  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1804  white=(double *) RelinquishMagickMemory(white);
1805  black=(double *) RelinquishMagickMemory(black);
1806  return(status);
1807 }
1808 
1809 /*
1810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1811 % %
1812 % %
1813 % %
1814 % E n h a n c e I m a g e %
1815 % %
1816 % %
1817 % %
1818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1819 %
1820 % EnhanceImage() applies a digital filter that improves the quality of a
1821 % noisy image.
1822 %
1823 % The format of the EnhanceImage method is:
1824 %
1825 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1826 %
1827 % A description of each parameter follows:
1828 %
1829 % o image: the image.
1830 %
1831 % o exception: return any errors or warnings in this structure.
1832 %
1833 */
1835 {
1836 #define EnhanceImageTag "Enhance/Image"
1837 #define EnhancePixel(weight) \
1838  mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1839  distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1840  distance_squared=(4.0+mean)*distance*distance; \
1841  mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1842  distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1843  distance_squared+=(7.0-mean)*distance*distance; \
1844  mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1845  distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1846  distance_squared+=(5.0-mean)*distance*distance; \
1847  mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1848  distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1849  distance_squared+=(5.0-mean)*distance*distance; \
1850  mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1851  distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1852  distance_squared+=(5.0-mean)*distance*distance; \
1853  if (distance_squared < 0.069) \
1854  { \
1855  aggregate.red+=(weight)*GetPixelRed(image,r); \
1856  aggregate.green+=(weight)*GetPixelGreen(image,r); \
1857  aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1858  aggregate.black+=(weight)*GetPixelBlack(image,r); \
1859  aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1860  total_weight+=(weight); \
1861  } \
1862  r+=GetPixelChannels(image);
1863 
1864  CacheView
1865  *enhance_view,
1866  *image_view;
1867 
1868  Image
1869  *enhance_image;
1870 
1872  status;
1873 
1875  progress;
1876 
1877  ssize_t
1878  y;
1879 
1880  /*
1881  Initialize enhanced image attributes.
1882  */
1883  assert(image != (const Image *) NULL);
1884  assert(image->signature == MagickCoreSignature);
1885  if (image->debug != MagickFalse)
1886  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1887  assert(exception != (ExceptionInfo *) NULL);
1888  assert(exception->signature == MagickCoreSignature);
1889  enhance_image=CloneImage(image,0,0,MagickTrue,
1890  exception);
1891  if (enhance_image == (Image *) NULL)
1892  return((Image *) NULL);
1893  if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1894  {
1895  enhance_image=DestroyImage(enhance_image);
1896  return((Image *) NULL);
1897  }
1898  /*
1899  Enhance image.
1900  */
1901  status=MagickTrue;
1902  progress=0;
1903  image_view=AcquireVirtualCacheView(image,exception);
1904  enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1905 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1906  #pragma omp parallel for schedule(static) shared(progress,status) \
1907  magick_number_threads(image,enhance_image,image->rows,1)
1908 #endif
1909  for (y=0; y < (ssize_t) image->rows; y++)
1910  {
1911  PixelInfo
1912  pixel;
1913 
1914  const Quantum
1915  *magick_restrict p;
1916 
1917  Quantum
1918  *magick_restrict q;
1919 
1920  ssize_t
1921  x;
1922 
1923  ssize_t
1924  center;
1925 
1926  if (status == MagickFalse)
1927  continue;
1928  p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1929  q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1930  exception);
1931  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1932  {
1933  status=MagickFalse;
1934  continue;
1935  }
1936  center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1937  GetPixelInfo(image,&pixel);
1938  for (x=0; x < (ssize_t) image->columns; x++)
1939  {
1940  double
1941  distance,
1942  distance_squared,
1943  mean,
1944  total_weight;
1945 
1946  PixelInfo
1947  aggregate;
1948 
1949  const Quantum
1950  *magick_restrict r;
1951 
1952  GetPixelInfo(image,&aggregate);
1953  total_weight=0.0;
1954  GetPixelInfoPixel(image,p+center,&pixel);
1955  r=p;
1956  EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1957  EnhancePixel(8.0); EnhancePixel(5.0);
1958  r=p+GetPixelChannels(image)*(image->columns+4);
1959  EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1960  EnhancePixel(20.0); EnhancePixel(8.0);
1961  r=p+2*GetPixelChannels(image)*(image->columns+4);
1962  EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1963  EnhancePixel(40.0); EnhancePixel(10.0);
1964  r=p+3*GetPixelChannels(image)*(image->columns+4);
1965  EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1966  EnhancePixel(20.0); EnhancePixel(8.0);
1967  r=p+4*GetPixelChannels(image)*(image->columns+4);
1968  EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1969  EnhancePixel(8.0); EnhancePixel(5.0);
1970  if (total_weight > MagickEpsilon)
1971  {
1972  pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1973  pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1974  pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1975  pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1976  pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1977  }
1978  SetPixelViaPixelInfo(enhance_image,&pixel,q);
1979  p+=GetPixelChannels(image);
1980  q+=GetPixelChannels(enhance_image);
1981  }
1982  if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1983  status=MagickFalse;
1984  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1985  {
1987  proceed;
1988 
1989 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1990  #pragma omp atomic
1991 #endif
1992  progress++;
1993  proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
1994  if (proceed == MagickFalse)
1995  status=MagickFalse;
1996  }
1997  }
1998  enhance_view=DestroyCacheView(enhance_view);
1999  image_view=DestroyCacheView(image_view);
2000  if (status == MagickFalse)
2001  enhance_image=DestroyImage(enhance_image);
2002  return(enhance_image);
2003 }
2004 
2005 /*
2006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2007 % %
2008 % %
2009 % %
2010 % E q u a l i z e I m a g e %
2011 % %
2012 % %
2013 % %
2014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2015 %
2016 % EqualizeImage() applies a histogram equalization to the image.
2017 %
2018 % The format of the EqualizeImage method is:
2019 %
2020 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
2021 %
2022 % A description of each parameter follows:
2023 %
2024 % o image: the image.
2025 %
2026 % o exception: return any errors or warnings in this structure.
2027 %
2028 */
2030  ExceptionInfo *exception)
2031 {
2032 #define EqualizeImageTag "Equalize/Image"
2033 
2034  CacheView
2035  *image_view;
2036 
2037  double
2038  black[CompositePixelChannel+1],
2039  *equalize_map,
2040  *histogram,
2041  *map,
2042  white[CompositePixelChannel+1];
2043 
2045  status;
2046 
2048  progress;
2049 
2050  ssize_t
2051  i;
2052 
2053  ssize_t
2054  y;
2055 
2056  /*
2057  Allocate and initialize histogram arrays.
2058  */
2059  assert(image != (Image *) NULL);
2060  assert(image->signature == MagickCoreSignature);
2061 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2062  if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2063  return(MagickTrue);
2064 #endif
2065  if (image->debug != MagickFalse)
2066  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2067  equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2068  sizeof(*equalize_map));
2069  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2070  sizeof(*histogram));
2071  map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*sizeof(*map));
2072  if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
2073  (map == (double *) NULL))
2074  {
2075  if (map != (double *) NULL)
2076  map=(double *) RelinquishMagickMemory(map);
2077  if (histogram != (double *) NULL)
2078  histogram=(double *) RelinquishMagickMemory(histogram);
2079  if (equalize_map != (double *) NULL)
2080  equalize_map=(double *) RelinquishMagickMemory(equalize_map);
2081  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2082  image->filename);
2083  }
2084  /*
2085  Form histogram.
2086  */
2087  status=MagickTrue;
2088  (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2089  sizeof(*histogram));
2090  image_view=AcquireVirtualCacheView(image,exception);
2091  for (y=0; y < (ssize_t) image->rows; y++)
2092  {
2093  const Quantum
2094  *magick_restrict p;
2095 
2096  ssize_t
2097  x;
2098 
2099  if (status == MagickFalse)
2100  continue;
2101  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2102  if (p == (const Quantum *) NULL)
2103  {
2104  status=MagickFalse;
2105  continue;
2106  }
2107  for (x=0; x < (ssize_t) image->columns; x++)
2108  {
2109  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2110  {
2111  double
2112  intensity;
2113 
2114  intensity=(double) p[i];
2115  if ((image->channel_mask & SyncChannels) != 0)
2116  intensity=GetPixelIntensity(image,p);
2117  histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2118  ClampToQuantum(intensity))+i]++;
2119  }
2120  p+=GetPixelChannels(image);
2121  }
2122  }
2123  image_view=DestroyCacheView(image_view);
2124  /*
2125  Integrate the histogram to get the equalization map.
2126  */
2127  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2128  {
2129  double
2130  intensity;
2131 
2132  ssize_t
2133  j;
2134 
2135  intensity=0.0;
2136  for (j=0; j <= (ssize_t) MaxMap; j++)
2137  {
2138  intensity+=histogram[GetPixelChannels(image)*j+i];
2139  map[GetPixelChannels(image)*j+i]=intensity;
2140  }
2141  }
2142  (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2143  sizeof(*equalize_map));
2144  (void) memset(black,0,sizeof(*black));
2145  (void) memset(white,0,sizeof(*white));
2146  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2147  {
2148  ssize_t
2149  j;
2150 
2151  black[i]=map[i];
2152  white[i]=map[GetPixelChannels(image)*MaxMap+i];
2153  if (black[i] != white[i])
2154  for (j=0; j <= (ssize_t) MaxMap; j++)
2155  equalize_map[GetPixelChannels(image)*j+i]=(double)
2156  ScaleMapToQuantum((double) ((MaxMap*(map[
2157  GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
2158  }
2159  histogram=(double *) RelinquishMagickMemory(histogram);
2160  map=(double *) RelinquishMagickMemory(map);
2161  if (image->storage_class == PseudoClass)
2162  {
2163  ssize_t
2164  j;
2165 
2166  /*
2167  Equalize colormap.
2168  */
2169  for (j=0; j < (ssize_t) image->colors; j++)
2170  {
2171  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2172  {
2173  PixelChannel channel = GetPixelChannelChannel(image,
2174  RedPixelChannel);
2175  if (black[channel] != white[channel])
2176  image->colormap[j].red=equalize_map[GetPixelChannels(image)*
2177  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+
2178  channel];
2179  }
2180  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2181  {
2182  PixelChannel channel = GetPixelChannelChannel(image,
2184  if (black[channel] != white[channel])
2185  image->colormap[j].green=equalize_map[GetPixelChannels(image)*
2186  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+
2187  channel];
2188  }
2189  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2190  {
2191  PixelChannel channel = GetPixelChannelChannel(image,
2193  if (black[channel] != white[channel])
2194  image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
2195  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+
2196  channel];
2197  }
2198  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2199  {
2200  PixelChannel channel = GetPixelChannelChannel(image,
2202  if (black[channel] != white[channel])
2203  image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
2204  ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+
2205  channel];
2206  }
2207  }
2208  }
2209  /*
2210  Equalize image.
2211  */
2212  progress=0;
2213  image_view=AcquireAuthenticCacheView(image,exception);
2214 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2215  #pragma omp parallel for schedule(static) shared(progress,status) \
2216  magick_number_threads(image,image,image->rows,1)
2217 #endif
2218  for (y=0; y < (ssize_t) image->rows; y++)
2219  {
2220  Quantum
2221  *magick_restrict q;
2222 
2223  ssize_t
2224  x;
2225 
2226  if (status == MagickFalse)
2227  continue;
2228  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2229  if (q == (Quantum *) NULL)
2230  {
2231  status=MagickFalse;
2232  continue;
2233  }
2234  for (x=0; x < (ssize_t) image->columns; x++)
2235  {
2236  ssize_t
2237  j;
2238 
2239  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2240  {
2241  PixelChannel channel = GetPixelChannelChannel(image,j);
2242  PixelTrait traits = GetPixelChannelTraits(image,channel);
2243  if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2244  continue;
2245  q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2246  ScaleQuantumToMap(q[j])+j]);
2247  }
2248  q+=GetPixelChannels(image);
2249  }
2250  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2251  status=MagickFalse;
2252  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2253  {
2255  proceed;
2256 
2257 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2258  #pragma omp atomic
2259 #endif
2260  progress++;
2261  proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2262  if (proceed == MagickFalse)
2263  status=MagickFalse;
2264  }
2265  }
2266  image_view=DestroyCacheView(image_view);
2267  equalize_map=(double *) RelinquishMagickMemory(equalize_map);
2268  return(status);
2269 }
2270 
2271 /*
2272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2273 % %
2274 % %
2275 % %
2276 % G a m m a I m a g e %
2277 % %
2278 % %
2279 % %
2280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2281 %
2282 % GammaImage() gamma-corrects a particular image channel. The same
2283 % image viewed on different devices will have perceptual differences in the
2284 % way the image's intensities are represented on the screen. Specify
2285 % individual gamma levels for the red, green, and blue channels, or adjust
2286 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
2287 %
2288 % You can also reduce the influence of a particular channel with a gamma
2289 % value of 0.
2290 %
2291 % The format of the GammaImage method is:
2292 %
2293 % MagickBooleanType GammaImage(Image *image,const double gamma,
2294 % ExceptionInfo *exception)
2295 %
2296 % A description of each parameter follows:
2297 %
2298 % o image: the image.
2299 %
2300 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
2301 %
2302 % o gamma: the image gamma.
2303 %
2304 */
2305 
2306 static inline double gamma_pow(const double value,const double gamma)
2307 {
2308  return(value < 0.0 ? value : pow(value,gamma));
2309 }
2310 
2312  ExceptionInfo *exception)
2313 {
2314 #define GammaImageTag "Gamma/Image"
2315 
2316  CacheView
2317  *image_view;
2318 
2320  status;
2321 
2323  progress;
2324 
2325  Quantum
2326  *gamma_map;
2327 
2328  ssize_t
2329  i;
2330 
2331  ssize_t
2332  y;
2333 
2334  /*
2335  Allocate and initialize gamma maps.
2336  */
2337  assert(image != (Image *) NULL);
2338  assert(image->signature == MagickCoreSignature);
2339  if (image->debug != MagickFalse)
2340  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2341  if (gamma == 1.0)
2342  return(MagickTrue);
2343  gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
2344  if (gamma_map == (Quantum *) NULL)
2345  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2346  image->filename);
2347  (void) memset(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
2348  if (gamma != 0.0)
2349  for (i=0; i <= (ssize_t) MaxMap; i++)
2350  gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
2351  MaxMap,PerceptibleReciprocal(gamma))));
2352  if (image->storage_class == PseudoClass)
2353  for (i=0; i < (ssize_t) image->colors; i++)
2354  {
2355  /*
2356  Gamma-correct colormap.
2357  */
2358  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2359  image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
2360  ClampToQuantum(image->colormap[i].red))];
2361  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2362  image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2363  ClampToQuantum(image->colormap[i].green))];
2364  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2365  image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
2366  ClampToQuantum(image->colormap[i].blue))];
2367  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2368  image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2369  ClampToQuantum(image->colormap[i].alpha))];
2370  }
2371  /*
2372  Gamma-correct image.
2373  */
2374  status=MagickTrue;
2375  progress=0;
2376  image_view=AcquireAuthenticCacheView(image,exception);
2377 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2378  #pragma omp parallel for schedule(static) shared(progress,status) \
2379  magick_number_threads(image,image,image->rows,1)
2380 #endif
2381  for (y=0; y < (ssize_t) image->rows; y++)
2382  {
2383  Quantum
2384  *magick_restrict q;
2385 
2386  ssize_t
2387  x;
2388 
2389  if (status == MagickFalse)
2390  continue;
2391  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2392  if (q == (Quantum *) NULL)
2393  {
2394  status=MagickFalse;
2395  continue;
2396  }
2397  for (x=0; x < (ssize_t) image->columns; x++)
2398  {
2399  ssize_t
2400  j;
2401 
2402  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2403  {
2404  PixelChannel channel = GetPixelChannelChannel(image,j);
2405  PixelTrait traits = GetPixelChannelTraits(image,channel);
2406  if ((traits & UpdatePixelTrait) == 0)
2407  continue;
2408  q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2409  q[j]))];
2410  }
2411  q+=GetPixelChannels(image);
2412  }
2413  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2414  status=MagickFalse;
2415  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2416  {
2418  proceed;
2419 
2420 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2421  #pragma omp atomic
2422 #endif
2423  progress++;
2424  proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2425  if (proceed == MagickFalse)
2426  status=MagickFalse;
2427  }
2428  }
2429  image_view=DestroyCacheView(image_view);
2430  gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2431  if (image->gamma != 0.0)
2432  image->gamma*=gamma;
2433  return(status);
2434 }
2435 
2436 /*
2437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2438 % %
2439 % %
2440 % %
2441 % G r a y s c a l e I m a g e %
2442 % %
2443 % %
2444 % %
2445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2446 %
2447 % GrayscaleImage() converts the image to grayscale.
2448 %
2449 % The format of the GrayscaleImage method is:
2450 %
2451 % MagickBooleanType GrayscaleImage(Image *image,
2452 % const PixelIntensityMethod method ,ExceptionInfo *exception)
2453 %
2454 % A description of each parameter follows:
2455 %
2456 % o image: the image.
2457 %
2458 % o method: the pixel intensity method.
2459 %
2460 % o exception: return any errors or warnings in this structure.
2461 %
2462 */
2464  const PixelIntensityMethod method,ExceptionInfo *exception)
2465 {
2466 #define GrayscaleImageTag "Grayscale/Image"
2467 
2468  CacheView
2469  *image_view;
2470 
2472  status;
2473 
2475  progress;
2476 
2477  ssize_t
2478  y;
2479 
2480  assert(image != (Image *) NULL);
2481  assert(image->signature == MagickCoreSignature);
2482  if (image->debug != MagickFalse)
2483  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2484  if (image->storage_class == PseudoClass)
2485  {
2486  if (SyncImage(image,exception) == MagickFalse)
2487  return(MagickFalse);
2488  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2489  return(MagickFalse);
2490  }
2491 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2492  if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2493  {
2494  image->intensity=method;
2495  image->type=GrayscaleType;
2496  if ((method == Rec601LuminancePixelIntensityMethod) ||
2498  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2499  return(SetImageColorspace(image,GRAYColorspace,exception));
2500  }
2501 #endif
2502  /*
2503  Grayscale image.
2504  */
2505  status=MagickTrue;
2506  progress=0;
2507  image_view=AcquireAuthenticCacheView(image,exception);
2508 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2509  #pragma omp parallel for schedule(static) shared(progress,status) \
2510  magick_number_threads(image,image,image->rows,1)
2511 #endif
2512  for (y=0; y < (ssize_t) image->rows; y++)
2513  {
2514  Quantum
2515  *magick_restrict q;
2516 
2517  ssize_t
2518  x;
2519 
2520  if (status == MagickFalse)
2521  continue;
2522  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2523  if (q == (Quantum *) NULL)
2524  {
2525  status=MagickFalse;
2526  continue;
2527  }
2528  for (x=0; x < (ssize_t) image->columns; x++)
2529  {
2531  blue,
2532  green,
2533  red,
2534  intensity;
2535 
2536  red=(MagickRealType) GetPixelRed(image,q);
2537  green=(MagickRealType) GetPixelGreen(image,q);
2538  blue=(MagickRealType) GetPixelBlue(image,q);
2539  intensity=0.0;
2540  switch (method)
2541  {
2543  {
2544  intensity=(red+green+blue)/3.0;
2545  break;
2546  }
2548  {
2549  intensity=MagickMax(MagickMax(red,green),blue);
2550  break;
2551  }
2553  {
2554  intensity=(MagickMin(MagickMin(red,green),blue)+
2555  MagickMax(MagickMax(red,green),blue))/2.0;
2556  break;
2557  }
2559  {
2560  intensity=(MagickRealType) (((double) red*red+green*green+
2561  blue*blue)/3.0);
2562  break;
2563  }
2565  {
2566  if (image->colorspace == RGBColorspace)
2567  {
2568  red=EncodePixelGamma(red);
2569  green=EncodePixelGamma(green);
2570  blue=EncodePixelGamma(blue);
2571  }
2572  intensity=0.298839*red+0.586811*green+0.114350*blue;
2573  break;
2574  }
2576  {
2577  if (image->colorspace == sRGBColorspace)
2578  {
2579  red=DecodePixelGamma(red);
2580  green=DecodePixelGamma(green);
2581  blue=DecodePixelGamma(blue);
2582  }
2583  intensity=0.298839*red+0.586811*green+0.114350*blue;
2584  break;
2585  }
2587  default:
2588  {
2589  if (image->colorspace == RGBColorspace)
2590  {
2591  red=EncodePixelGamma(red);
2592  green=EncodePixelGamma(green);
2593  blue=EncodePixelGamma(blue);
2594  }
2595  intensity=0.212656*red+0.715158*green+0.072186*blue;
2596  break;
2597  }
2599  {
2600  if (image->colorspace == sRGBColorspace)
2601  {
2602  red=DecodePixelGamma(red);
2603  green=DecodePixelGamma(green);
2604  blue=DecodePixelGamma(blue);
2605  }
2606  intensity=0.212656*red+0.715158*green+0.072186*blue;
2607  break;
2608  }
2610  {
2611  intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2612  blue*blue)/sqrt(3.0));
2613  break;
2614  }
2615  }
2616  SetPixelGray(image,ClampToQuantum(intensity),q);
2617  q+=GetPixelChannels(image);
2618  }
2619  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2620  status=MagickFalse;
2621  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2622  {
2624  proceed;
2625 
2626 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2627  #pragma omp atomic
2628 #endif
2629  progress++;
2630  proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2631  if (proceed == MagickFalse)
2632  status=MagickFalse;
2633  }
2634  }
2635  image_view=DestroyCacheView(image_view);
2636  image->intensity=method;
2637  image->type=GrayscaleType;
2638  if ((method == Rec601LuminancePixelIntensityMethod) ||
2640  return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2641  return(SetImageColorspace(image,GRAYColorspace,exception));
2642 }
2643 
2644 /*
2645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2646 % %
2647 % %
2648 % %
2649 % H a l d C l u t I m a g e %
2650 % %
2651 % %
2652 % %
2653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2654 %
2655 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2656 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2657 % Create it with the HALD coder. You can apply any color transformation to
2658 % the Hald image and then use this method to apply the transform to the
2659 % image.
2660 %
2661 % The format of the HaldClutImage method is:
2662 %
2663 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2664 % ExceptionInfo *exception)
2665 %
2666 % A description of each parameter follows:
2667 %
2668 % o image: the image, which is replaced by indexed CLUT values
2669 %
2670 % o hald_image: the color lookup table image for replacement color values.
2671 %
2672 % o exception: return any errors or warnings in this structure.
2673 %
2674 */
2676  const Image *hald_image,ExceptionInfo *exception)
2677 {
2678 #define HaldClutImageTag "Clut/Image"
2679 
2680  typedef struct _HaldInfo
2681  {
2682  double
2683  x,
2684  y,
2685  z;
2686  } HaldInfo;
2687 
2688  CacheView
2689  *hald_view,
2690  *image_view;
2691 
2692  double
2693  width;
2694 
2696  status;
2697 
2699  progress;
2700 
2701  PixelInfo
2702  zero;
2703 
2704  size_t
2705  cube_size,
2706  length,
2707  level;
2708 
2709  ssize_t
2710  y;
2711 
2712  assert(image != (Image *) NULL);
2713  assert(image->signature == MagickCoreSignature);
2714  if (image->debug != MagickFalse)
2715  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2716  assert(hald_image != (Image *) NULL);
2717  assert(hald_image->signature == MagickCoreSignature);
2718  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2719  return(MagickFalse);
2720  if (image->alpha_trait == UndefinedPixelTrait)
2721  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2722  /*
2723  Hald clut image.
2724  */
2725  status=MagickTrue;
2726  progress=0;
2727  length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2728  (MagickRealType) hald_image->rows);
2729  for (level=2; (level*level*level) < length; level++) ;
2730  level*=level;
2731  cube_size=level*level;
2732  width=(double) hald_image->columns;
2733  GetPixelInfo(hald_image,&zero);
2734  hald_view=AcquireVirtualCacheView(hald_image,exception);
2735  image_view=AcquireAuthenticCacheView(image,exception);
2736 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2737  #pragma omp parallel for schedule(static) shared(progress,status) \
2738  magick_number_threads(image,image,image->rows,1)
2739 #endif
2740  for (y=0; y < (ssize_t) image->rows; y++)
2741  {
2742  Quantum
2743  *magick_restrict q;
2744 
2745  ssize_t
2746  x;
2747 
2748  if (status == MagickFalse)
2749  continue;
2750  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2751  if (q == (Quantum *) NULL)
2752  {
2753  status=MagickFalse;
2754  continue;
2755  }
2756  for (x=0; x < (ssize_t) image->columns; x++)
2757  {
2758  double
2759  area,
2760  offset;
2761 
2762  HaldInfo
2763  point;
2764 
2765  PixelInfo
2766  pixel,
2767  pixel1,
2768  pixel2,
2769  pixel3,
2770  pixel4;
2771 
2772  point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2773  point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2774  point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2775  offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2776  point.x-=floor(point.x);
2777  point.y-=floor(point.y);
2778  point.z-=floor(point.z);
2779  pixel1=zero;
2780  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2781  fmod(offset,width),floor(offset/width),&pixel1,exception);
2782  if (status == MagickFalse)
2783  break;
2784  pixel2=zero;
2785  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2786  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2787  if (status == MagickFalse)
2788  break;
2789  pixel3=zero;
2790  area=point.y;
2791  if (hald_image->interpolate == NearestInterpolatePixel)
2792  area=(point.y < 0.5) ? 0.0 : 1.0;
2793  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2794  area,&pixel3);
2795  offset+=cube_size;
2796  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2797  fmod(offset,width),floor(offset/width),&pixel1,exception);
2798  if (status == MagickFalse)
2799  break;
2800  status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2801  fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2802  if (status == MagickFalse)
2803  break;
2804  pixel4=zero;
2805  CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2806  area,&pixel4);
2807  pixel=zero;
2808  area=point.z;
2809  if (hald_image->interpolate == NearestInterpolatePixel)
2810  area=(point.z < 0.5)? 0.0 : 1.0;
2811  CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2812  area,&pixel);
2813  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2814  SetPixelRed(image,ClampToQuantum(pixel.red),q);
2815  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2816  SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2817  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2818  SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2819  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2820  (image->colorspace == CMYKColorspace))
2821  SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2822  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2823  (image->alpha_trait != UndefinedPixelTrait))
2824  SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2825  q+=GetPixelChannels(image);
2826  }
2827  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2828  status=MagickFalse;
2829  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2830  {
2832  proceed;
2833 
2834 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2835  #pragma omp atomic
2836 #endif
2837  progress++;
2838  proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2839  if (proceed == MagickFalse)
2840  status=MagickFalse;
2841  }
2842  }
2843  hald_view=DestroyCacheView(hald_view);
2844  image_view=DestroyCacheView(image_view);
2845  return(status);
2846 }
2847 
2848 /*
2849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2850 % %
2851 % %
2852 % %
2853 % L e v e l I m a g e %
2854 % %
2855 % %
2856 % %
2857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2858 %
2859 % LevelImage() adjusts the levels of a particular image channel by
2860 % scaling the colors falling between specified white and black points to
2861 % the full available quantum range.
2862 %
2863 % The parameters provided represent the black, and white points. The black
2864 % point specifies the darkest color in the image. Colors darker than the
2865 % black point are set to zero. White point specifies the lightest color in
2866 % the image. Colors brighter than the white point are set to the maximum
2867 % quantum value.
2868 %
2869 % If a '!' flag is given, map black and white colors to the given levels
2870 % rather than mapping those levels to black and white. See
2871 % LevelizeImage() below.
2872 %
2873 % Gamma specifies a gamma correction to apply to the image.
2874 %
2875 % The format of the LevelImage method is:
2876 %
2877 % MagickBooleanType LevelImage(Image *image,const double black_point,
2878 % const double white_point,const double gamma,ExceptionInfo *exception)
2879 %
2880 % A description of each parameter follows:
2881 %
2882 % o image: the image.
2883 %
2884 % o black_point: The level to map zero (black) to.
2885 %
2886 % o white_point: The level to map QuantumRange (white) to.
2887 %
2888 % o exception: return any errors or warnings in this structure.
2889 %
2890 */
2891 
2892 static inline double LevelPixel(const double black_point,
2893  const double white_point,const double gamma,const double pixel)
2894 {
2895  double
2896  level_pixel,
2897  scale;
2898 
2899  scale=PerceptibleReciprocal(white_point-black_point);
2900  level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2901  PerceptibleReciprocal(gamma));
2902  return(level_pixel);
2903 }
2904 
2905 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2906  const double white_point,const double gamma,ExceptionInfo *exception)
2907 {
2908 #define LevelImageTag "Level/Image"
2909 
2910  CacheView
2911  *image_view;
2912 
2914  status;
2915 
2917  progress;
2918 
2919  ssize_t
2920  i;
2921 
2922  ssize_t
2923  y;
2924 
2925  /*
2926  Allocate and initialize levels map.
2927  */
2928  assert(image != (Image *) NULL);
2929  assert(image->signature == MagickCoreSignature);
2930  if (image->debug != MagickFalse)
2931  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2932  if (image->storage_class == PseudoClass)
2933  for (i=0; i < (ssize_t) image->colors; i++)
2934  {
2935  /*
2936  Level colormap.
2937  */
2938  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2939  image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2940  white_point,gamma,image->colormap[i].red));
2941  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2942  image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2943  white_point,gamma,image->colormap[i].green));
2944  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2945  image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2946  white_point,gamma,image->colormap[i].blue));
2947  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2948  image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2949  white_point,gamma,image->colormap[i].alpha));
2950  }
2951  /*
2952  Level image.
2953  */
2954  status=MagickTrue;
2955  progress=0;
2956  image_view=AcquireAuthenticCacheView(image,exception);
2957 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2958  #pragma omp parallel for schedule(static) shared(progress,status) \
2959  magick_number_threads(image,image,image->rows,1)
2960 #endif
2961  for (y=0; y < (ssize_t) image->rows; y++)
2962  {
2963  Quantum
2964  *magick_restrict q;
2965 
2966  ssize_t
2967  x;
2968 
2969  if (status == MagickFalse)
2970  continue;
2971  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2972  if (q == (Quantum *) NULL)
2973  {
2974  status=MagickFalse;
2975  continue;
2976  }
2977  for (x=0; x < (ssize_t) image->columns; x++)
2978  {
2979  ssize_t
2980  j;
2981 
2982  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2983  {
2984  PixelChannel channel = GetPixelChannelChannel(image,j);
2985  PixelTrait traits = GetPixelChannelTraits(image,channel);
2986  if ((traits & UpdatePixelTrait) == 0)
2987  continue;
2988  q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2989  (double) q[j]));
2990  }
2991  q+=GetPixelChannels(image);
2992  }
2993  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2994  status=MagickFalse;
2995  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2996  {
2998  proceed;
2999 
3000 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3001  #pragma omp atomic
3002 #endif
3003  progress++;
3004  proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3005  if (proceed == MagickFalse)
3006  status=MagickFalse;
3007  }
3008  }
3009  image_view=DestroyCacheView(image_view);
3010  (void) ClampImage(image,exception);
3011  return(status);
3012 }
3013 
3014 /*
3015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3016 % %
3017 % %
3018 % %
3019 % L e v e l i z e I m a g e %
3020 % %
3021 % %
3022 % %
3023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3024 %
3025 % LevelizeImage() applies the reversed LevelImage() operation to just
3026 % the specific channels specified. It compresses the full range of color
3027 % values, so that they lie between the given black and white points. Gamma is
3028 % applied before the values are mapped.
3029 %
3030 % LevelizeImage() can be called with by using a +level command line
3031 % API option, or using a '!' on a -level or LevelImage() geometry string.
3032 %
3033 % It can be used to de-contrast a greyscale image to the exact levels
3034 % specified. Or by using specific levels for each channel of an image you
3035 % can convert a gray-scale image to any linear color gradient, according to
3036 % those levels.
3037 %
3038 % The format of the LevelizeImage method is:
3039 %
3040 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
3041 % const double white_point,const double gamma,ExceptionInfo *exception)
3042 %
3043 % A description of each parameter follows:
3044 %
3045 % o image: the image.
3046 %
3047 % o black_point: The level to map zero (black) to.
3048 %
3049 % o white_point: The level to map QuantumRange (white) to.
3050 %
3051 % o gamma: adjust gamma by this factor before mapping values.
3052 %
3053 % o exception: return any errors or warnings in this structure.
3054 %
3055 */
3057  const double black_point,const double white_point,const double gamma,
3058  ExceptionInfo *exception)
3059 {
3060 #define LevelizeImageTag "Levelize/Image"
3061 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3062  (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
3063 
3064  CacheView
3065  *image_view;
3066 
3068  status;
3069 
3071  progress;
3072 
3073  ssize_t
3074  i;
3075 
3076  ssize_t
3077  y;
3078 
3079  /*
3080  Allocate and initialize levels map.
3081  */
3082  assert(image != (Image *) NULL);
3083  assert(image->signature == MagickCoreSignature);
3084  if (image->debug != MagickFalse)
3085  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3086  if (image->storage_class == PseudoClass)
3087  for (i=0; i < (ssize_t) image->colors; i++)
3088  {
3089  /*
3090  Level colormap.
3091  */
3092  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3093  image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
3094  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3095  image->colormap[i].green=(double) LevelizeValue(
3096  image->colormap[i].green);
3097  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3098  image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
3099  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3100  image->colormap[i].alpha=(double) LevelizeValue(
3101  image->colormap[i].alpha);
3102  }
3103  /*
3104  Level image.
3105  */
3106  status=MagickTrue;
3107  progress=0;
3108  image_view=AcquireAuthenticCacheView(image,exception);
3109 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3110  #pragma omp parallel for schedule(static) shared(progress,status) \
3111  magick_number_threads(image,image,image->rows,1)
3112 #endif
3113  for (y=0; y < (ssize_t) image->rows; y++)
3114  {
3115  Quantum
3116  *magick_restrict q;
3117 
3118  ssize_t
3119  x;
3120 
3121  if (status == MagickFalse)
3122  continue;
3123  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3124  if (q == (Quantum *) NULL)
3125  {
3126  status=MagickFalse;
3127  continue;
3128  }
3129  for (x=0; x < (ssize_t) image->columns; x++)
3130  {
3131  ssize_t
3132  j;
3133 
3134  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3135  {
3136  PixelChannel channel = GetPixelChannelChannel(image,j);
3137  PixelTrait traits = GetPixelChannelTraits(image,channel);
3138  if ((traits & UpdatePixelTrait) == 0)
3139  continue;
3140  q[j]=LevelizeValue(q[j]);
3141  }
3142  q+=GetPixelChannels(image);
3143  }
3144  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3145  status=MagickFalse;
3146  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3147  {
3149  proceed;
3150 
3151 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3152  #pragma omp atomic
3153 #endif
3154  progress++;
3155  proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3156  if (proceed == MagickFalse)
3157  status=MagickFalse;
3158  }
3159  }
3160  image_view=DestroyCacheView(image_view);
3161  return(status);
3162 }
3163 
3164 /*
3165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3166 % %
3167 % %
3168 % %
3169 % L e v e l I m a g e C o l o r s %
3170 % %
3171 % %
3172 % %
3173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174 %
3175 % LevelImageColors() maps the given color to "black" and "white" values,
3176 % linearly spreading out the colors, and level values on a channel by channel
3177 % bases, as per LevelImage(). The given colors allows you to specify
3178 % different level ranges for each of the color channels separately.
3179 %
3180 % If the boolean 'invert' is set true the image values will modifyed in the
3181 % reverse direction. That is any existing "black" and "white" colors in the
3182 % image will become the color values given, with all other values compressed
3183 % appropriately. This effectivally maps a greyscale gradient into the given
3184 % color gradient.
3185 %
3186 % The format of the LevelImageColors method is:
3187 %
3188 % MagickBooleanType LevelImageColors(Image *image,
3189 % const PixelInfo *black_color,const PixelInfo *white_color,
3190 % const MagickBooleanType invert,ExceptionInfo *exception)
3191 %
3192 % A description of each parameter follows:
3193 %
3194 % o image: the image.
3195 %
3196 % o black_color: The color to map black to/from
3197 %
3198 % o white_point: The color to map white to/from
3199 %
3200 % o invert: if true map the colors (levelize), rather than from (level)
3201 %
3202 % o exception: return any errors or warnings in this structure.
3203 %
3204 */
3206  const PixelInfo *black_color,const PixelInfo *white_color,
3207  const MagickBooleanType invert,ExceptionInfo *exception)
3208 {
3209  ChannelType
3210  channel_mask;
3211 
3213  status;
3214 
3215  /*
3216  Allocate and initialize levels map.
3217  */
3218  assert(image != (Image *) NULL);
3219  assert(image->signature == MagickCoreSignature);
3220  if (image->debug != MagickFalse)
3221  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3222  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3223  ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3224  (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3225  (void) SetImageColorspace(image,sRGBColorspace,exception);
3226  status=MagickTrue;
3227  if (invert == MagickFalse)
3228  {
3229  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3230  {
3231  channel_mask=SetImageChannelMask(image,RedChannel);
3232  status&=LevelImage(image,black_color->red,white_color->red,1.0,
3233  exception);
3234  (void) SetImageChannelMask(image,channel_mask);
3235  }
3236  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3237  {
3238  channel_mask=SetImageChannelMask(image,GreenChannel);
3239  status&=LevelImage(image,black_color->green,white_color->green,1.0,
3240  exception);
3241  (void) SetImageChannelMask(image,channel_mask);
3242  }
3243  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3244  {
3245  channel_mask=SetImageChannelMask(image,BlueChannel);
3246  status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
3247  exception);
3248  (void) SetImageChannelMask(image,channel_mask);
3249  }
3250  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3251  (image->colorspace == CMYKColorspace))
3252  {
3253  channel_mask=SetImageChannelMask(image,BlackChannel);
3254  status&=LevelImage(image,black_color->black,white_color->black,1.0,
3255  exception);
3256  (void) SetImageChannelMask(image,channel_mask);
3257  }
3258  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3259  (image->alpha_trait != UndefinedPixelTrait))
3260  {
3261  channel_mask=SetImageChannelMask(image,AlphaChannel);
3262  status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
3263  exception);
3264  (void) SetImageChannelMask(image,channel_mask);
3265  }
3266  }
3267  else
3268  {
3269  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3270  {
3271  channel_mask=SetImageChannelMask(image,RedChannel);
3272  status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
3273  exception);
3274  (void) SetImageChannelMask(image,channel_mask);
3275  }
3276  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3277  {
3278  channel_mask=SetImageChannelMask(image,GreenChannel);
3279  status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
3280  exception);
3281  (void) SetImageChannelMask(image,channel_mask);
3282  }
3283  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3284  {
3285  channel_mask=SetImageChannelMask(image,BlueChannel);
3286  status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
3287  exception);
3288  (void) SetImageChannelMask(image,channel_mask);
3289  }
3290  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3291  (image->colorspace == CMYKColorspace))
3292  {
3293  channel_mask=SetImageChannelMask(image,BlackChannel);
3294  status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
3295  exception);
3296  (void) SetImageChannelMask(image,channel_mask);
3297  }
3298  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3299  (image->alpha_trait != UndefinedPixelTrait))
3300  {
3301  channel_mask=SetImageChannelMask(image,AlphaChannel);
3302  status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
3303  exception);
3304  (void) SetImageChannelMask(image,channel_mask);
3305  }
3306  }
3307  return(status != 0 ? MagickTrue : MagickFalse);
3308 }
3309 
3310 /*
3311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3312 % %
3313 % %
3314 % %
3315 % L i n e a r S t r e t c h I m a g e %
3316 % %
3317 % %
3318 % %
3319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3320 %
3321 % LinearStretchImage() discards any pixels below the black point and above
3322 % the white point and levels the remaining pixels.
3323 %
3324 % The format of the LinearStretchImage method is:
3325 %
3326 % MagickBooleanType LinearStretchImage(Image *image,
3327 % const double black_point,const double white_point,
3328 % ExceptionInfo *exception)
3329 %
3330 % A description of each parameter follows:
3331 %
3332 % o image: the image.
3333 %
3334 % o black_point: the black point.
3335 %
3336 % o white_point: the white point.
3337 %
3338 % o exception: return any errors or warnings in this structure.
3339 %
3340 */
3342  const double black_point,const double white_point,ExceptionInfo *exception)
3343 {
3344 #define LinearStretchImageTag "LinearStretch/Image"
3345 
3346  CacheView
3347  *image_view;
3348 
3349  double
3350  *histogram,
3351  intensity;
3352 
3354  status;
3355 
3356  ssize_t
3357  black,
3358  white,
3359  y;
3360 
3361  /*
3362  Allocate histogram and linear map.
3363  */
3364  assert(image != (Image *) NULL);
3365  assert(image->signature == MagickCoreSignature);
3366  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
3367  if (histogram == (double *) NULL)
3368  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3369  image->filename);
3370  /*
3371  Form histogram.
3372  */
3373  (void) memset(histogram,0,(MaxMap+1)*sizeof(*histogram));
3374  image_view=AcquireVirtualCacheView(image,exception);
3375  for (y=0; y < (ssize_t) image->rows; y++)
3376  {
3377  const Quantum
3378  *magick_restrict p;
3379 
3380  ssize_t
3381  x;
3382 
3383  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3384  if (p == (const Quantum *) NULL)
3385  break;
3386  for (x=0; x < (ssize_t) image->columns; x++)
3387  {
3388  intensity=GetPixelIntensity(image,p);
3389  histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3390  p+=GetPixelChannels(image);
3391  }
3392  }
3393  image_view=DestroyCacheView(image_view);
3394  /*
3395  Find the histogram boundaries by locating the black and white point levels.
3396  */
3397  intensity=0.0;
3398  for (black=0; black < (ssize_t) MaxMap; black++)
3399  {
3400  intensity+=histogram[black];
3401  if (intensity >= black_point)
3402  break;
3403  }
3404  intensity=0.0;
3405  for (white=(ssize_t) MaxMap; white != 0; white--)
3406  {
3407  intensity+=histogram[white];
3408  if (intensity >= white_point)
3409  break;
3410  }
3411  histogram=(double *) RelinquishMagickMemory(histogram);
3412  status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
3413  (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3414  return(status);
3415 }
3416 
3417 
3418 /*
3419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3420 % %
3421 % %
3422 % %
3423 % M o d u l a t e I m a g e %
3424 % %
3425 % %
3426 % %
3427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3428 %
3429 % ModulateImage() lets you control the brightness, saturation, and hue
3430 % of an image. Modulate represents the brightness, saturation, and hue
3431 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
3432 % modulation is lightness, saturation, and hue. For HWB, use blackness,
3433 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
3434 %
3435 % The format of the ModulateImage method is:
3436 %
3437 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
3438 % ExceptionInfo *exception)
3439 %
3440 % A description of each parameter follows:
3441 %
3442 % o image: the image.
3443 %
3444 % o modulate: Define the percent change in brightness, saturation, and hue.
3445 %
3446 % o exception: return any errors or warnings in this structure.
3447 %
3448 */
3449 
3450 static inline void ModulateHCL(const double percent_hue,
3451  const double percent_chroma,const double percent_luma,double *red,
3452  double *green,double *blue)
3453 {
3454  double
3455  hue,
3456  luma,
3457  chroma;
3458 
3459  /*
3460  Increase or decrease color luma, chroma, or hue.
3461  */
3462  ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3463  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3464  chroma*=0.01*percent_chroma;
3465  luma*=0.01*percent_luma;
3466  ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3467 }
3468 
3469 static inline void ModulateHCLp(const double percent_hue,
3470  const double percent_chroma,const double percent_luma,double *red,
3471  double *green,double *blue)
3472 {
3473  double
3474  hue,
3475  luma,
3476  chroma;
3477 
3478  /*
3479  Increase or decrease color luma, chroma, or hue.
3480  */
3481  ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3482  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3483  chroma*=0.01*percent_chroma;
3484  luma*=0.01*percent_luma;
3485  ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3486 }
3487 
3488 static inline void ModulateHSB(const double percent_hue,
3489  const double percent_saturation,const double percent_brightness,double *red,
3490  double *green,double *blue)
3491 {
3492  double
3493  brightness,
3494  hue,
3495  saturation;
3496 
3497  /*
3498  Increase or decrease color brightness, saturation, or hue.
3499  */
3500  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3501  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3502  saturation*=0.01*percent_saturation;
3503  brightness*=0.01*percent_brightness;
3504  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3505 }
3506 
3507 static inline void ModulateHSI(const double percent_hue,
3508  const double percent_saturation,const double percent_intensity,double *red,
3509  double *green,double *blue)
3510 {
3511  double
3512  intensity,
3513  hue,
3514  saturation;
3515 
3516  /*
3517  Increase or decrease color intensity, saturation, or hue.
3518  */
3519  ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3520  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3521  saturation*=0.01*percent_saturation;
3522  intensity*=0.01*percent_intensity;
3523  ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3524 }
3525 
3526 static inline void ModulateHSL(const double percent_hue,
3527  const double percent_saturation,const double percent_lightness,double *red,
3528  double *green,double *blue)
3529 {
3530  double
3531  hue,
3532  lightness,
3533  saturation;
3534 
3535  /*
3536  Increase or decrease color lightness, saturation, or hue.
3537  */
3538  ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3539  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3540  saturation*=0.01*percent_saturation;
3541  lightness*=0.01*percent_lightness;
3542  ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3543 }
3544 
3545 static inline void ModulateHSV(const double percent_hue,
3546  const double percent_saturation,const double percent_value,double *red,
3547  double *green,double *blue)
3548 {
3549  double
3550  hue,
3551  saturation,
3552  value;
3553 
3554  /*
3555  Increase or decrease color value, saturation, or hue.
3556  */
3557  ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3558  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3559  saturation*=0.01*percent_saturation;
3560  value*=0.01*percent_value;
3561  ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3562 }
3563 
3564 static inline void ModulateHWB(const double percent_hue,
3565  const double percent_whiteness,const double percent_blackness,double *red,
3566  double *green,double *blue)
3567 {
3568  double
3569  blackness,
3570  hue,
3571  whiteness;
3572 
3573  /*
3574  Increase or decrease color blackness, whiteness, or hue.
3575  */
3576  ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3577  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3578  blackness*=0.01*percent_blackness;
3579  whiteness*=0.01*percent_whiteness;
3580  ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3581 }
3582 
3583 static inline void ModulateLCHab(const double percent_luma,
3584  const double percent_chroma,const double percent_hue,double *red,
3585  double *green,double *blue)
3586 {
3587  double
3588  hue,
3589  luma,
3590  chroma;
3591 
3592  /*
3593  Increase or decrease color luma, chroma, or hue.
3594  */
3595  ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3596  luma*=0.01*percent_luma;
3597  chroma*=0.01*percent_chroma;
3598  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3599  ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3600 }
3601 
3602 static inline void ModulateLCHuv(const double percent_luma,
3603  const double percent_chroma,const double percent_hue,double *red,
3604  double *green,double *blue)
3605 {
3606  double
3607  hue,
3608  luma,
3609  chroma;
3610 
3611  /*
3612  Increase or decrease color luma, chroma, or hue.
3613  */
3614  ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3615  luma*=0.01*percent_luma;
3616  chroma*=0.01*percent_chroma;
3617  hue+=fmod((percent_hue-100.0),200.0)/200.0;
3618  ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3619 }
3620 
3622  ExceptionInfo *exception)
3623 {
3624 #define ModulateImageTag "Modulate/Image"
3625 
3626  CacheView
3627  *image_view;
3628 
3630  colorspace;
3631 
3632  const char
3633  *artifact;
3634 
3635  double
3636  percent_brightness,
3637  percent_hue,
3638  percent_saturation;
3639 
3640  GeometryInfo
3641  geometry_info;
3642 
3644  status;
3645 
3647  progress;
3648 
3650  flags;
3651 
3652  ssize_t
3653  i;
3654 
3655  ssize_t
3656  y;
3657 
3658  /*
3659  Initialize modulate table.
3660  */
3661  assert(image != (Image *) NULL);
3662  assert(image->signature == MagickCoreSignature);
3663  if (image->debug != MagickFalse)
3664  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3665  if (modulate == (char *) NULL)
3666  return(MagickFalse);
3668  (void) SetImageColorspace(image,sRGBColorspace,exception);
3669  flags=ParseGeometry(modulate,&geometry_info);
3670  percent_brightness=geometry_info.rho;
3671  percent_saturation=geometry_info.sigma;
3672  if ((flags & SigmaValue) == 0)
3673  percent_saturation=100.0;
3674  percent_hue=geometry_info.xi;
3675  if ((flags & XiValue) == 0)
3676  percent_hue=100.0;
3677  colorspace=UndefinedColorspace;
3678  artifact=GetImageArtifact(image,"modulate:colorspace");
3679  if (artifact != (const char *) NULL)
3681  MagickFalse,artifact);
3682  if (image->storage_class == PseudoClass)
3683  for (i=0; i < (ssize_t) image->colors; i++)
3684  {
3685  double
3686  blue,
3687  green,
3688  red;
3689 
3690  /*
3691  Modulate image colormap.
3692  */
3693  red=(double) image->colormap[i].red;
3694  green=(double) image->colormap[i].green;
3695  blue=(double) image->colormap[i].blue;
3696  switch (colorspace)
3697  {
3698  case HCLColorspace:
3699  {
3700  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3701  &red,&green,&blue);
3702  break;
3703  }
3704  case HCLpColorspace:
3705  {
3706  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3707  &red,&green,&blue);
3708  break;
3709  }
3710  case HSBColorspace:
3711  {
3712  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3713  &red,&green,&blue);
3714  break;
3715  }
3716  case HSIColorspace:
3717  {
3718  ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3719  &red,&green,&blue);
3720  break;
3721  }
3722  case HSLColorspace:
3723  default:
3724  {
3725  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3726  &red,&green,&blue);
3727  break;
3728  }
3729  case HSVColorspace:
3730  {
3731  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3732  &red,&green,&blue);
3733  break;
3734  }
3735  case HWBColorspace:
3736  {
3737  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3738  &red,&green,&blue);
3739  break;
3740  }
3741  case LCHColorspace:
3742  case LCHabColorspace:
3743  {
3744  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3745  &red,&green,&blue);
3746  break;
3747  }
3748  case LCHuvColorspace:
3749  {
3750  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3751  &red,&green,&blue);
3752  break;
3753  }
3754  }
3755  image->colormap[i].red=red;
3756  image->colormap[i].green=green;
3757  image->colormap[i].blue=blue;
3758  }
3759  /*
3760  Modulate image.
3761  */
3762 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3763  if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3764  percent_saturation,colorspace,exception) != MagickFalse)
3765  return(MagickTrue);
3766 #endif
3767  status=MagickTrue;
3768  progress=0;
3769  image_view=AcquireAuthenticCacheView(image,exception);
3770 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3771  #pragma omp parallel for schedule(static) shared(progress,status) \
3772  magick_number_threads(image,image,image->rows,1)
3773 #endif
3774  for (y=0; y < (ssize_t) image->rows; y++)
3775  {
3776  Quantum
3777  *magick_restrict q;
3778 
3779  ssize_t
3780  x;
3781 
3782  if (status == MagickFalse)
3783  continue;
3784  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3785  if (q == (Quantum *) NULL)
3786  {
3787  status=MagickFalse;
3788  continue;
3789  }
3790  for (x=0; x < (ssize_t) image->columns; x++)
3791  {
3792  double
3793  blue,
3794  green,
3795  red;
3796 
3797  red=(double) GetPixelRed(image,q);
3798  green=(double) GetPixelGreen(image,q);
3799  blue=(double) GetPixelBlue(image,q);
3800  switch (colorspace)
3801  {
3802  case HCLColorspace:
3803  {
3804  ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3805  &red,&green,&blue);
3806  break;
3807  }
3808  case HCLpColorspace:
3809  {
3810  ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3811  &red,&green,&blue);
3812  break;
3813  }
3814  case HSBColorspace:
3815  {
3816  ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3817  &red,&green,&blue);
3818  break;
3819  }
3820  case HSLColorspace:
3821  default:
3822  {
3823  ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3824  &red,&green,&blue);
3825  break;
3826  }
3827  case HSVColorspace:
3828  {
3829  ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3830  &red,&green,&blue);
3831  break;
3832  }
3833  case HWBColorspace:
3834  {
3835  ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3836  &red,&green,&blue);
3837  break;
3838  }
3839  case LCHabColorspace:
3840  {
3841  ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3842  &red,&green,&blue);
3843  break;
3844  }
3845  case LCHColorspace:
3846  case LCHuvColorspace:
3847  {
3848  ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3849  &red,&green,&blue);
3850  break;
3851  }
3852  }
3853  SetPixelRed(image,ClampToQuantum(red),q);
3854  SetPixelGreen(image,ClampToQuantum(green),q);
3855  SetPixelBlue(image,ClampToQuantum(blue),q);
3856  q+=GetPixelChannels(image);
3857  }
3858  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859  status=MagickFalse;
3860  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3861  {
3863  proceed;
3864 
3865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3866  #pragma omp atomic
3867 #endif
3868  progress++;
3869  proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3870  if (proceed == MagickFalse)
3871  status=MagickFalse;
3872  }
3873  }
3874  image_view=DestroyCacheView(image_view);
3875  return(status);
3876 }
3877 
3878 /*
3879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3880 % %
3881 % %
3882 % %
3883 % N e g a t e I m a g e %
3884 % %
3885 % %
3886 % %
3887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3888 %
3889 % NegateImage() negates the colors in the reference image. The grayscale
3890 % option means that only grayscale values within the image are negated.
3891 %
3892 % The format of the NegateImage method is:
3893 %
3894 % MagickBooleanType NegateImage(Image *image,
3895 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3896 %
3897 % A description of each parameter follows:
3898 %
3899 % o image: the image.
3900 %
3901 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3902 %
3903 % o exception: return any errors or warnings in this structure.
3904 %
3905 */
3907  const MagickBooleanType grayscale,ExceptionInfo *exception)
3908 {
3909 #define NegateImageTag "Negate/Image"
3910 
3911  CacheView
3912  *image_view;
3913 
3915  status;
3916 
3918  progress;
3919 
3920  ssize_t
3921  i;
3922 
3923  ssize_t
3924  y;
3925 
3926  assert(image != (Image *) NULL);
3927  assert(image->signature == MagickCoreSignature);
3928  if (image->debug != MagickFalse)
3929  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3930  if (image->storage_class == PseudoClass)
3931  for (i=0; i < (ssize_t) image->colors; i++)
3932  {
3933  /*
3934  Negate colormap.
3935  */
3936  if (grayscale != MagickFalse)
3937  if ((image->colormap[i].red != image->colormap[i].green) ||
3938  (image->colormap[i].green != image->colormap[i].blue))
3939  continue;
3940  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3941  image->colormap[i].red=QuantumRange-image->colormap[i].red;
3942  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3943  image->colormap[i].green=QuantumRange-image->colormap[i].green;
3944  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3945  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3946  }
3947  /*
3948  Negate image.
3949  */
3950  status=MagickTrue;
3951  progress=0;
3952  image_view=AcquireAuthenticCacheView(image,exception);
3953  if( grayscale != MagickFalse )
3954  {
3955  for (y=0; y < (ssize_t) image->rows; y++)
3956  {
3958  sync;
3959 
3960  Quantum
3961  *magick_restrict q;
3962 
3963  ssize_t
3964  x;
3965 
3966  if (status == MagickFalse)
3967  continue;
3968  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3969  exception);
3970  if (q == (Quantum *) NULL)
3971  {
3972  status=MagickFalse;
3973  continue;
3974  }
3975  for (x=0; x < (ssize_t) image->columns; x++)
3976  {
3977  ssize_t
3978  j;
3979 
3980  if (IsPixelGray(image,q) == MagickFalse)
3981  {
3982  q+=GetPixelChannels(image);
3983  continue;
3984  }
3985  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3986  {
3987  PixelChannel channel = GetPixelChannelChannel(image,j);
3988  PixelTrait traits = GetPixelChannelTraits(image,channel);
3989  if ((traits & UpdatePixelTrait) == 0)
3990  continue;
3991  q[j]=QuantumRange-q[j];
3992  }
3993  q+=GetPixelChannels(image);
3994  }
3995  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3996  if (sync == MagickFalse)
3997  status=MagickFalse;
3998  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3999  {
4001  proceed;
4002 
4003  progress++;
4004  proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4005  if (proceed == MagickFalse)
4006  status=MagickFalse;
4007  }
4008  }
4009  image_view=DestroyCacheView(image_view);
4010  return(MagickTrue);
4011  }
4012  /*
4013  Negate image.
4014  */
4015 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4016  #pragma omp parallel for schedule(static) shared(progress,status) \
4017  magick_number_threads(image,image,image->rows,1)
4018 #endif
4019  for (y=0; y < (ssize_t) image->rows; y++)
4020  {
4021  Quantum
4022  *magick_restrict q;
4023 
4024  ssize_t
4025  x;
4026 
4027  if (status == MagickFalse)
4028  continue;
4029  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4030  if (q == (Quantum *) NULL)
4031  {
4032  status=MagickFalse;
4033  continue;
4034  }
4035  for (x=0; x < (ssize_t) image->columns; x++)
4036  {
4037  ssize_t
4038  j;
4039 
4040  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4041  {
4042  PixelChannel channel = GetPixelChannelChannel(image,j);
4043  PixelTrait traits = GetPixelChannelTraits(image,channel);
4044  if ((traits & UpdatePixelTrait) == 0)
4045  continue;
4046  q[j]=QuantumRange-q[j];
4047  }
4048  q+=GetPixelChannels(image);
4049  }
4050  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4051  status=MagickFalse;
4052  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4053  {
4055  proceed;
4056 
4057 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4058  #pragma omp atomic
4059 #endif
4060  progress++;
4061  proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4062  if (proceed == MagickFalse)
4063  status=MagickFalse;
4064  }
4065  }
4066  image_view=DestroyCacheView(image_view);
4067  return(status);
4068 }
4069 
4070 /*
4071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4072 % %
4073 % %
4074 % %
4075 % N o r m a l i z e I m a g e %
4076 % %
4077 % %
4078 % %
4079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4080 %
4081 % The NormalizeImage() method enhances the contrast of a color image by
4082 % mapping the darkest 2 percent of all pixel to black and the brightest
4083 % 1 percent to white.
4084 %
4085 % The format of the NormalizeImage method is:
4086 %
4087 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
4088 %
4089 % A description of each parameter follows:
4090 %
4091 % o image: the image.
4092 %
4093 % o exception: return any errors or warnings in this structure.
4094 %
4095 */
4097  ExceptionInfo *exception)
4098 {
4099  double
4100  black_point,
4101  white_point;
4102 
4103  black_point=(double) image->columns*image->rows*0.0015;
4104  white_point=(double) image->columns*image->rows*0.9995;
4105  return(ContrastStretchImage(image,black_point,white_point,exception));
4106 }
4107 
4108 /*
4109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4110 % %
4111 % %
4112 % %
4113 % S i g m o i d a l C o n t r a s t I m a g e %
4114 % %
4115 % %
4116 % %
4117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4118 %
4119 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
4120 % sigmoidal contrast algorithm. Increase the contrast of the image using a
4121 % sigmoidal transfer function without saturating highlights or shadows.
4122 % Contrast indicates how much to increase the contrast (0 is none; 3 is
4123 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
4124 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
4125 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
4126 % is reduced.
4127 %
4128 % The format of the SigmoidalContrastImage method is:
4129 %
4130 % MagickBooleanType SigmoidalContrastImage(Image *image,
4131 % const MagickBooleanType sharpen,const char *levels,
4132 % ExceptionInfo *exception)
4133 %
4134 % A description of each parameter follows:
4135 %
4136 % o image: the image.
4137 %
4138 % o sharpen: Increase or decrease image contrast.
4139 %
4140 % o contrast: strength of the contrast, the larger the number the more
4141 % 'threshold-like' it becomes.
4142 %
4143 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
4144 %
4145 % o exception: return any errors or warnings in this structure.
4146 %
4147 */
4148 
4149 /*
4150  ImageMagick 6 has a version of this function which uses LUTs.
4151 */
4152 
4153 /*
4154  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
4155  constant" set to a.
4156 
4157  The first version, based on the hyperbolic tangent tanh, when combined with
4158  the scaling step, is an exact arithmetic clone of the sigmoid function
4159  based on the logistic curve. The equivalence is based on the identity
4160 
4161  1/(1+exp(-t)) = (1+tanh(t/2))/2
4162 
4163  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
4164  scaled sigmoidal derivation is invariant under affine transformations of
4165  the ordinate.
4166 
4167  The tanh version is almost certainly more accurate and cheaper. The 0.5
4168  factor in the argument is to clone the legacy ImageMagick behavior. The
4169  reason for making the define depend on atanh even though it only uses tanh
4170  has to do with the construction of the inverse of the scaled sigmoidal.
4171 */
4172 #if defined(MAGICKCORE_HAVE_ATANH)
4173 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4174 #else
4175 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4176 #endif
4177 /*
4178  Scaled sigmoidal function:
4179 
4180  ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
4181  ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
4182 
4183  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
4184  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
4185  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
4186  zero. This is fixed below by exiting immediately when contrast is small,
4187  leaving the image (or colormap) unmodified. This appears to be safe because
4188  the series expansion of the logistic sigmoidal function around x=b is
4189 
4190  1/2-a*(b-x)/4+...
4191 
4192  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
4193 */
4194 #define ScaledSigmoidal(a,b,x) ( \
4195  (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4196  (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4197 /*
4198  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
4199  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
4200  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
4201  when creating a LUT from in gamut values, hence the branching. In
4202  addition, HDRI may have out of gamut values.
4203  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
4204  It is only a right inverse. This is unavoidable.
4205 */
4206 static inline double InverseScaledSigmoidal(const double a,const double b,
4207  const double x)
4208 {
4209  const double sig0=Sigmoidal(a,b,0.0);
4210  const double sig1=Sigmoidal(a,b,1.0);
4211  const double argument=(sig1-sig0)*x+sig0;
4212  const double clamped=
4213  (
4214 #if defined(MAGICKCORE_HAVE_ATANH)
4215  argument < -1+MagickEpsilon
4216  ?
4217  -1+MagickEpsilon
4218  :
4219  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4220  );
4221  return(b+(2.0/a)*atanh(clamped));
4222 #else
4223  argument < MagickEpsilon
4224  ?
4226  :
4227  ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4228  );
4229  return(b-log(1.0/clamped-1.0)/a);
4230 #endif
4231 }
4232 
4234  const MagickBooleanType sharpen,const double contrast,const double midpoint,
4235  ExceptionInfo *exception)
4236 {
4237 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4238 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
4239  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4240 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
4241  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4242 
4243  CacheView
4244  *image_view;
4245 
4247  status;
4248 
4250  progress;
4251 
4252  ssize_t
4253  y;
4254 
4255  /*
4256  Convenience macros.
4257  */
4258  assert(image != (Image *) NULL);
4259  assert(image->signature == MagickCoreSignature);
4260  if (image->debug != MagickFalse)
4261  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4262  /*
4263  Side effect: may clamp values unless contrast<MagickEpsilon, in which
4264  case nothing is done.
4265  */
4266  if (contrast < MagickEpsilon)
4267  return(MagickTrue);
4268  /*
4269  Sigmoidal-contrast enhance colormap.
4270  */
4271  if (image->storage_class == PseudoClass)
4272  {
4273  ssize_t
4274  i;
4275 
4276  if( sharpen != MagickFalse )
4277  for (i=0; i < (ssize_t) image->colors; i++)
4278  {
4279  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4280  image->colormap[i].red=(MagickRealType) ScaledSig(
4281  image->colormap[i].red);
4282  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4283  image->colormap[i].green=(MagickRealType) ScaledSig(
4284  image->colormap[i].green);
4285  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4286  image->colormap[i].blue=(MagickRealType) ScaledSig(
4287  image->colormap[i].blue);
4288  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4289  image->colormap[i].alpha=(MagickRealType) ScaledSig(
4290  image->colormap[i].alpha);
4291  }
4292  else
4293  for (i=0; i < (ssize_t) image->colors; i++)
4294  {
4295  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4297  image->colormap[i].red);
4298  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4300  image->colormap[i].green);
4301  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4303  image->colormap[i].blue);
4304  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4306  image->colormap[i].alpha);
4307  }
4308  }
4309  /*
4310  Sigmoidal-contrast enhance image.
4311  */
4312  status=MagickTrue;
4313  progress=0;
4314  image_view=AcquireAuthenticCacheView(image,exception);
4315 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4316  #pragma omp parallel for schedule(static) shared(progress,status) \
4317  magick_number_threads(image,image,image->rows,1)
4318 #endif
4319  for (y=0; y < (ssize_t) image->rows; y++)
4320  {
4321  Quantum
4322  *magick_restrict q;
4323 
4324  ssize_t
4325  x;
4326 
4327  if (status == MagickFalse)
4328  continue;
4329  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4330  if (q == (Quantum *) NULL)
4331  {
4332  status=MagickFalse;
4333  continue;
4334  }
4335  for (x=0; x < (ssize_t) image->columns; x++)
4336  {
4337  ssize_t
4338  i;
4339 
4340  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4341  {
4342  PixelChannel channel = GetPixelChannelChannel(image,i);
4343  PixelTrait traits = GetPixelChannelTraits(image,channel);
4344  if ((traits & UpdatePixelTrait) == 0)
4345  continue;
4346  if( sharpen != MagickFalse )
4347  q[i]=ScaledSig(q[i]);
4348  else
4349  q[i]=InverseScaledSig(q[i]);
4350  }
4351  q+=GetPixelChannels(image);
4352  }
4353  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4354  status=MagickFalse;
4355  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4356  {
4358  proceed;
4359 
4360 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4361  #pragma omp atomic
4362 #endif
4363  progress++;
4364  proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4365  image->rows);
4366  if (proceed == MagickFalse)
4367  status=MagickFalse;
4368  }
4369  }
4370  image_view=DestroyCacheView(image_view);
4371  return(status);
4372 }
4373 
4374 /*
4375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4376 % %
4377 % %
4378 % %
4379 % W h i t e B a l a n c e I m a g e %
4380 % %
4381 % %
4382 % %
4383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4384 %
4385 % WhiteBalanceImage() applies white balancing to an image according to a
4386 % grayworld assumption in the LAB colorspace.
4387 %
4388 % The format of the WhiteBalanceImage method is:
4389 %
4390 % MagickBooleanType WhiteBalanceImage(Image *image,
4391 % ExceptionInfo *exception)
4392 %
4393 % A description of each parameter follows:
4394 %
4395 % o image: The image to auto-level
4396 %
4397 % o exception: return any errors or warnings in this structure.
4398 %
4399 */
4401  ExceptionInfo *exception)
4402 {
4403 #define WhiteBalanceImageTag "WhiteBalance/Image"
4404 
4405  CacheView
4406  *image_view;
4407 
4408  const char
4409  *artifact;
4410 
4411  double
4412  a_mean,
4413  b_mean;
4414 
4416  progress;
4417 
4419  status;
4420 
4421  ssize_t
4422  y;
4423 
4424  /*
4425  White balance image.
4426  */
4427  assert(image != (Image *) NULL);
4428  assert(image->signature == MagickCoreSignature);
4429  if (image->debug != MagickFalse)
4430  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4431  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4432  return(MagickFalse);
4433  status=TransformImageColorspace(image,LabColorspace,exception);
4434  a_mean=0.0;
4435  b_mean=0.0;
4436  image_view=AcquireAuthenticCacheView(image,exception);
4437  for (y=0; y < (ssize_t) image->rows; y++)
4438  {
4439  const Quantum
4440  *magick_restrict p;
4441 
4442  ssize_t
4443  x;
4444 
4445  if (status == MagickFalse)
4446  continue;
4447  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4448  if (p == (Quantum *) NULL)
4449  {
4450  status=MagickFalse;
4451  continue;
4452  }
4453  for (x=0; x < (ssize_t) image->columns; x++)
4454  {
4455  a_mean+=QuantumScale*GetPixela(image,p)-0.5;
4456  b_mean+=QuantumScale*GetPixelb(image,p)-0.5;
4457  p+=GetPixelChannels(image);
4458  }
4459  }
4460  a_mean/=((double) image->columns*image->rows);
4461  b_mean/=((double) image->columns*image->rows);
4462  progress=0;
4463 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4464  #pragma omp parallel for schedule(static) shared(progress,status) \
4465  magick_number_threads(image,image,image->rows,1)
4466 #endif
4467  for (y=0; y < (ssize_t) image->rows; y++)
4468  {
4469  Quantum
4470  *magick_restrict q;
4471 
4472  ssize_t
4473  x;
4474 
4475  if (status == MagickFalse)
4476  continue;
4477  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4478  if (q == (Quantum *) NULL)
4479  {
4480  status=MagickFalse;
4481  continue;
4482  }
4483  for (x=0; x < (ssize_t) image->columns; x++)
4484  {
4485  double
4486  a,
4487  b;
4488 
4489  /*
4490  Scale the chroma distance shifted according to amount of luminance.
4491  */
4492  a=(double) GetPixela(image,q)-1.1*GetPixelL(image,q)*a_mean;
4493  b=(double) GetPixelb(image,q)-1.1*GetPixelL(image,q)*b_mean;
4494  SetPixela(image,ClampToQuantum(a),q);
4495  SetPixelb(image,ClampToQuantum(b),q);
4496  q+=GetPixelChannels(image);
4497  }
4498  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4499  status=MagickFalse;
4500  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4501  {
4503  proceed;
4504 
4505 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4506  #pragma omp atomic
4507 #endif
4508  progress++;
4509  proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4510  if (proceed == MagickFalse)
4511  status=MagickFalse;
4512  }
4513  }
4514  image_view=DestroyCacheView(image_view);
4515  artifact=GetImageArtifact(image,"white-balance:vibrance");
4516  if (artifact != (const char *) NULL)
4517  {
4518  ChannelType
4519  channel_mask;
4520 
4521  double
4522  black_point;
4523 
4524  GeometryInfo
4525  geometry_info;
4526 
4528  flags;
4529 
4530  /*
4531  Level the a & b channels.
4532  */
4533  flags=ParseGeometry(artifact,&geometry_info);
4534  black_point=geometry_info.rho;
4535  if ((flags & PercentValue) != 0)
4536  black_point*=(double) (QuantumRange/100.0);
4537  channel_mask=SetImageChannelMask(image,(ChannelType) (aChannel |
4538  bChannel));
4539  status&=LevelImage(image,black_point,(double) QuantumRange-black_point,
4540  1.0,exception);
4541  (void) SetImageChannelMask(image,channel_mask);
4542  }
4543  status&=TransformImageColorspace(image,sRGBColorspace,exception);
4544  return(status != 0 ? MagickTrue : MagickFalse);
4545 }
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:128
MagickExport MagickBooleanType NegateImage(Image *image, const MagickBooleanType grayscale, ExceptionInfo *exception)
Definition: enhance.c:3906
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:3056
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:3341
MagickPrivate void ConvertLCHabToRGB(const double, const double, const double, double *, double *, double *)
MagickExport MagickBooleanType AutoGammaImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:111
static ssize_t GetPixelChannelOffset(const Image *magick_restrict image, const PixelChannel channel)
static void ModulateHWB(const double percent_hue, const double percent_whiteness, const double percent_blackness, double *red, double *green, double *blue)
Definition: enhance.c:3564
PixelInfo * colormap
Definition: image.h:179
MagickExport void ConvertRGBToHSL(const double red, const double green, const double blue, double *hue, double *saturation, double *lightness)
Definition: gem.c:1099
MagickExport MemoryInfo * RelinquishVirtualMemory(MemoryInfo *memory_info)
Definition: memory.c:1229
MagickPrivate void ConvertHSIToRGB(const double, const double, const double, double *, double *, double *)
MagickExport XMLTreeInfo * DestroyXMLTree(XMLTreeInfo *xml_info)
Definition: xml-tree.c:557
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:3878
static PixelTrait GetPixelRedTraits(const Image *magick_restrict image)
MagickExport MagickBooleanType TransformImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1593
#define ContrastImageTag
#define Sigmoidal(a, b, x)
Definition: enhance.c:4175
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:3031
MagickExport MagickBooleanType FunctionImage(Image *image, const MagickFunction function, const size_t number_parameters, const double *parameters, ExceptionInfo *exception)
Definition: statistic.c:1072
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:3469
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:2892
#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 double StringToDouble(const char *magick_restrict string, char **magick_restrict sentinal)
static PixelTrait GetPixelChannelTraits(const Image *magick_restrict image, const PixelChannel channel)
#define MagickPI
Definition: image-private.h:40
MagickExport MagickBooleanType EqualizeImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:2029
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:3526
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:2306
MagickExport MagickBooleanType GrayscaleImage(Image *image, const PixelIntensityMethod method, ExceptionInfo *exception)
Definition: enhance.c:2463
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:118
MagickExport XMLTreeInfo * GetXMLTreeChild(XMLTreeInfo *xml_info, const char *tag)
Definition: xml-tree.c:895
double sigma
Definition: geometry.h:107
ClassType storage_class
Definition: image.h:154
size_t width
Definition: geometry.h:131
#define LevelImageTag
#define ThrowBinaryException(severity, tag, context)
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:137
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 *)
MagickExport MagickBooleanType SetImageGray(Image *image, ExceptionInfo *exception)
Definition: colorspace.c:1482
#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:4400
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:173
unsigned int MagickStatusType
Definition: magick-type.h:129
static double PerceptibleReciprocal(const double x)
MagickExport Image * EnhanceImage(const Image *image, ExceptionInfo *exception)
Definition: enhance.c:1834
#define GrayscaleImageTag
#define ContrastStretchImageTag
static MagickBooleanType IssRGBCompatibleColorspace(const ColorspaceType colorspace)
MagickExport MagickBooleanType NormalizeImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:4096
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
static void ModulateLCHuv(const double percent_luma, const double percent_chroma, const double percent_hue, double *red, double *green, double *blue)
Definition: enhance.c:3602
MagickExport MagickBooleanType ModulateImage(Image *image, const char *modulate, ExceptionInfo *exception)
Definition: enhance.c:3621
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
static void ModulateLCHab(const double percent_luma, const double percent_chroma, const double percent_hue, double *red, double *green, double *blue)
Definition: enhance.c:3583
ChannelType channel_mask
Definition: image.h:288
MagickPrivate void ConvertLCHuvToRGB(const double, const double, const double, double *, double *, double *)
MagickExport const char * GetXMLTreeContent(XMLTreeInfo *xml_info)
Definition: xml-tree.c:935
#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:2487
#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:3450
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:3507
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:3205
size_t columns
Definition: image.h:172
#define QuantumScale
Definition: magick-type.h:123
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
static PixelTrait GetPixelGreenTraits(const Image *magick_restrict image)
static void SetPixelBlue(const Image *magick_restrict image, const Quantum blue, Quantum *magick_restrict pixel)
ChannelType
Definition: pixel.h:33
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2603
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:1339
#define MaxMap
Definition: magick-type.h:83
#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:90
double xi
Definition: geometry.h:107
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1402
MagickExport MagickBooleanType LevelImage(Image *image, const double black_point, const double white_point, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:2905
#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:3545
static void ModulateHSB(const double percent_hue, const double percent_saturation, const double percent_brightness, double *red, double *green, double *blue)
Definition: enhance.c:3488
#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
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 *)
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:2675
MagickExport MagickBooleanType GammaImage(Image *image, const double gamma, ExceptionInfo *exception)
Definition: enhance.c:2311
MagickExport MagickBooleanType ClampImage(Image *image, ExceptionInfo *exception)
Definition: threshold.c:1089
MagickRealType green
Definition: pixel.h:193
#define WhiteBalanceImageTag
MagickPrivate void ConvertRGBToLCHab(const double, const double, const double, double *, double *, double *)
static void SetPixelRed(const Image *magick_restrict image, const Quantum red, Quantum *magick_restrict pixel)
MagickExport MagickBooleanType AutoLevelImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:185
#define ClutImageTag
#define MagickExport
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
ssize_t y
Definition: geometry.h: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:4233
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:4206
MagickPrivate void ConvertRGBToLCHuv(const double, const double, const double, double *, double *, double *)
Definition: gem.c:1414
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
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1168
#define ColorDecisionListCorrectImageTag
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:779
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:1955
#define QuantumRange
Definition: magick-type.h:91
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)