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