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