43#include "MagickCore/studio.h"
44#include "MagickCore/accelerate-private.h"
45#include "MagickCore/animate.h"
46#include "MagickCore/artifact.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/cache-private.h"
51#include "MagickCore/cache-view.h"
52#include "MagickCore/client.h"
53#include "MagickCore/color.h"
54#include "MagickCore/color-private.h"
55#include "MagickCore/colorspace.h"
56#include "MagickCore/colorspace-private.h"
57#include "MagickCore/composite.h"
58#include "MagickCore/composite-private.h"
59#include "MagickCore/compress.h"
60#include "MagickCore/constitute.h"
61#include "MagickCore/display.h"
62#include "MagickCore/draw.h"
63#include "MagickCore/enhance.h"
64#include "MagickCore/exception.h"
65#include "MagickCore/exception-private.h"
66#include "MagickCore/gem.h"
67#include "MagickCore/gem-private.h"
68#include "MagickCore/geometry.h"
69#include "MagickCore/list.h"
70#include "MagickCore/image-private.h"
71#include "MagickCore/magic.h"
72#include "MagickCore/magick.h"
73#include "MagickCore/memory_.h"
74#include "MagickCore/module.h"
75#include "MagickCore/monitor.h"
76#include "MagickCore/monitor-private.h"
77#include "MagickCore/option.h"
78#include "MagickCore/paint.h"
79#include "MagickCore/pixel-accessor.h"
80#include "MagickCore/profile.h"
81#include "MagickCore/property.h"
82#include "MagickCore/quantize.h"
83#include "MagickCore/quantum-private.h"
84#include "MagickCore/random_.h"
85#include "MagickCore/random-private.h"
86#include "MagickCore/resource_.h"
87#include "MagickCore/segment.h"
88#include "MagickCore/semaphore.h"
89#include "MagickCore/signature-private.h"
90#include "MagickCore/statistic.h"
91#include "MagickCore/statistic-private.h"
92#include "MagickCore/string_.h"
93#include "MagickCore/thread-private.h"
94#include "MagickCore/timer.h"
95#include "MagickCore/utility.h"
96#include "MagickCore/version.h"
138 channel[MaxPixelChannels];
141static PixelChannels **DestroyPixelTLS(
const Image *images,
142 PixelChannels **pixels)
150 assert(pixels != (PixelChannels **) NULL);
151 rows=MagickMax(GetImageListLength(images),(
size_t)
152 GetMagickResourceLimit(ThreadResource));
153 for (i=0; i < (ssize_t) rows; i++)
154 if (pixels[i] != (PixelChannels *) NULL)
155 pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]);
156 pixels=(PixelChannels **) RelinquishMagickMemory(pixels);
160static PixelChannels **AcquirePixelTLS(
const Image *images)
176 number_images=GetImageListLength(images);
177 rows=MagickMax(number_images,(
size_t) GetMagickResourceLimit(ThreadResource));
178 pixels=(PixelChannels **) AcquireQuantumMemory(rows,
sizeof(*pixels));
179 if (pixels == (PixelChannels **) NULL)
180 return((PixelChannels **) NULL);
181 (void) memset(pixels,0,rows*
sizeof(*pixels));
182 columns=MagickMax(number_images,MaxPixelChannels);
183 for (next=images; next != (Image *) NULL; next=next->next)
184 columns=MagickMax(next->columns,columns);
185 for (i=0; i < (ssize_t) rows; i++)
190 pixels[i]=(PixelChannels *) AcquireQuantumMemory(columns,
sizeof(**pixels));
191 if (pixels[i] == (PixelChannels *) NULL)
192 return(DestroyPixelTLS(images,pixels));
193 for (j=0; j < (ssize_t) columns; j++)
198 for (k=0; k < MaxPixelChannels; k++)
199 pixels[i][j].channel[k]=0.0;
205static inline double EvaluateMax(
const double x,
const double y)
212#if defined(__cplusplus) || defined(c_plusplus)
216static int IntensityCompare(
const void *x,
const void *y)
228 color_1=(
const PixelChannels *) x;
229 color_2=(
const PixelChannels *) y;
231 for (i=0; i < MaxPixelChannels; i++)
232 distance+=color_1->channel[i]-(
double) color_2->channel[i];
233 return(distance < 0.0 ? -1 : distance > 0.0 ? 1 : 0);
236#if defined(__cplusplus) || defined(c_plusplus)
240static double ApplyEvaluateOperator(RandomInfo *random_info,
const Quantum pixel,
241 const MagickEvaluateOperator op,
const double value)
252 case UndefinedEvaluateOperator:
254 case AbsEvaluateOperator:
256 result=(double) fabs((
double) pixel+value);
259 case AddEvaluateOperator:
261 result=(double) pixel+value;
264 case AddModulusEvaluateOperator:
272 result=(double) pixel+value;
273 result-=((double) QuantumRange+1.0)*floor(result/((
double)
277 case AndEvaluateOperator:
279 result=(double) ((ssize_t) pixel & (ssize_t) (value+0.5));
282 case CosineEvaluateOperator:
284 result=(double) QuantumRange*(0.5*cos((
double) (2.0*MagickPI*
285 QuantumScale*(
double) pixel*value))+0.5);
288 case DivideEvaluateOperator:
290 result=(double) pixel/(value == 0.0 ? 1.0 : value);
293 case ExponentialEvaluateOperator:
295 result=(double) QuantumRange*exp(value*QuantumScale*(
double) pixel);
298 case GaussianNoiseEvaluateOperator:
300 result=(double) GenerateDifferentialNoise(random_info,pixel,GaussianNoise,
304 case ImpulseNoiseEvaluateOperator:
306 result=(double) GenerateDifferentialNoise(random_info,pixel,ImpulseNoise,
310 case InverseLogEvaluateOperator:
312 result=(double) QuantumRange*pow((value+1.0),QuantumScale*(
double)
313 pixel-1.0)*MagickSafeReciprocal(value);
316 case LaplacianNoiseEvaluateOperator:
318 result=(double) GenerateDifferentialNoise(random_info,pixel,
319 LaplacianNoise,value);
322 case LeftShiftEvaluateOperator:
324 result=(double) pixel;
325 for (i=0; i < (ssize_t) value; i++)
329 case LogEvaluateOperator:
331 if ((QuantumScale*(
double) pixel) >= MagickEpsilon)
332 result=(double) QuantumRange*log(QuantumScale*value*
333 (
double) pixel+1.0)/log((
double) (value+1.0));
336 case MaxEvaluateOperator:
338 result=(double) EvaluateMax((
double) pixel,value);
341 case MeanEvaluateOperator:
343 result=(double) pixel+value;
346 case MedianEvaluateOperator:
348 result=(double) pixel+value;
351 case MinEvaluateOperator:
353 result=MagickMin((
double) pixel,value);
356 case MultiplicativeNoiseEvaluateOperator:
358 result=(double) GenerateDifferentialNoise(random_info,pixel,
359 MultiplicativeGaussianNoise,value);
362 case MultiplyEvaluateOperator:
364 result=(double) pixel*value;
367 case OrEvaluateOperator:
369 result=(double) ((ssize_t) pixel | (ssize_t) (value+0.5));
372 case PoissonNoiseEvaluateOperator:
374 result=(double) GenerateDifferentialNoise(random_info,pixel,PoissonNoise,
378 case PowEvaluateOperator:
380 if (fabs(value) <= MagickEpsilon)
382 if (((
double) pixel < 0.0) && ((value-floor(value)) > MagickEpsilon))
383 result=(double) -((
double) QuantumRange*pow(-(QuantumScale*(
double)
386 result=(double) QuantumRange*pow(QuantumScale*(
double) pixel,value);
389 case RightShiftEvaluateOperator:
391 result=(double) pixel;
392 for (i=0; i < (ssize_t) value; i++)
396 case RootMeanSquareEvaluateOperator:
398 result=((double) pixel*(double) pixel+value);
401 case SetEvaluateOperator:
406 case SineEvaluateOperator:
408 result=(double) QuantumRange*(0.5*sin((
double) (2.0*MagickPI*
409 QuantumScale*(
double) pixel*value))+0.5);
412 case SubtractEvaluateOperator:
414 result=(double) pixel-value;
417 case SumEvaluateOperator:
419 result=(double) pixel+value;
422 case ThresholdEvaluateOperator:
424 result=(double) (((
double) pixel <= value) ? 0 : QuantumRange);
427 case ThresholdBlackEvaluateOperator:
429 result=(double) (((
double) pixel <= value) ? 0 : pixel);
432 case ThresholdWhiteEvaluateOperator:
434 result=(double) (((
double) pixel > value) ? QuantumRange : pixel);
437 case UniformNoiseEvaluateOperator:
439 result=(double) GenerateDifferentialNoise(random_info,pixel,UniformNoise,
443 case XorEvaluateOperator:
445 result=(double) ((ssize_t) pixel ^ (ssize_t) (value+0.5));
452static Image *AcquireImageCanvas(
const Image *images,ExceptionInfo *exception)
463 columns=images->columns;
465 for (p=images; p != (Image *) NULL; p=p->next)
467 if (p->number_channels > q->number_channels)
469 if (p->columns > columns)
474 return(CloneImage(q,columns,rows,MagickTrue,exception));
477MagickExport Image *EvaluateImages(
const Image *images,
478 const MagickEvaluateOperator op,ExceptionInfo *exception)
480#define EvaluateImageTag "Evaluate/Image"
499 **magick_restrict evaluate_pixels;
502 **magick_restrict random_info;
511#if defined(MAGICKCORE_OPENMP_SUPPORT)
516 assert(images != (Image *) NULL);
517 assert(images->signature == MagickCoreSignature);
518 assert(exception != (ExceptionInfo *) NULL);
519 assert(exception->signature == MagickCoreSignature);
520 if (IsEventLogging() != MagickFalse)
521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
522 image=AcquireImageCanvas(images,exception);
523 if (image == (Image *) NULL)
524 return((Image *) NULL);
525 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
527 image=DestroyImage(image);
528 return((Image *) NULL);
530 number_images=GetImageListLength(images);
531 evaluate_pixels=AcquirePixelTLS(images);
532 if (evaluate_pixels == (PixelChannels **) NULL)
534 image=DestroyImage(image);
535 (void) ThrowMagickException(exception,GetMagickModule(),
536 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
537 return((Image *) NULL);
539 image_view=(CacheView **) AcquireQuantumMemory(number_images,
540 sizeof(*image_view));
541 if (image_view == (CacheView **) NULL)
543 image=DestroyImage(image);
544 evaluate_pixels=DestroyPixelTLS(images,evaluate_pixels);
545 (void) ThrowMagickException(exception,GetMagickModule(),
546 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
550 for (n=0; n < (ssize_t) number_images; n++)
552 image_view[n]=AcquireVirtualCacheView(view,exception);
553 view=GetNextImageInList(view);
560 random_info=AcquireRandomInfoTLS();
561 evaluate_view=AcquireAuthenticCacheView(image,exception);
562 if (op == MedianEvaluateOperator)
564#if defined(MAGICKCORE_OPENMP_SUPPORT)
565 key=GetRandomSecretKey(random_info[0]);
566 #pragma omp parallel for schedule(static) shared(progress,status) \
567 magick_number_threads(image,images,image->rows,key == ~0UL)
569 for (y=0; y < (ssize_t) image->rows; y++)
572 id = GetOpenMPThreadId();
589 if (status == MagickFalse)
591 p=(
const Quantum **) AcquireQuantumMemory(number_images,
sizeof(*p));
592 if (p == (
const Quantum **) NULL)
595 (void) ThrowMagickException(exception,GetMagickModule(),
596 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
600 for (j=0; j < (ssize_t) number_images; j++)
602 p[j]=GetCacheViewVirtualPixels(image_view[j],0,y,image->columns,1,
604 if (p[j] == (
const Quantum *) NULL)
607 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
609 if ((j < (ssize_t) number_images) || (q == (Quantum *) NULL))
614 evaluate_pixel=evaluate_pixels[id];
615 for (x=0; x < (ssize_t) image->columns; x++)
624 for (j=0; j < (ssize_t) number_images; j++)
626 for (i=0; i < MaxPixelChannels; i++)
627 evaluate_pixel[j].channel[i]=0.0;
628 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
630 PixelChannel channel = GetPixelChannelChannel(image,i);
631 PixelTrait traits = GetPixelChannelTraits(next,channel);
632 PixelTrait evaluate_traits = GetPixelChannelTraits(image,channel);
633 if (((traits & UpdatePixelTrait) == 0) ||
634 ((evaluate_traits & UpdatePixelTrait) == 0))
636 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(
637 random_info[
id],GetPixelChannel(next,channel,p[j]),op,
638 evaluate_pixel[j].channel[i]);
640 p[j]+=(ptrdiff_t) GetPixelChannels(next);
641 next=GetNextImageInList(next);
643 qsort((
void *) evaluate_pixel,number_images,
sizeof(*evaluate_pixel),
645 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
647 PixelChannel channel = GetPixelChannelChannel(image,i);
648 PixelTrait traits = GetPixelChannelTraits(image,channel);
649 if ((traits & UpdatePixelTrait) == 0)
651 q[i]=ClampToQuantum(evaluate_pixel[number_images/2].channel[i]);
653 q+=(ptrdiff_t) GetPixelChannels(image);
655 p=(
const Quantum **) RelinquishMagickMemory((
void *) p);
656 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
658 if (images->progress_monitor != (MagickProgressMonitor) NULL)
663#if defined(MAGICKCORE_OPENMP_SUPPORT)
667 proceed=SetImageProgress(images,EvaluateImageTag,progress,
669 if (proceed == MagickFalse)
676#if defined(MAGICKCORE_OPENMP_SUPPORT)
677 key=GetRandomSecretKey(random_info[0]);
678 #pragma omp parallel for schedule(static) shared(progress,status) \
679 magick_number_threads(image,images,image->rows,key == ~0UL)
681 for (y=0; y < (ssize_t) image->rows; y++)
687 id = GetOpenMPThreadId();
705 if (status == MagickFalse)
707 p=(
const Quantum **) AcquireQuantumMemory(number_images,
sizeof(*p));
708 if (p == (
const Quantum **) NULL)
711 (void) ThrowMagickException(exception,GetMagickModule(),
712 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
716 for (j=0; j < (ssize_t) number_images; j++)
718 p[j]=GetCacheViewVirtualPixels(image_view[j],0,y,image->columns,1,
720 if (p[j] == (
const Quantum *) NULL)
723 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
725 if ((j < (ssize_t) number_images) || (q == (Quantum *) NULL))
730 evaluate_pixel=evaluate_pixels[id];
731 for (j=0; j < (ssize_t) image->columns; j++)
732 for (i=0; i < MaxPixelChannels; i++)
733 evaluate_pixel[j].channel[i]=0.0;
735 for (j=0; j < (ssize_t) number_images; j++)
737 for (x=0; x < (ssize_t) image->columns; x++)
739 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
741 PixelChannel channel = GetPixelChannelChannel(image,i);
742 PixelTrait traits = GetPixelChannelTraits(next,channel);
743 PixelTrait evaluate_traits = GetPixelChannelTraits(image,channel);
744 if (((traits & UpdatePixelTrait) == 0) ||
745 ((evaluate_traits & UpdatePixelTrait) == 0))
747 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
748 random_info[
id],GetPixelChannel(next,channel,p[j]),j == 0 ?
749 AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
751 p[j]+=(ptrdiff_t) GetPixelChannels(next);
753 next=GetNextImageInList(next);
755 for (x=0; x < (ssize_t) image->columns; x++)
759 case MeanEvaluateOperator:
761 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
762 evaluate_pixel[x].channel[i]/=(
double) number_images;
765 case MultiplyEvaluateOperator:
767 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
769 for (j=0; j < (ssize_t) (number_images-1); j++)
770 evaluate_pixel[x].channel[i]*=QuantumScale;
774 case RootMeanSquareEvaluateOperator:
776 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
777 evaluate_pixel[x].channel[i]=sqrt(evaluate_pixel[x].channel[i]/
785 for (x=0; x < (ssize_t) image->columns; x++)
787 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
789 PixelChannel channel = GetPixelChannelChannel(image,i);
790 PixelTrait traits = GetPixelChannelTraits(image,channel);
791 if ((traits & UpdatePixelTrait) == 0)
793 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
795 q+=(ptrdiff_t) GetPixelChannels(image);
797 p=(
const Quantum **) RelinquishMagickMemory((
void *) p);
798 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
800 if (images->progress_monitor != (MagickProgressMonitor) NULL)
805#if defined(MAGICKCORE_OPENMP_SUPPORT)
809 proceed=SetImageProgress(images,EvaluateImageTag,progress,
811 if (proceed == MagickFalse)
816 for (n=0; n < (ssize_t) number_images; n++)
817 image_view[n]=DestroyCacheView(image_view[n]);
818 image_view=(CacheView **) RelinquishMagickMemory(image_view);
819 evaluate_view=DestroyCacheView(evaluate_view);
820 evaluate_pixels=DestroyPixelTLS(images,evaluate_pixels);
821 random_info=DestroyRandomInfoTLS(random_info);
822 if (status == MagickFalse)
823 image=DestroyImage(image);
827MagickExport MagickBooleanType EvaluateImage(Image *image,
828 const MagickEvaluateOperator op,
const double value,ExceptionInfo *exception)
844 **magick_restrict random_info;
849#if defined(MAGICKCORE_OPENMP_SUPPORT)
854 assert(image != (Image *) NULL);
855 assert(image->signature == MagickCoreSignature);
856 assert(exception != (ExceptionInfo *) NULL);
857 assert(exception->signature == MagickCoreSignature);
858 if (IsEventLogging() != MagickFalse)
859 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
860 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
865 artifact=GetImageArtifact(image,
"evaluate:clamp");
866 if (artifact != (
const char *) NULL)
867 clamp=IsStringTrue(artifact);
868 random_info=AcquireRandomInfoTLS();
869 image_view=AcquireAuthenticCacheView(image,exception);
870#if defined(MAGICKCORE_OPENMP_SUPPORT)
871 key=GetRandomSecretKey(random_info[0]);
872 #pragma omp parallel for schedule(static) shared(progress,status) \
873 magick_number_threads(image,image,image->rows,key == ~0UL)
875 for (y=0; y < (ssize_t) image->rows; y++)
878 id = GetOpenMPThreadId();
886 if (status == MagickFalse)
888 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
889 if (q == (Quantum *) NULL)
894 for (x=0; x < (ssize_t) image->columns; x++)
902 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
904 PixelChannel channel = GetPixelChannelChannel(image,i);
905 PixelTrait traits = GetPixelChannelTraits(image,channel);
906 if ((traits & UpdatePixelTrait) == 0)
908 result=ApplyEvaluateOperator(random_info[
id],q[i],op,value);
909 if (op == MeanEvaluateOperator)
911 q[i]=clamp != MagickFalse ? ClampPixel(result) : ClampToQuantum(result);
913 q+=(ptrdiff_t) GetPixelChannels(image);
915 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
917 if (image->progress_monitor != (MagickProgressMonitor) NULL)
922#if defined(MAGICKCORE_OPENMP_SUPPORT)
926 proceed=SetImageProgress(image,EvaluateImageTag,progress,image->rows);
927 if (proceed == MagickFalse)
931 image_view=DestroyCacheView(image_view);
932 random_info=DestroyRandomInfoTLS(random_info);
970static Quantum ApplyFunction(Quantum pixel,
const MagickFunction function,
971 const size_t number_parameters,
const double *parameters,
972 ExceptionInfo *exception)
984 case PolynomialFunction:
991 for (i=0; i < (ssize_t) number_parameters; i++)
992 result=result*QuantumScale*(
double) pixel+parameters[i];
993 result*=(double) QuantumRange;
996 case SinusoidFunction:
1007 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
1008 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
1009 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
1010 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
1011 result=(double) QuantumRange*(amplitude*sin((
double) (2.0*
1012 MagickPI*(frequency*QuantumScale*(
double) pixel+phase/360.0)))+bias);
1015 case ArcsinFunction:
1027 width=(number_parameters >= 1) ? parameters[0] : 1.0;
1028 center=(number_parameters >= 2) ? parameters[1] : 0.5;
1029 range=(number_parameters >= 3) ? parameters[2] : 1.0;
1030 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
1031 result=2.0*MagickSafeReciprocal(width)*(QuantumScale*(double) pixel-
1034 result=bias-range/2.0;
1037 result=bias+range/2.0;
1039 result=(double) (range/MagickPI*asin((
double) result)+bias);
1040 result*=(double) QuantumRange;
1043 case ArctanFunction:
1054 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
1055 center=(number_parameters >= 2) ? parameters[1] : 0.5;
1056 range=(number_parameters >= 3) ? parameters[2] : 1.0;
1057 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
1058 result=MagickPI*slope*(QuantumScale*(double) pixel-center);
1059 result=(double) QuantumRange*(range/MagickPI*atan((
double) result)+bias);
1062 case UndefinedFunction:
1065 return(ClampToQuantum(result));
1068MagickExport MagickBooleanType FunctionImage(Image *image,
1069 const MagickFunction function,
const size_t number_parameters,
1070 const double *parameters,ExceptionInfo *exception)
1072#define FunctionImageTag "Function/Image "
1086 assert(image != (Image *) NULL);
1087 assert(image->signature == MagickCoreSignature);
1088 assert(exception != (ExceptionInfo *) NULL);
1089 assert(exception->signature == MagickCoreSignature);
1090 if (IsEventLogging() != MagickFalse)
1091 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1092#if defined(MAGICKCORE_OPENCL_SUPPORT)
1093 if (AccelerateFunctionImage(image,function,number_parameters,parameters,
1094 exception) != MagickFalse)
1097 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1098 return(MagickFalse);
1101 image_view=AcquireAuthenticCacheView(image,exception);
1102#if defined(MAGICKCORE_OPENMP_SUPPORT)
1103 #pragma omp parallel for schedule(static) shared(progress,status) \
1104 magick_number_threads(image,image,image->rows,1)
1106 for (y=0; y < (ssize_t) image->rows; y++)
1114 if (status == MagickFalse)
1116 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1117 if (q == (Quantum *) NULL)
1122 for (x=0; x < (ssize_t) image->columns; x++)
1127 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1129 PixelChannel channel = GetPixelChannelChannel(image,i);
1130 PixelTrait traits = GetPixelChannelTraits(image,channel);
1131 if ((traits & UpdatePixelTrait) == 0)
1133 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1136 q+=(ptrdiff_t) GetPixelChannels(image);
1138 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1140 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1145#if defined(MAGICKCORE_OPENMP_SUPPORT)
1149 proceed=SetImageProgress(image,FunctionImageTag,progress,image->rows);
1150 if (proceed == MagickFalse)
1154 image_view=DestroyCacheView(image_view);
1185MagickExport MagickBooleanType GetImageEntropy(
const Image *image,
1186 double *entropy,ExceptionInfo *exception)
1189 *channel_statistics;
1191 assert(image != (Image *) NULL);
1192 assert(image->signature == MagickCoreSignature);
1193 if (IsEventLogging() != MagickFalse)
1194 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1195 channel_statistics=GetImageStatistics(image,exception);
1196 if (channel_statistics == (ChannelStatistics *) NULL)
1199 return(MagickFalse);
1201 *entropy=channel_statistics[CompositePixelChannel].entropy;
1202 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1203 channel_statistics);
1236MagickExport MagickBooleanType GetImageExtrema(
const Image *image,
1237 size_t *minima,
size_t *maxima,ExceptionInfo *exception)
1246 assert(image != (Image *) NULL);
1247 assert(image->signature == MagickCoreSignature);
1248 if (IsEventLogging() != MagickFalse)
1249 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1250 status=GetImageRange(image,&min,&max,exception);
1251 *minima=(size_t) ceil(min-0.5);
1252 *maxima=(size_t) floor(max+0.5);
1286MagickExport MagickBooleanType GetImageKurtosis(
const Image *image,
1287 double *kurtosis,
double *skewness,ExceptionInfo *exception)
1290 *channel_statistics;
1292 assert(image != (Image *) NULL);
1293 assert(image->signature == MagickCoreSignature);
1294 if (IsEventLogging() != MagickFalse)
1295 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1296 channel_statistics=GetImageStatistics(image,exception);
1297 if (channel_statistics == (ChannelStatistics *) NULL)
1301 return(MagickFalse);
1303 *kurtosis=channel_statistics[CompositePixelChannel].kurtosis;
1304 *skewness=channel_statistics[CompositePixelChannel].skewness;
1305 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1306 channel_statistics);
1340MagickExport MagickBooleanType GetImageMean(
const Image *image,
double *mean,
1341 double *standard_deviation,ExceptionInfo *exception)
1344 *channel_statistics;
1346 assert(image != (Image *) NULL);
1347 assert(image->signature == MagickCoreSignature);
1348 if (IsEventLogging() != MagickFalse)
1349 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1350 channel_statistics=GetImageStatistics(image,exception);
1351 if (channel_statistics == (ChannelStatistics *) NULL)
1354 *standard_deviation=NAN;
1355 return(MagickFalse);
1357 *mean=channel_statistics[CompositePixelChannel].mean;
1358 *standard_deviation=
1359 channel_statistics[CompositePixelChannel].standard_deviation;
1360 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1361 channel_statistics);
1392MagickExport MagickBooleanType GetImageMedian(
const Image *image,
double *median,
1393 ExceptionInfo *exception)
1396 *channel_statistics;
1398 assert(image != (Image *) NULL);
1399 assert(image->signature == MagickCoreSignature);
1400 if (IsEventLogging() != MagickFalse)
1401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1402 channel_statistics=GetImageStatistics(image,exception);
1403 if (channel_statistics == (ChannelStatistics *) NULL)
1406 return(MagickFalse);
1408 *median=channel_statistics[CompositePixelChannel].median;
1409 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1410 channel_statistics);
1440MagickExport ChannelMoments *GetImageMoments(
const Image *image,
1441 ExceptionInfo *exception)
1443#define MaxNumberImageMoments 8
1453 M00[2*MaxPixelChannels+1] = { 0.0 },
1454 M01[2*MaxPixelChannels+1] = { 0.0 },
1455 M02[2*MaxPixelChannels+1] = { 0.0 },
1456 M03[2*MaxPixelChannels+1] = { 0.0 },
1457 M10[2*MaxPixelChannels+1] = { 0.0 },
1458 M11[2*MaxPixelChannels+1] = { 0.0 },
1459 M12[2*MaxPixelChannels+1] = { 0.0 },
1460 M20[2*MaxPixelChannels+1] = { 0.0 },
1461 M21[2*MaxPixelChannels+1] = { 0.0 },
1462 M22[2*MaxPixelChannels+1] = { 0.0 },
1463 M30[2*MaxPixelChannels+1] = { 0.0 };
1466 centroid[2*MaxPixelChannels+1] = { 0 };
1472 assert(image != (Image *) NULL);
1473 assert(image->signature == MagickCoreSignature);
1474 if (IsEventLogging() != MagickFalse)
1475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1476 channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1477 sizeof(*channel_moments));
1478 if (channel_moments == (ChannelMoments *) NULL)
1479 return(channel_moments);
1480 (void) memset(channel_moments,0,(MaxPixelChannels+1)*
1481 sizeof(*channel_moments));
1482 image_view=AcquireVirtualCacheView(image,exception);
1483 for (y=0; y < (ssize_t) image->rows; y++)
1494 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1495 if (p == (
const Quantum *) NULL)
1497 for (x=0; x < (ssize_t) image->columns; x++)
1502 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1507 PixelChannel channel = GetPixelChannelChannel(image,i);
1508 PixelTrait traits = GetPixelChannelTraits(image,channel);
1509 if ((traits & UpdatePixelTrait) == 0)
1511 pixel=QuantumScale*p[i];
1512 M00[channel]+=pixel;
1513 M00[CompositePixelChannel]+=pixel;
1514 M10[channel]+=x*pixel;
1515 M10[CompositePixelChannel]+=x*pixel;
1516 M01[channel]+=y*pixel;
1517 M01[CompositePixelChannel]+=y*pixel;
1519 p+=(ptrdiff_t) GetPixelChannels(image);
1522 for (c=0; c <= MaxPixelChannels; c++)
1527 centroid[c].x=M10[c]*MagickSafeReciprocal(M00[c]);
1528 centroid[c].y=M01[c]*MagickSafeReciprocal(M00[c]);
1530 for (y=0; y < (ssize_t) image->rows; y++)
1541 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1542 if (p == (
const Quantum *) NULL)
1544 for (x=0; x < (ssize_t) image->columns; x++)
1549 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1551 PixelChannel channel = GetPixelChannelChannel(image,i);
1552 PixelTrait traits = GetPixelChannelTraits(image,channel);
1553 if ((traits & UpdatePixelTrait) == 0)
1555 M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1556 QuantumScale*(double) p[i];
1557 M11[CompositePixelChannel]+=(x-centroid[channel].x)*(y-
1558 centroid[channel].y)*QuantumScale*(double) p[i];
1559 M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1560 QuantumScale*(double) p[i];
1561 M20[CompositePixelChannel]+=(x-centroid[channel].x)*(x-
1562 centroid[channel].x)*QuantumScale*(double) p[i];
1563 M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1564 QuantumScale*(double) p[i];
1565 M02[CompositePixelChannel]+=(y-centroid[channel].y)*(y-
1566 centroid[channel].y)*QuantumScale*(double) p[i];
1567 M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1568 (y-centroid[channel].y)*QuantumScale*(
double) p[i];
1569 M21[CompositePixelChannel]+=(x-centroid[channel].x)*(x-
1570 centroid[channel].x)*(y-centroid[channel].y)*QuantumScale*(
double)
1572 M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1573 (y-centroid[channel].y)*QuantumScale*(
double) p[i];
1574 M12[CompositePixelChannel]+=(x-centroid[channel].x)*(y-
1575 centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*(
double)
1577 M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1578 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*(double)
1580 M22[CompositePixelChannel]+=(x-centroid[channel].x)*(x-
1581 centroid[channel].x)*(y-centroid[channel].y)*(y-centroid[channel].y)*
1582 QuantumScale*(double) p[i];
1583 M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1584 (x-centroid[channel].x)*QuantumScale*(
double) p[i];
1585 M30[CompositePixelChannel]+=(x-centroid[channel].x)*(x-
1586 centroid[channel].x)*(x-centroid[channel].x)*QuantumScale*(
double)
1588 M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1589 (y-centroid[channel].y)*QuantumScale*(
double) p[i];
1590 M03[CompositePixelChannel]+=(y-centroid[channel].y)*(y-
1591 centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*(
double)
1594 p+=(ptrdiff_t) GetPixelChannels(image);
1597 channels=(double) GetImageChannels(image);
1598 M00[CompositePixelChannel]/=channels;
1599 M01[CompositePixelChannel]/=channels;
1600 M02[CompositePixelChannel]/=channels;
1601 M03[CompositePixelChannel]/=channels;
1602 M10[CompositePixelChannel]/=channels;
1603 M11[CompositePixelChannel]/=channels;
1604 M12[CompositePixelChannel]/=channels;
1605 M20[CompositePixelChannel]/=channels;
1606 M21[CompositePixelChannel]/=channels;
1607 M22[CompositePixelChannel]/=channels;
1608 M30[CompositePixelChannel]/=channels;
1609 for (c=0; c <= MaxPixelChannels; c++)
1614 channel_moments[c].centroid=centroid[c];
1615 channel_moments[c].ellipse_axis.x=sqrt((2.0*MagickSafeReciprocal(M00[c]))*
1616 ((M20[c]+M02[c])+sqrt(4.0*M11[c]*M11[c]+(M20[c]-M02[c])*
1618 channel_moments[c].ellipse_axis.y=sqrt((2.0*MagickSafeReciprocal(M00[c]))*
1619 ((M20[c]+M02[c])-sqrt(4.0*M11[c]*M11[c]+(M20[c]-M02[c])*
1621 channel_moments[c].ellipse_angle=RadiansToDegrees(1.0/2.0*atan(2.0*
1622 M11[c]*MagickSafeReciprocal(M20[c]-M02[c])));
1623 if (fabs(M11[c]) < 0.0)
1625 if ((fabs(M20[c]-M02[c]) >= 0.0) && ((M20[c]-M02[c]) < 0.0))
1626 channel_moments[c].ellipse_angle+=90.0;
1631 if (fabs(M20[c]-M02[c]) >= 0.0)
1633 if ((M20[c]-M02[c]) < 0.0)
1634 channel_moments[c].ellipse_angle+=90.0;
1636 channel_moments[c].ellipse_angle+=180.0;
1640 if ((fabs(M20[c]-M02[c]) >= 0.0) && ((M20[c]-M02[c]) < 0.0))
1641 channel_moments[c].ellipse_angle+=90.0;
1642 channel_moments[c].ellipse_eccentricity=sqrt(1.0-(
1643 channel_moments[c].ellipse_axis.y*
1644 channel_moments[c].ellipse_axis.y*MagickSafeReciprocal(
1645 channel_moments[c].ellipse_axis.x*
1646 channel_moments[c].ellipse_axis.x)));
1647 channel_moments[c].ellipse_intensity=M00[c]*
1648 MagickSafeReciprocal(MagickPI*channel_moments[c].ellipse_axis.x*
1649 channel_moments[c].ellipse_axis.y+MagickEpsilon);
1651 for (c=0; c <= MaxPixelChannels; c++)
1658 M11[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(1.0+1.0)/2.0));
1659 M20[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(2.0+0.0)/2.0));
1660 M02[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(0.0+2.0)/2.0));
1661 M21[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(2.0+1.0)/2.0));
1662 M12[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(1.0+2.0)/2.0));
1663 M22[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(2.0+2.0)/2.0));
1664 M30[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(3.0+0.0)/2.0));
1665 M03[c]*=MagickSafeReciprocal(pow(M00[c],1.0+(0.0+3.0)/2.0));
1668 image_view=DestroyCacheView(image_view);
1669 for (c=0; c <= MaxPixelChannels; c++)
1674 channel_moments[c].invariant[0]=M20[c]+M02[c];
1675 channel_moments[c].invariant[1]=(M20[c]-M02[c])*(M20[c]-M02[c])+4.0*M11[c]*
1677 channel_moments[c].invariant[2]=(M30[c]-3.0*M12[c])*(M30[c]-3.0*M12[c])+
1678 (3.0*M21[c]-M03[c])*(3.0*M21[c]-M03[c]);
1679 channel_moments[c].invariant[3]=(M30[c]+M12[c])*(M30[c]+M12[c])+(M21[c]+
1680 M03[c])*(M21[c]+M03[c]);
1681 channel_moments[c].invariant[4]=(M30[c]-3.0*M12[c])*(M30[c]+M12[c])*
1682 ((M30[c]+M12[c])*(M30[c]+M12[c])-3.0*(M21[c]+M03[c])*(M21[c]+M03[c]))+
1683 (3.0*M21[c]-M03[c])*(M21[c]+M03[c])*(3.0*(M30[c]+M12[c])*(M30[c]+M12[c])-
1684 (M21[c]+M03[c])*(M21[c]+M03[c]));
1685 channel_moments[c].invariant[5]=(M20[c]-M02[c])*((M30[c]+M12[c])*(M30[c]+
1686 M12[c])-(M21[c]+M03[c])*(M21[c]+M03[c]))+4.0*M11[c]*(M30[c]+M12[c])*
1688 channel_moments[c].invariant[6]=(3.0*M21[c]-M03[c])*(M30[c]+M12[c])*
1689 ((M30[c]+M12[c])*(M30[c]+M12[c])-3.0*(M21[c]+M03[c])*(M21[c]+M03[c]))-
1690 (M30[c]-3*M12[c])*(M21[c]+M03[c])*(3.0*(M30[c]+M12[c])*(M30[c]+M12[c])-
1691 (M21[c]+M03[c])*(M21[c]+M03[c]));
1692 channel_moments[c].invariant[7]=M11[c]*((M30[c]+M12[c])*(M30[c]+M12[c])-
1693 (M03[c]+M21[c])*(M03[c]+M21[c]))-(M20[c]-M02[c])*(M30[c]+M12[c])*
1696 if (y < (ssize_t) image->rows)
1697 channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1698 return(channel_moments);
1727MagickExport ChannelPerceptualHash *GetImagePerceptualHash(
const Image *image,
1728 ExceptionInfo *exception)
1730 ChannelPerceptualHash
1747 perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1748 MaxPixelChannels+1UL,
sizeof(*perceptual_hash));
1749 if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1750 return((ChannelPerceptualHash *) NULL);
1751 artifact=GetImageArtifact(image,
"phash:colorspaces");
1752 if (artifact != (
const char *) NULL)
1753 colorspaces=AcquireString(artifact);
1755 colorspaces=AcquireString(
"xyY,HSB");
1756 perceptual_hash[0].number_colorspaces=0;
1757 perceptual_hash[0].number_channels=0;
1759 for (i=0; (p=StringToken(
",",&q)) != (
char *) NULL; i++)
1774 if (i >= MaximumNumberOfPerceptualColorspaces)
1776 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,p);
1779 perceptual_hash[0].colorspace[i]=(ColorspaceType) colorspace;
1780 hash_image=BlurImage(image,0.0,1.0,exception);
1781 if (hash_image == (Image *) NULL)
1783 hash_image->depth=8;
1784 status=TransformImageColorspace(hash_image,(ColorspaceType) colorspace,
1786 if (status == MagickFalse)
1788 moments=GetImageMoments(hash_image,exception);
1789 perceptual_hash[0].number_colorspaces++;
1790 perceptual_hash[0].number_channels+=GetImageChannels(hash_image);
1791 hash_image=DestroyImage(hash_image);
1792 if (moments == (ChannelMoments *) NULL)
1794 for (channel=0; channel <= MaxPixelChannels; channel++)
1795 for (j=0; j < MaximumNumberOfImageMoments; j++)
1796 perceptual_hash[channel].phash[i][j]=
1797 (-MagickSafeLog10(moments[channel].invariant[j]));
1798 moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1800 colorspaces=DestroyString(colorspaces);
1801 return(perceptual_hash);
1833MagickExport MagickBooleanType GetImageRange(
const Image *image,
double *minima,
1834 double *maxima,ExceptionInfo *exception)
1853 range_info = { -MagickMaximumValue, MagickMaximumValue };
1858 assert(image != (Image *) NULL);
1859 assert(image->signature == MagickCoreSignature);
1860 if (IsEventLogging() != MagickFalse)
1861 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1863 image_view=AcquireVirtualCacheView(image,exception);
1864 q=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
1865 if (q != (
const Quantum *) NULL)
1867 range_info.maxima=(double) q[0];
1868 range_info.minima=(double) q[0];
1870#if defined(MAGICKCORE_OPENMP_SUPPORT)
1871 #pragma omp parallel for schedule(static) shared(range_info,status) \
1872 magick_number_threads(image,image,image->rows,1)
1874 for (y=0; y < (ssize_t) image->rows; y++)
1885 if (status == MagickFalse)
1887 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1888 if (p == (
const Quantum *) NULL)
1893 channel_range=range_info;
1894 for (x=0; x < (ssize_t) image->columns; x++)
1899 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1901 PixelChannel channel = GetPixelChannelChannel(image,i);
1902 PixelTrait traits = GetPixelChannelTraits(image,channel);
1903 if ((traits & UpdatePixelTrait) == 0)
1905 if ((
double) p[i] > channel_range.maxima)
1906 channel_range.maxima=(double) p[i];
1907 if ((
double) p[i] < channel_range.minima)
1908 channel_range.minima=(double) p[i];
1910 p+=(ptrdiff_t) GetPixelChannels(image);
1912#if defined(MAGICKCORE_OPENMP_SUPPORT)
1913 #pragma omp critical (MagickCore_GetImageRange)
1916 if (channel_range.maxima > range_info.maxima)
1917 range_info.maxima=channel_range.maxima;
1918 if (channel_range.minima < range_info.minima)
1919 range_info.minima=channel_range.minima;
1922 image_view=DestroyCacheView(image_view);
1923 *maxima=range_info.maxima;
1924 *minima=range_info.minima;
1962static ssize_t GetMedianPixel(Quantum *pixels,
const size_t n)
1964#define SwapPixels(alpha,beta) \
1966 Quantum gamma=(alpha); \
1967 (alpha)=(beta);(beta)=gamma; \
1972 high = (ssize_t) n-1,
1973 median = (low+high)/2;
1984 if (high == (low+1))
1986 if (pixels[low] > pixels[high])
1987 SwapPixels(pixels[low],pixels[high]);
1990 if (pixels[mid] > pixels[high])
1991 SwapPixels(pixels[mid],pixels[high]);
1992 if (pixels[low] > pixels[high])
1993 SwapPixels(pixels[low], pixels[high]);
1994 if (pixels[mid] > pixels[low])
1995 SwapPixels(pixels[mid],pixels[low]);
1996 SwapPixels(pixels[mid],pixels[low+1]);
1999 do l++;
while (pixels[low] > pixels[l]);
2000 do h--;
while (pixels[h] > pixels[low]);
2003 SwapPixels(pixels[l],pixels[h]);
2005 SwapPixels(pixels[low],pixels[h]);
2013static inline long double MagickSafeReciprocalLD(
const long double x)
2021 sign=x < 0.0 ? -1.0 : 1.0;
2022 if ((sign*x) >= MagickEpsilon)
2024 return(sign/MagickEpsilon);
2027MagickExport ChannelStatistics *GetImageStatistics(
const Image *image,
2028 ExceptionInfo *exception)
2031 *channel_statistics;
2059 assert(image != (Image *) NULL);
2060 assert(image->signature == MagickCoreSignature);
2061 if (IsEventLogging() != MagickFalse)
2062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2063 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,
2064 MagickMax(GetPixelChannels(image),1)*
sizeof(*histogram));
2065 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
2066 MaxPixelChannels+1,
sizeof(*channel_statistics));
2067 if ((channel_statistics == (ChannelStatistics *) NULL) ||
2068 (histogram == (
double *) NULL))
2070 if (histogram != (
double *) NULL)
2071 histogram=(
double *) RelinquishMagickMemory(histogram);
2072 if (channel_statistics != (ChannelStatistics *) NULL)
2073 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2074 channel_statistics);
2075 (void) ThrowMagickException(exception,GetMagickModule(),
2076 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
2077 return(channel_statistics);
2079 (void) memset(channel_statistics,0,(MaxPixelChannels+1)*
2080 sizeof(*channel_statistics));
2081 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2083 ChannelStatistics *cs = channel_statistics+i;
2086 cs->maxima=(-MagickMaximumValue);
2087 cs->minima=MagickMaximumValue;
2091 cs->standard_deviation=0.0;
2097 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2098 sizeof(*histogram));
2099 for (y=0; y < (ssize_t) image->rows; y++)
2110 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2111 if (p == (
const Quantum *) NULL)
2113 for (x=0; x < (ssize_t) image->columns; x++)
2115 if (GetPixelReadMask(image,p) <= (QuantumRange/2))
2117 p+=(ptrdiff_t) GetPixelChannels(image);
2120 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2125 PixelChannel channel = GetPixelChannelChannel(image,i);
2126 PixelTrait traits = GetPixelChannelTraits(image,channel);
2127 if ((traits & UpdatePixelTrait) == 0)
2129 cs=channel_statistics+channel;
2130 if (cs->depth != MAGICKCORE_QUANTUM_DEPTH)
2133 range=GetQuantumRange(depth);
2134 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2135 range) ? MagickTrue : MagickFalse;
2136 if (status != MagickFalse)
2139 if (cs->depth > channel_statistics[CompositePixelChannel].depth)
2140 channel_statistics[CompositePixelChannel].depth=cs->depth;
2146 if ((
double) p[i] < cs->minima)
2147 cs->minima=(double) p[i];
2148 if ((
double) p[i] > cs->maxima)
2149 cs->maxima=(double) p[i];
2150 histogram[(ssize_t) GetPixelChannels(image)*ScaleQuantumToMap(
2151 ClampToQuantum((
double) p[i]))+i]++;
2152 cs->sumLD+=(
long double) p[i];
2158 cs->sum_squared+=(double) p[i]*(
double) p[i];
2159 cs->sum_cubed+=(double) p[i]*(
double) p[i]*(double) p[i];
2160 cs->sum_fourth_power+=(double) p[i]*(
double) p[i]*(double) p[i]*
2175 delta=(double) p[i]-cs->M1;
2177 delta_n2=delta_n*delta_n;
2178 term1=delta*delta_n*n1;
2179 cs->M4+=term1*delta_n2*(n*n-3.0*n+3.0)+6.0*delta_n2*cs->M2-4.0*
2181 cs->M3+=term1*delta_n*(n-2.0)-3.0*delta_n*cs->M2;
2186 p+=(ptrdiff_t) GetPixelChannels(image);
2189 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2197 PixelChannel channel = GetPixelChannelChannel(image,i);
2198 PixelTrait traits = GetPixelChannelTraits(image,channel);
2199 if ((traits & UpdatePixelTrait) == 0)
2201 cs=channel_statistics+(ssize_t) channel;
2205 cs->mean=(double) (cs->sumLD/(
long double) cs->area);
2207 adj_area=cs->area/(cs->area-1.0);
2209 cs->sum=(double) cs->sum;
2212 cs->standard_deviation=0.0;
2220 cs->standard_deviation=(double) sqrtl(cs->M2/((
long double)
2223 cs->standard_deviation=(double) sqrtl(cs->M2/((
long double)
2225 cs->variance=cs->standard_deviation*cs->standard_deviation;
2226 cs->skewness=(double) (sqrtl(cs->area)*cs->M3/powl(cs->M2*adj_area,
2228 cs->kurtosis=(double) (cs->area*cs->M4/(cs->M2*cs->M2*adj_area*
2232 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2243 PixelChannel channel = GetPixelChannelChannel(image,i);
2244 PixelTrait traits = GetPixelChannelTraits(image,channel);
2245 if ((traits & UpdatePixelTrait) == 0)
2247 cs=channel_statistics+(ssize_t) channel;
2251 cs->sum_squared/=cs->area;
2252 cs->sum_cubed/=cs->area;
2253 cs->sum_fourth_power/=cs->area;
2259 for (j=0; j <= (ssize_t) MaxMap; j++)
2260 if (histogram[(ssize_t) GetPixelChannels(image)*j+i] > 0.0)
2262 area=MagickSafeReciprocalLD(channel_statistics[channel].area);
2263 number_bins=(double) MagickSafeReciprocalLD((
long double) log2(number_bins));
2264 for (j=0; j <= (ssize_t) MaxMap; j++)
2270 count=(double) (area*histogram[(ssize_t) GetPixelChannels(image)*j+i]);
2271 entropy=-count*log2(count)*number_bins;
2272 if (IsNaN(entropy) != 0)
2274 channel_statistics[channel].entropy+=(double) entropy;
2275 channel_statistics[CompositePixelChannel].entropy+=((double) entropy/
2276 GetPixelChannels(image));
2279 histogram=(
double *) RelinquishMagickMemory(histogram);
2280 median_info=AcquireVirtualMemory(image->columns,image->rows*
sizeof(*median));
2281 if (median_info == (MemoryInfo *) NULL)
2282 (void) ThrowMagickException(exception,GetMagickModule(),
2283 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
2286 median=(Quantum *) GetVirtualMemoryBlob(median_info);
2287 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2295 PixelChannel channel = GetPixelChannelChannel(image,i);
2296 PixelTrait traits = GetPixelChannelTraits(image,channel);
2297 if ((traits & UpdatePixelTrait) == 0)
2299 for (y=0; y < (ssize_t) image->rows; y++)
2307 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2308 if (p == (
const Quantum *) NULL)
2310 for (x=0; x < (ssize_t) image->columns; x++)
2312 if (GetPixelReadMask(image,p) <= (QuantumRange/2))
2314 p+=(ptrdiff_t) GetPixelChannels(image);
2318 p+=(ptrdiff_t) GetPixelChannels(image);
2321 channel_statistics[channel].median=(double) median[
2322 GetMedianPixel(median,n)];
2324 median_info=RelinquishVirtualMemory(median_info);
2327 ChannelStatistics *cs_comp = channel_statistics+CompositePixelChannel;
2329 cs_comp->sum_squared=0.0;
2330 cs_comp->sum_cubed=0.0;
2331 cs_comp->sum_fourth_power=0.0;
2332 cs_comp->maxima=(-MagickMaximumValue);
2333 cs_comp->minima=MagickMaximumValue;
2336 cs_comp->median=0.0;
2337 cs_comp->variance=0.0;
2338 cs_comp->standard_deviation=0.0;
2339 cs_comp->entropy=0.0;
2340 cs_comp->skewness=0.0;
2341 cs_comp->kurtosis=0.0;
2342 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2347 PixelChannel channel = GetPixelChannelChannel(image,i);
2348 PixelTrait traits = GetPixelChannelTraits(image,channel);
2349 if ((traits & UpdatePixelTrait) == 0)
2351 cs=channel_statistics+channel;
2352 if (cs_comp->maxima < cs->maxima)
2353 cs_comp->maxima=cs->maxima;
2354 if (cs_comp->minima > cs->minima)
2355 cs_comp->minima=cs->minima;
2356 cs_comp->sum+=cs->sum;
2357 cs_comp->sum_squared+=cs->sum_squared;
2358 cs_comp->sum_cubed+=cs->sum_cubed;
2359 cs_comp->sum_fourth_power+=cs->sum_fourth_power;
2360 cs_comp->median+=cs->median;
2361 cs_comp->area+=cs->area;
2362 cs_comp->mean+=cs->mean;
2363 cs_comp->variance+=cs->variance;
2364 cs_comp->standard_deviation+=cs->standard_deviation;
2365 cs_comp->skewness+=cs->skewness;
2366 cs_comp->kurtosis+=cs->kurtosis;
2367 cs_comp->entropy+=cs->entropy;
2369 channels=(double) GetImageChannels(image);
2370 cs_comp->sum/=channels;
2371 cs_comp->sum_squared/=channels;
2372 cs_comp->sum_cubed/=channels;
2373 cs_comp->sum_fourth_power/=channels;
2374 cs_comp->median/=channels;
2375 cs_comp->area/=channels;
2376 cs_comp->mean/=channels;
2377 cs_comp->variance/=channels;
2378 cs_comp->standard_deviation/=channels;
2379 cs_comp->skewness/=channels;
2380 cs_comp->kurtosis/=channels;
2381 cs_comp->entropy/=channels;
2383 if (y < (ssize_t) image->rows)
2384 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2385 channel_statistics);
2386 return(channel_statistics);
2422MagickExport Image *PolynomialImage(
const Image *images,
2423 const size_t number_terms,
const double *terms,ExceptionInfo *exception)
2425#define PolynomialImageTag "Polynomial/Image"
2440 **magick_restrict polynomial_pixels;
2448 assert(images != (Image *) NULL);
2449 assert(images->signature == MagickCoreSignature);
2450 if (IsEventLogging() != MagickFalse)
2451 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
2452 assert(exception != (ExceptionInfo *) NULL);
2453 assert(exception->signature == MagickCoreSignature);
2454 image=AcquireImageCanvas(images,exception);
2455 if (image == (Image *) NULL)
2456 return((Image *) NULL);
2457 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2459 image=DestroyImage(image);
2460 return((Image *) NULL);
2462 number_images=GetImageListLength(images);
2463 polynomial_pixels=AcquirePixelTLS(images);
2464 if (polynomial_pixels == (PixelChannels **) NULL)
2466 image=DestroyImage(image);
2467 (void) ThrowMagickException(exception,GetMagickModule(),
2468 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
2469 return((Image *) NULL);
2476 polynomial_view=AcquireAuthenticCacheView(image,exception);
2477#if defined(MAGICKCORE_OPENMP_SUPPORT)
2478 #pragma omp parallel for schedule(static) shared(progress,status) \
2479 magick_number_threads(image,image,image->rows,1)
2481 for (y=0; y < (ssize_t) image->rows; y++)
2490 id = GetOpenMPThreadId();
2503 if (status == MagickFalse)
2505 q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2507 if (q == (Quantum *) NULL)
2512 polynomial_pixel=polynomial_pixels[id];
2513 for (j=0; j < (ssize_t) image->columns; j++)
2514 for (i=0; i < MaxPixelChannels; i++)
2515 polynomial_pixel[j].channel[i]=0.0;
2517 for (j=0; j < (ssize_t) number_images; j++)
2522 if (j >= (ssize_t) number_terms)
2524 image_view=AcquireVirtualCacheView(next,exception);
2525 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2526 if (p == (
const Quantum *) NULL)
2528 image_view=DestroyCacheView(image_view);
2531 for (x=0; x < (ssize_t) image->columns; x++)
2533 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2539 PixelChannel channel = GetPixelChannelChannel(image,i);
2540 PixelTrait traits = GetPixelChannelTraits(next,channel);
2541 PixelTrait polynomial_traits = GetPixelChannelTraits(image,channel);
2542 if (((traits & UpdatePixelTrait) == 0) ||
2543 ((polynomial_traits & UpdatePixelTrait) == 0))
2545 coefficient=(MagickRealType) terms[2*j];
2546 degree=(MagickRealType) terms[(j << 1)+1];
2547 polynomial_pixel[x].channel[i]+=coefficient*
2548 pow(QuantumScale*(
double) GetPixelChannel(image,channel,p),degree);
2550 p+=(ptrdiff_t) GetPixelChannels(next);
2552 image_view=DestroyCacheView(image_view);
2553 next=GetNextImageInList(next);
2555 for (x=0; x < (ssize_t) image->columns; x++)
2557 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2559 PixelChannel channel = GetPixelChannelChannel(image,i);
2560 PixelTrait traits = GetPixelChannelTraits(image,channel);
2561 if ((traits & UpdatePixelTrait) == 0)
2563 q[i]=ClampToQuantum((
double) QuantumRange*
2564 polynomial_pixel[x].channel[i]);
2566 q+=(ptrdiff_t) GetPixelChannels(image);
2568 if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2570 if (images->progress_monitor != (MagickProgressMonitor) NULL)
2575#if defined(MAGICKCORE_OPENMP_SUPPORT)
2579 proceed=SetImageProgress(images,PolynomialImageTag,progress,
2581 if (proceed == MagickFalse)
2585 polynomial_view=DestroyCacheView(polynomial_view);
2586 polynomial_pixels=DestroyPixelTLS(images,polynomial_pixels);
2587 if (status == MagickFalse)
2588 image=DestroyImage(image);
2655static PixelList *DestroyPixelList(PixelList *pixel_list)
2657 if (pixel_list == (PixelList *) NULL)
2658 return((PixelList *) NULL);
2659 if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2660 pixel_list->skip_list.nodes=(SkipNode *) RelinquishAlignedMemory(
2661 pixel_list->skip_list.nodes);
2662 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2666static PixelList **DestroyPixelListTLS(PixelList **pixel_list)
2671 assert(pixel_list != (PixelList **) NULL);
2672 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2673 if (pixel_list[i] != (PixelList *) NULL)
2674 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2675 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2679static PixelList *AcquirePixelList(
const size_t width,
const size_t height)
2684 pixel_list=(PixelList *) AcquireMagickMemory(
sizeof(*pixel_list));
2685 if (pixel_list == (PixelList *) NULL)
2687 (void) memset((
void *) pixel_list,0,
sizeof(*pixel_list));
2688 pixel_list->length=width*height;
2689 pixel_list->skip_list.nodes=(SkipNode *) AcquireAlignedMemory(65537UL,
2690 sizeof(*pixel_list->skip_list.nodes));
2691 if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2692 return(DestroyPixelList(pixel_list));
2693 (void) memset(pixel_list->skip_list.nodes,0,65537UL*
2694 sizeof(*pixel_list->skip_list.nodes));
2695 pixel_list->signature=MagickCoreSignature;
2699static PixelList **AcquirePixelListTLS(
const size_t width,
2700 const size_t height)
2711 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2712 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2713 sizeof(*pixel_list));
2714 if (pixel_list == (PixelList **) NULL)
2715 return((PixelList **) NULL);
2716 (void) memset(pixel_list,0,number_threads*
sizeof(*pixel_list));
2717 for (i=0; i < (ssize_t) number_threads; i++)
2719 pixel_list[i]=AcquirePixelList(width,height);
2720 if (pixel_list[i] == (PixelList *) NULL)
2721 return(DestroyPixelListTLS(pixel_list));
2726static void AddNodePixelList(PixelList *pixel_list,
const size_t color)
2741 p=(&pixel_list->skip_list);
2742 p->nodes[color].signature=pixel_list->signature;
2743 p->nodes[color].count=1;
2748 (void) memset(update,0,
sizeof(update));
2749 for (level=p->level; level >= 0; level--)
2751 while (p->nodes[search].next[level] < color)
2752 search=p->nodes[search].next[level];
2753 update[level]=search;
2758 for (level=0; ; level++)
2760 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2761 if ((pixel_list->seed & 0x300) != 0x300)
2766 if (level > (p->level+2))
2771 while (level > p->level)
2774 update[p->level]=65536UL;
2781 p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2782 p->nodes[update[level]].next[level]=color;
2783 }
while (level-- > 0);
2786static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2800 p=(&pixel_list->skip_list);
2805 color=p->nodes[color].next[0];
2806 count+=(ssize_t) p->nodes[color].count;
2807 }
while (count <= (ssize_t) (pixel_list->length >> 1));
2808 *pixel=ScaleShortToQuantum((
unsigned short) color);
2811static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2827 p=(&pixel_list->skip_list);
2830 max_count=p->nodes[mode].count;
2834 color=p->nodes[color].next[0];
2835 if (p->nodes[color].count > max_count)
2838 max_count=p->nodes[mode].count;
2840 count+=(ssize_t) p->nodes[color].count;
2841 }
while (count < (ssize_t) pixel_list->length);
2842 *pixel=ScaleShortToQuantum((
unsigned short) mode);
2845static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2861 p=(&pixel_list->skip_list);
2863 next=p->nodes[color].next[0];
2869 next=p->nodes[color].next[0];
2870 count+=(ssize_t) p->nodes[color].count;
2871 }
while (count <= (ssize_t) (pixel_list->length >> 1));
2872 if ((previous == 65536UL) && (next != 65536UL))
2875 if ((previous != 65536UL) && (next == 65536UL))
2877 *pixel=ScaleShortToQuantum((
unsigned short) color);
2880static inline void InsertPixelList(
const Quantum pixel,PixelList *pixel_list)
2888 index=ScaleQuantumToShort(pixel);
2889 signature=pixel_list->skip_list.nodes[index].signature;
2890 if (signature == pixel_list->signature)
2892 pixel_list->skip_list.nodes[index].count++;
2895 AddNodePixelList(pixel_list,index);
2898static void ResetPixelList(PixelList *pixel_list)
2912 p=(&pixel_list->skip_list);
2913 root=p->nodes+65536UL;
2915 for (level=0; level < 9; level++)
2916 root->next[level]=65536UL;
2917 pixel_list->seed=pixel_list->signature++;
2920MagickExport Image *StatisticImage(
const Image *image,
const StatisticType type,
2921 const size_t width,
const size_t height,ExceptionInfo *exception)
2923#define StatisticImageTag "Statistic/Image"
2939 **magick_restrict pixel_list;
2948 assert(image != (Image *) NULL);
2949 assert(image->signature == MagickCoreSignature);
2950 if (IsEventLogging() != MagickFalse)
2951 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2952 assert(exception != (ExceptionInfo *) NULL);
2953 assert(exception->signature == MagickCoreSignature);
2954 statistic_image=CloneImage(image,0,0,MagickTrue,
2956 if (statistic_image == (Image *) NULL)
2957 return((Image *) NULL);
2958 status=SetImageStorageClass(statistic_image,DirectClass,exception);
2959 if (status == MagickFalse)
2961 statistic_image=DestroyImage(statistic_image);
2962 return((Image *) NULL);
2964 pixel_list=AcquirePixelListTLS(MagickMax(width,1),MagickMax(height,1));
2965 if (pixel_list == (PixelList **) NULL)
2967 statistic_image=DestroyImage(statistic_image);
2968 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
2973 center=(ssize_t) GetPixelChannels(image)*((ssize_t) image->columns+
2974 MagickMax((ssize_t) width,1L))*(MagickMax((ssize_t) height,1)/2L)+(ssize_t)
2975 GetPixelChannels(image)*(MagickMax((ssize_t) width,1L)/2L);
2978 image_view=AcquireVirtualCacheView(image,exception);
2979 statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
2980#if defined(MAGICKCORE_OPENMP_SUPPORT)
2981 #pragma omp parallel for schedule(static) shared(progress,status) \
2982 magick_number_threads(image,statistic_image,statistic_image->rows,1)
2984 for (y=0; y < (ssize_t) statistic_image->rows; y++)
2987 id = GetOpenMPThreadId();
2998 if (status == MagickFalse)
3000 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
3001 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
3002 MagickMax(height,1),exception);
3003 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
3004 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3009 for (x=0; x < (ssize_t) statistic_image->columns; x++)
3014 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3027 *magick_restrict pixels;
3035 PixelChannel channel = GetPixelChannelChannel(image,i);
3036 PixelTrait traits = GetPixelChannelTraits(image,channel);
3037 PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
3039 if (((traits & UpdatePixelTrait) == 0) ||
3040 ((statistic_traits & UpdatePixelTrait) == 0))
3042 if (((statistic_traits & CopyPixelTrait) != 0) ||
3043 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
3045 SetPixelChannel(statistic_image,channel,p[center+i],q);
3054 ResetPixelList(pixel_list[
id]);
3055 for (v=0; v < (ssize_t) MagickMax(height,1); v++)
3057 for (u=0; u < (ssize_t) MagickMax(width,1); u++)
3059 if ((type == MedianStatistic) || (type == ModeStatistic) ||
3060 (type == NonpeakStatistic))
3062 InsertPixelList(pixels[i],pixel_list[
id]);
3063 pixels+=(ptrdiff_t) GetPixelChannels(image);
3067 if ((
double) pixels[i] < minimum)
3068 minimum=(double) pixels[i];
3069 if ((
double) pixels[i] > maximum)
3070 maximum=(double) pixels[i];
3071 sum+=(double) pixels[i];
3072 sum_squared+=(double) pixels[i]*(
double) pixels[i];
3073 pixels+=(ptrdiff_t) GetPixelChannels(image);
3075 pixels+=(ptrdiff_t) GetPixelChannels(image)*image->columns;
3079 case ContrastStatistic:
3081 pixel=ClampToQuantum(MagickAbsoluteValue((maximum-minimum)*
3082 MagickSafeReciprocal(maximum+minimum)));
3085 case GradientStatistic:
3087 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
3090 case MaximumStatistic:
3092 pixel=ClampToQuantum(maximum);
3098 pixel=ClampToQuantum(sum/area);
3101 case MedianStatistic:
3103 GetMedianPixelList(pixel_list[
id],&pixel);
3106 case MinimumStatistic:
3108 pixel=ClampToQuantum(minimum);
3113 GetModePixelList(pixel_list[
id],&pixel);
3116 case NonpeakStatistic:
3118 GetNonpeakPixelList(pixel_list[
id],&pixel);
3121 case RootMeanSquareStatistic:
3123 pixel=ClampToQuantum(sqrt(sum_squared/area));
3126 case StandardDeviationStatistic:
3128 pixel=ClampToQuantum(sqrt(sum_squared/area-(sum/area*sum/area)));
3132 SetPixelChannel(statistic_image,channel,pixel,q);
3134 p+=(ptrdiff_t) GetPixelChannels(image);
3135 q+=(ptrdiff_t) GetPixelChannels(statistic_image);
3137 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3139 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3144#if defined(MAGICKCORE_OPENMP_SUPPORT)
3148 proceed=SetImageProgress(image,StatisticImageTag,progress,image->rows);
3149 if (proceed == MagickFalse)
3153 statistic_view=DestroyCacheView(statistic_view);
3154 image_view=DestroyCacheView(image_view);
3155 pixel_list=DestroyPixelListTLS(pixel_list);
3156 if (status == MagickFalse)
3157 statistic_image=DestroyImage(statistic_image);
3158 return(statistic_image);