MagickCore  6.7.5
feature.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %               FFFFF  EEEEE   AAA   TTTTT  U   U  RRRR   EEEEE               %
00007 %               F      E      A   A    T    U   U  R   R  E                   %
00008 %               FFF    EEE    AAAAA    T    U   U  RRRR   EEE                 %
00009 %               F      E      A   A    T    U   U  R R    E                   %
00010 %               F      EEEEE  A   A    T     UUU   R  R   EEEEE               %
00011 %                                                                             %
00012 %                                                                             %
00013 %                      MagickCore Image Feature Methods                       %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                                 July 1992                                   %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "MagickCore/studio.h"
00044 #include "MagickCore/property.h"
00045 #include "MagickCore/animate.h"
00046 #include "MagickCore/blob.h"
00047 #include "MagickCore/blob-private.h"
00048 #include "MagickCore/cache.h"
00049 #include "MagickCore/cache-private.h"
00050 #include "MagickCore/cache-view.h"
00051 #include "MagickCore/client.h"
00052 #include "MagickCore/color.h"
00053 #include "MagickCore/color-private.h"
00054 #include "MagickCore/colorspace.h"
00055 #include "MagickCore/colorspace-private.h"
00056 #include "MagickCore/composite.h"
00057 #include "MagickCore/composite-private.h"
00058 #include "MagickCore/compress.h"
00059 #include "MagickCore/constitute.h"
00060 #include "MagickCore/display.h"
00061 #include "MagickCore/draw.h"
00062 #include "MagickCore/enhance.h"
00063 #include "MagickCore/exception.h"
00064 #include "MagickCore/exception-private.h"
00065 #include "MagickCore/feature.h"
00066 #include "MagickCore/gem.h"
00067 #include "MagickCore/geometry.h"
00068 #include "MagickCore/list.h"
00069 #include "MagickCore/image-private.h"
00070 #include "MagickCore/magic.h"
00071 #include "MagickCore/magick.h"
00072 #include "MagickCore/memory_.h"
00073 #include "MagickCore/module.h"
00074 #include "MagickCore/monitor.h"
00075 #include "MagickCore/monitor-private.h"
00076 #include "MagickCore/option.h"
00077 #include "MagickCore/paint.h"
00078 #include "MagickCore/pixel-accessor.h"
00079 #include "MagickCore/profile.h"
00080 #include "MagickCore/quantize.h"
00081 #include "MagickCore/quantum-private.h"
00082 #include "MagickCore/random_.h"
00083 #include "MagickCore/segment.h"
00084 #include "MagickCore/semaphore.h"
00085 #include "MagickCore/signature-private.h"
00086 #include "MagickCore/string_.h"
00087 #include "MagickCore/thread-private.h"
00088 #include "MagickCore/timer.h"
00089 #include "MagickCore/utility.h"
00090 #include "MagickCore/version.h"
00091 
00092 /*
00093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00094 %                                                                             %
00095 %                                                                             %
00096 %                                                                             %
00097 %   G e t I m a g e F e a t u r e s                                           %
00098 %                                                                             %
00099 %                                                                             %
00100 %                                                                             %
00101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00102 %
00103 %  GetImageFeatures() returns features for each channel in the image in
00104 %  each of four directions (horizontal, vertical, left and right diagonals)
00105 %  for the specified distance.  The features include the angular second
00106 %  moment, contrast, correlation, sum of squares: variance, inverse difference
00107 %  moment, sum average, sum varience, sum entropy, entropy, difference variance,%  difference entropy, information measures of correlation 1, information
00108 %  measures of correlation 2, and maximum correlation coefficient.  You can
00109 %  access the red channel contrast, for example, like this:
00110 %
00111 %      channel_features=GetImageFeatures(image,1,exception);
00112 %      contrast=channel_features[RedPixelChannel].contrast[0];
00113 %
00114 %  Use MagickRelinquishMemory() to free the features buffer.
00115 %
00116 %  The format of the GetImageFeatures method is:
00117 %
00118 %      ChannelFeatures *GetImageFeatures(const Image *image,
00119 %        const size_t distance,ExceptionInfo *exception)
00120 %
00121 %  A description of each parameter follows:
00122 %
00123 %    o image: the image.
00124 %
00125 %    o distance: the distance.
00126 %
00127 %    o exception: return any errors or warnings in this structure.
00128 %
00129 */
00130 
00131 static inline ssize_t MagickAbsoluteValue(const ssize_t x)
00132 {
00133   if (x < 0)
00134     return(-x);
00135   return(x);
00136 }
00137 
00138 MagickExport ChannelFeatures *GetImageFeatures(const Image *image,
00139   const size_t distance,ExceptionInfo *exception)
00140 {
00141   typedef struct _ChannelStatistics
00142   {
00143     PixelInfo
00144       direction[4];  /* horizontal, vertical, left and right diagonals */
00145   } ChannelStatistics;
00146 
00147   CacheView
00148     *image_view;
00149 
00150   ChannelFeatures
00151     *channel_features;
00152 
00153   ChannelStatistics
00154     **cooccurrence,
00155     correlation,
00156     *density_x,
00157     *density_xy,
00158     *density_y,
00159     entropy_x,
00160     entropy_xy,
00161     entropy_xy1,
00162     entropy_xy2,
00163     entropy_y,
00164     mean,
00165     **Q,
00166     *sum,
00167     sum_squares,
00168     variance;
00169 
00170   PixelPacket
00171     gray,
00172     *grays;
00173 
00174   MagickBooleanType
00175     status;
00176 
00177   register ssize_t
00178     i;
00179 
00180   size_t
00181     length;
00182 
00183   ssize_t
00184     y;
00185 
00186   unsigned int
00187     number_grays;
00188 
00189   assert(image != (Image *) NULL);
00190   assert(image->signature == MagickSignature);
00191   if (image->debug != MagickFalse)
00192     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00193   if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
00194     return((ChannelFeatures *) NULL);
00195   length=CompositeChannels+1UL;
00196   channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
00197     sizeof(*channel_features));
00198   if (channel_features == (ChannelFeatures *) NULL)
00199     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00200   (void) ResetMagickMemory(channel_features,0,length*
00201     sizeof(*channel_features));
00202   /*
00203     Form grays.
00204   */
00205   grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
00206   if (grays == (PixelPacket *) NULL)
00207     {
00208       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
00209         channel_features);
00210       (void) ThrowMagickException(exception,GetMagickModule(),
00211         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00212       return(channel_features);
00213     }
00214   for (i=0; i <= (ssize_t) MaxMap; i++)
00215   {
00216     grays[i].red=(~0U);
00217     grays[i].green=(~0U);
00218     grays[i].blue=(~0U);
00219     grays[i].alpha=(~0U);
00220     grays[i].black=(~0U);
00221   }
00222   status=MagickTrue;
00223   image_view=AcquireCacheView(image);
00224 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00225   #pragma omp parallel for schedule(static,4) shared(status)
00226 #endif
00227   for (y=0; y < (ssize_t) image->rows; y++)
00228   {
00229     register const Quantum
00230       *restrict p;
00231 
00232     register ssize_t
00233       x;
00234 
00235     if (status == MagickFalse)
00236       continue;
00237     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00238     if (p == (const Quantum *) NULL)
00239       {
00240         status=MagickFalse;
00241         continue;
00242       }
00243     for (x=0; x < (ssize_t) image->columns; x++)
00244     {
00245       grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
00246         ScaleQuantumToMap(GetPixelRed(image,p));
00247       grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
00248         ScaleQuantumToMap(GetPixelGreen(image,p));
00249       grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
00250         ScaleQuantumToMap(GetPixelBlue(image,p));
00251       if (image->colorspace == CMYKColorspace)
00252         grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
00253           ScaleQuantumToMap(GetPixelBlack(image,p));
00254       if (image->matte != MagickFalse)
00255         grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
00256           ScaleQuantumToMap(GetPixelAlpha(image,p));
00257       p+=GetPixelChannels(image);
00258     }
00259   }
00260   image_view=DestroyCacheView(image_view);
00261   if (status == MagickFalse)
00262     {
00263       grays=(PixelPacket *) RelinquishMagickMemory(grays);
00264       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
00265         channel_features);
00266       return(channel_features);
00267     }
00268   (void) ResetMagickMemory(&gray,0,sizeof(gray));
00269   for (i=0; i <= (ssize_t) MaxMap; i++)
00270   {
00271     if (grays[i].red != ~0U)
00272       grays[gray.red++].red=grays[i].red;
00273     if (grays[i].green != ~0U)
00274       grays[gray.green++].green=grays[i].green;
00275     if (grays[i].blue != ~0U)
00276       grays[gray.blue++].blue=grays[i].blue;
00277     if (image->colorspace == CMYKColorspace)
00278       if (grays[i].black != ~0U)
00279         grays[gray.black++].black=grays[i].black;
00280     if (image->matte != MagickFalse)
00281       if (grays[i].alpha != ~0U)
00282         grays[gray.alpha++].alpha=grays[i].alpha;
00283   }
00284   /*
00285     Allocate spatial dependence matrix.
00286   */
00287   number_grays=gray.red;
00288   if (gray.green > number_grays)
00289     number_grays=gray.green;
00290   if (gray.blue > number_grays)
00291     number_grays=gray.blue;
00292   if (image->colorspace == CMYKColorspace)
00293     if (gray.black > number_grays)
00294       number_grays=gray.black;
00295   if (image->matte != MagickFalse)
00296     if (gray.alpha > number_grays)
00297       number_grays=gray.alpha;
00298   cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
00299     sizeof(*cooccurrence));
00300   density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
00301     sizeof(*density_x));
00302   density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
00303     sizeof(*density_xy));
00304   density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
00305     sizeof(*density_y));
00306   Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
00307   sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
00308   if ((cooccurrence == (ChannelStatistics **) NULL) ||
00309       (density_x == (ChannelStatistics *) NULL) ||
00310       (density_xy == (ChannelStatistics *) NULL) ||
00311       (density_y == (ChannelStatistics *) NULL) ||
00312       (Q == (ChannelStatistics **) NULL) ||
00313       (sum == (ChannelStatistics *) NULL))
00314     {
00315       if (Q != (ChannelStatistics **) NULL)
00316         {
00317           for (i=0; i < (ssize_t) number_grays; i++)
00318             Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
00319           Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
00320         }
00321       if (sum != (ChannelStatistics *) NULL)
00322         sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
00323       if (density_y != (ChannelStatistics *) NULL)
00324         density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
00325       if (density_xy != (ChannelStatistics *) NULL)
00326         density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
00327       if (density_x != (ChannelStatistics *) NULL)
00328         density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
00329       if (cooccurrence != (ChannelStatistics **) NULL)
00330         {
00331           for (i=0; i < (ssize_t) number_grays; i++)
00332             cooccurrence[i]=(ChannelStatistics *)
00333               RelinquishMagickMemory(cooccurrence[i]);
00334           cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
00335             cooccurrence);
00336         }
00337       grays=(PixelPacket *) RelinquishMagickMemory(grays);
00338       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
00339         channel_features);
00340       (void) ThrowMagickException(exception,GetMagickModule(),
00341         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00342       return(channel_features);
00343     }
00344   (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
00345   (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
00346   (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
00347   (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
00348   (void) ResetMagickMemory(&mean,0,sizeof(mean));
00349   (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
00350   (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
00351   (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
00352   (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
00353   (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
00354   (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
00355   (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
00356   (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
00357   (void) ResetMagickMemory(&variance,0,sizeof(variance));
00358   for (i=0; i < (ssize_t) number_grays; i++)
00359   {
00360     cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
00361       sizeof(**cooccurrence));
00362     Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
00363     if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
00364         (Q[i] == (ChannelStatistics *) NULL))
00365       break;
00366     (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
00367       sizeof(**cooccurrence));
00368     (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
00369   }
00370   if (i < (ssize_t) number_grays)
00371     {
00372       for (i--; i >= 0; i--)
00373       {
00374         if (Q[i] != (ChannelStatistics *) NULL)
00375           Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
00376         if (cooccurrence[i] != (ChannelStatistics *) NULL)
00377           cooccurrence[i]=(ChannelStatistics *)
00378             RelinquishMagickMemory(cooccurrence[i]);
00379       }
00380       Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
00381       cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
00382       sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
00383       density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
00384       density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
00385       density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
00386       grays=(PixelPacket *) RelinquishMagickMemory(grays);
00387       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
00388         channel_features);
00389       (void) ThrowMagickException(exception,GetMagickModule(),
00390         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00391       return(channel_features);
00392     }
00393   /*
00394     Initialize spatial dependence matrix.
00395   */
00396   status=MagickTrue;
00397   image_view=AcquireCacheView(image);
00398   for (y=0; y < (ssize_t) image->rows; y++)
00399   {
00400     register const Quantum
00401       *restrict p;
00402 
00403     register ssize_t
00404       x;
00405 
00406     ssize_t
00407       i,
00408       offset,
00409       u,
00410       v;
00411 
00412     if (status == MagickFalse)
00413       continue;
00414     p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,y,image->columns+
00415       2*distance,distance+2,exception);
00416     if (p == (const Quantum *) NULL)
00417       {
00418         status=MagickFalse;
00419         continue;
00420       }
00421     p+=distance*GetPixelChannels(image);;
00422     for (x=0; x < (ssize_t) image->columns; x++)
00423     {
00424       for (i=0; i < 4; i++)
00425       {
00426         switch (i)
00427         {
00428           case 0:
00429           default:
00430           {
00431             /*
00432               Horizontal adjacency.
00433             */
00434             offset=(ssize_t) distance;
00435             break;
00436           }
00437           case 1:
00438           {
00439             /*
00440               Vertical adjacency.
00441             */
00442             offset=(ssize_t) (image->columns+2*distance);
00443             break;
00444           }
00445           case 2:
00446           {
00447             /*
00448               Right diagonal adjacency.
00449             */
00450             offset=(ssize_t) ((image->columns+2*distance)-distance);
00451             break;
00452           }
00453           case 3:
00454           {
00455             /*
00456               Left diagonal adjacency.
00457             */
00458             offset=(ssize_t) ((image->columns+2*distance)+distance);
00459             break;
00460           }
00461         }
00462         u=0;
00463         v=0;
00464         while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p)))
00465           u++;
00466         while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image))))
00467           v++;
00468         cooccurrence[u][v].direction[i].red++;
00469         cooccurrence[v][u].direction[i].red++;
00470         u=0;
00471         v=0;
00472         while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p)))
00473           u++;
00474         while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image))))
00475           v++;
00476         cooccurrence[u][v].direction[i].green++;
00477         cooccurrence[v][u].direction[i].green++;
00478         u=0;
00479         v=0;
00480         while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p)))
00481           u++;
00482         while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image))))
00483           v++;
00484         cooccurrence[u][v].direction[i].blue++;
00485         cooccurrence[v][u].direction[i].blue++;
00486         if (image->colorspace == CMYKColorspace)
00487           {
00488             u=0;
00489             v=0;
00490             while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p)))
00491               u++;
00492             while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image))))
00493               v++;
00494             cooccurrence[u][v].direction[i].black++;
00495             cooccurrence[v][u].direction[i].black++;
00496           }
00497         if (image->matte != MagickFalse)
00498           {
00499             u=0;
00500             v=0;
00501             while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p)))
00502               u++;
00503             while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image))))
00504               v++;
00505             cooccurrence[u][v].direction[i].alpha++;
00506             cooccurrence[v][u].direction[i].alpha++;
00507           }
00508       }
00509       p+=GetPixelChannels(image);
00510     }
00511   }
00512   grays=(PixelPacket *) RelinquishMagickMemory(grays);
00513   image_view=DestroyCacheView(image_view);
00514   if (status == MagickFalse)
00515     {
00516       for (i=0; i < (ssize_t) number_grays; i++)
00517         cooccurrence[i]=(ChannelStatistics *)
00518           RelinquishMagickMemory(cooccurrence[i]);
00519       cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
00520       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
00521         channel_features);
00522       (void) ThrowMagickException(exception,GetMagickModule(),
00523         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00524       return(channel_features);
00525     }
00526   /*
00527     Normalize spatial dependence matrix.
00528   */
00529   for (i=0; i < 4; i++)
00530   {
00531     double
00532       normalize;
00533 
00534     register ssize_t
00535       y;
00536 
00537     switch (i)
00538     {
00539       case 0:
00540       default:
00541       {
00542         /*
00543           Horizontal adjacency.
00544         */
00545         normalize=2.0*image->rows*(image->columns-distance);
00546         break;
00547       }
00548       case 1:
00549       {
00550         /*
00551           Vertical adjacency.
00552         */
00553         normalize=2.0*(image->rows-distance)*image->columns;
00554         break;
00555       }
00556       case 2:
00557       {
00558         /*
00559           Right diagonal adjacency.
00560         */
00561         normalize=2.0*(image->rows-distance)*(image->columns-distance);
00562         break;
00563       }
00564       case 3:
00565       {
00566         /*
00567           Left diagonal adjacency.
00568         */
00569         normalize=2.0*(image->rows-distance)*(image->columns-distance);
00570         break;
00571       }
00572     }
00573     normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 : normalize);
00574     for (y=0; y < (ssize_t) number_grays; y++)
00575     {
00576       register ssize_t
00577         x;
00578 
00579       for (x=0; x < (ssize_t) number_grays; x++)
00580       {
00581         cooccurrence[x][y].direction[i].red*=normalize;
00582         cooccurrence[x][y].direction[i].green*=normalize;
00583         cooccurrence[x][y].direction[i].blue*=normalize;
00584         if (image->colorspace == CMYKColorspace)
00585           cooccurrence[x][y].direction[i].black*=normalize;
00586         if (image->matte != MagickFalse)
00587           cooccurrence[x][y].direction[i].alpha*=normalize;
00588       }
00589     }
00590   }
00591   /*
00592     Compute texture features.
00593   */
00594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00595   #pragma omp parallel for schedule(static,4) shared(status)
00596 #endif
00597   for (i=0; i < 4; i++)
00598   {
00599     register ssize_t
00600       y;
00601 
00602     for (y=0; y < (ssize_t) number_grays; y++)
00603     {
00604       register ssize_t
00605         x;
00606 
00607       for (x=0; x < (ssize_t) number_grays; x++)
00608       {
00609         /*
00610           Angular second moment:  measure of homogeneity of the image.
00611         */
00612         channel_features[RedPixelChannel].angular_second_moment[i]+=
00613           cooccurrence[x][y].direction[i].red*
00614           cooccurrence[x][y].direction[i].red;
00615         channel_features[GreenPixelChannel].angular_second_moment[i]+=
00616           cooccurrence[x][y].direction[i].green*
00617           cooccurrence[x][y].direction[i].green;
00618         channel_features[BluePixelChannel].angular_second_moment[i]+=
00619           cooccurrence[x][y].direction[i].blue*
00620           cooccurrence[x][y].direction[i].blue;
00621         if (image->colorspace == CMYKColorspace)
00622           channel_features[BlackPixelChannel].angular_second_moment[i]+=
00623             cooccurrence[x][y].direction[i].black*
00624             cooccurrence[x][y].direction[i].black;
00625         if (image->matte != MagickFalse)
00626           channel_features[AlphaPixelChannel].angular_second_moment[i]+=
00627             cooccurrence[x][y].direction[i].alpha*
00628             cooccurrence[x][y].direction[i].alpha;
00629         /*
00630           Correlation: measure of linear-dependencies in the image.
00631         */
00632         sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
00633         sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
00634         sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
00635         if (image->colorspace == CMYKColorspace)
00636           sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black;
00637         if (image->matte != MagickFalse)
00638           sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha;
00639         correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
00640         correlation.direction[i].green+=x*y*
00641           cooccurrence[x][y].direction[i].green;
00642         correlation.direction[i].blue+=x*y*
00643           cooccurrence[x][y].direction[i].blue;
00644         if (image->colorspace == CMYKColorspace)
00645           correlation.direction[i].black+=x*y*
00646             cooccurrence[x][y].direction[i].black;
00647         if (image->matte != MagickFalse)
00648           correlation.direction[i].alpha+=x*y*
00649             cooccurrence[x][y].direction[i].alpha;
00650         /*
00651           Inverse Difference Moment.
00652         */
00653         channel_features[RedPixelChannel].inverse_difference_moment[i]+=
00654           cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
00655         channel_features[GreenPixelChannel].inverse_difference_moment[i]+=
00656           cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
00657         channel_features[BluePixelChannel].inverse_difference_moment[i]+=
00658           cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
00659         if (image->colorspace == CMYKColorspace)
00660           channel_features[BlackPixelChannel].inverse_difference_moment[i]+=
00661             cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1);
00662         if (image->matte != MagickFalse)
00663           channel_features[AlphaPixelChannel].inverse_difference_moment[i]+=
00664             cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1);
00665         /*
00666           Sum average.
00667         */
00668         density_xy[y+x+2].direction[i].red+=
00669           cooccurrence[x][y].direction[i].red;
00670         density_xy[y+x+2].direction[i].green+=
00671           cooccurrence[x][y].direction[i].green;
00672         density_xy[y+x+2].direction[i].blue+=
00673           cooccurrence[x][y].direction[i].blue;
00674         if (image->colorspace == CMYKColorspace)
00675           density_xy[y+x+2].direction[i].black+=
00676             cooccurrence[x][y].direction[i].black;
00677         if (image->matte != MagickFalse)
00678           density_xy[y+x+2].direction[i].alpha+=
00679             cooccurrence[x][y].direction[i].alpha;
00680         /*
00681           Entropy.
00682         */
00683         channel_features[RedPixelChannel].entropy[i]-=
00684           cooccurrence[x][y].direction[i].red*
00685           log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
00686         channel_features[GreenPixelChannel].entropy[i]-=
00687           cooccurrence[x][y].direction[i].green*
00688           log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
00689         channel_features[BluePixelChannel].entropy[i]-=
00690           cooccurrence[x][y].direction[i].blue*
00691           log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
00692         if (image->colorspace == CMYKColorspace)
00693           channel_features[BlackPixelChannel].entropy[i]-=
00694             cooccurrence[x][y].direction[i].black*
00695             log10(cooccurrence[x][y].direction[i].black+MagickEpsilon);
00696         if (image->matte != MagickFalse)
00697           channel_features[AlphaPixelChannel].entropy[i]-=
00698             cooccurrence[x][y].direction[i].alpha*
00699             log10(cooccurrence[x][y].direction[i].alpha+MagickEpsilon);
00700         /*
00701           Information Measures of Correlation.
00702         */
00703         density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
00704         density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
00705         density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
00706         if (image->matte != MagickFalse)
00707           density_x[x].direction[i].alpha+=
00708             cooccurrence[x][y].direction[i].alpha;
00709         if (image->colorspace == CMYKColorspace)
00710           density_x[x].direction[i].black+=
00711             cooccurrence[x][y].direction[i].black;
00712         density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
00713         density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
00714         density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
00715         if (image->colorspace == CMYKColorspace)
00716           density_y[y].direction[i].black+=
00717             cooccurrence[x][y].direction[i].black;
00718         if (image->matte != MagickFalse)
00719           density_y[y].direction[i].alpha+=
00720             cooccurrence[x][y].direction[i].alpha;
00721       }
00722       mean.direction[i].red+=y*sum[y].direction[i].red;
00723       sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
00724       mean.direction[i].green+=y*sum[y].direction[i].green;
00725       sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
00726       mean.direction[i].blue+=y*sum[y].direction[i].blue;
00727       sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
00728       if (image->colorspace == CMYKColorspace)
00729         {
00730           mean.direction[i].black+=y*sum[y].direction[i].black;
00731           sum_squares.direction[i].black+=y*y*sum[y].direction[i].black;
00732         }
00733       if (image->matte != MagickFalse)
00734         {
00735           mean.direction[i].alpha+=y*sum[y].direction[i].alpha;
00736           sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha;
00737         }
00738     }
00739     /*
00740       Correlation: measure of linear-dependencies in the image.
00741     */
00742     channel_features[RedPixelChannel].correlation[i]=
00743       (correlation.direction[i].red-mean.direction[i].red*
00744       mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
00745       (mean.direction[i].red*mean.direction[i].red))*sqrt(
00746       sum_squares.direction[i].red-(mean.direction[i].red*
00747       mean.direction[i].red)));
00748     channel_features[GreenPixelChannel].correlation[i]=
00749       (correlation.direction[i].green-mean.direction[i].green*
00750       mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
00751       (mean.direction[i].green*mean.direction[i].green))*sqrt(
00752       sum_squares.direction[i].green-(mean.direction[i].green*
00753       mean.direction[i].green)));
00754     channel_features[BluePixelChannel].correlation[i]=
00755       (correlation.direction[i].blue-mean.direction[i].blue*
00756       mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
00757       (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
00758       sum_squares.direction[i].blue-(mean.direction[i].blue*
00759       mean.direction[i].blue)));
00760     if (image->colorspace == CMYKColorspace)
00761       channel_features[BlackPixelChannel].correlation[i]=
00762         (correlation.direction[i].black-mean.direction[i].black*
00763         mean.direction[i].black)/(sqrt(sum_squares.direction[i].black-
00764         (mean.direction[i].black*mean.direction[i].black))*sqrt(
00765         sum_squares.direction[i].black-(mean.direction[i].black*
00766         mean.direction[i].black)));
00767     if (image->matte != MagickFalse)
00768       channel_features[AlphaPixelChannel].correlation[i]=
00769         (correlation.direction[i].alpha-mean.direction[i].alpha*
00770         mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha-
00771         (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt(
00772         sum_squares.direction[i].alpha-(mean.direction[i].alpha*
00773         mean.direction[i].alpha)));
00774   }
00775   /*
00776     Compute more texture features.
00777   */
00778 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00779   #pragma omp parallel for schedule(static,4) shared(status)
00780 #endif
00781   for (i=0; i < 4; i++)
00782   {
00783     register ssize_t
00784       x;
00785 
00786     for (x=2; x < (ssize_t) (2*number_grays); x++)
00787     {
00788       /*
00789         Sum average.
00790       */
00791       channel_features[RedPixelChannel].sum_average[i]+=
00792         x*density_xy[x].direction[i].red;
00793       channel_features[GreenPixelChannel].sum_average[i]+=
00794         x*density_xy[x].direction[i].green;
00795       channel_features[BluePixelChannel].sum_average[i]+=
00796         x*density_xy[x].direction[i].blue;
00797       if (image->colorspace == CMYKColorspace)
00798         channel_features[BlackPixelChannel].sum_average[i]+=
00799           x*density_xy[x].direction[i].black;
00800       if (image->matte != MagickFalse)
00801         channel_features[AlphaPixelChannel].sum_average[i]+=
00802           x*density_xy[x].direction[i].alpha;
00803       /*
00804         Sum entropy.
00805       */
00806       channel_features[RedPixelChannel].sum_entropy[i]-=
00807         density_xy[x].direction[i].red*
00808         log10(density_xy[x].direction[i].red+MagickEpsilon);
00809       channel_features[GreenPixelChannel].sum_entropy[i]-=
00810         density_xy[x].direction[i].green*
00811         log10(density_xy[x].direction[i].green+MagickEpsilon);
00812       channel_features[BluePixelChannel].sum_entropy[i]-=
00813         density_xy[x].direction[i].blue*
00814         log10(density_xy[x].direction[i].blue+MagickEpsilon);
00815       if (image->colorspace == CMYKColorspace)
00816         channel_features[BlackPixelChannel].sum_entropy[i]-=
00817           density_xy[x].direction[i].black*
00818           log10(density_xy[x].direction[i].black+MagickEpsilon);
00819       if (image->matte != MagickFalse)
00820         channel_features[AlphaPixelChannel].sum_entropy[i]-=
00821           density_xy[x].direction[i].alpha*
00822           log10(density_xy[x].direction[i].alpha+MagickEpsilon);
00823       /*
00824         Sum variance.
00825       */
00826       channel_features[RedPixelChannel].sum_variance[i]+=
00827         (x-channel_features[RedPixelChannel].sum_entropy[i])*
00828         (x-channel_features[RedPixelChannel].sum_entropy[i])*
00829         density_xy[x].direction[i].red;
00830       channel_features[GreenPixelChannel].sum_variance[i]+=
00831         (x-channel_features[GreenPixelChannel].sum_entropy[i])*
00832         (x-channel_features[GreenPixelChannel].sum_entropy[i])*
00833         density_xy[x].direction[i].green;
00834       channel_features[BluePixelChannel].sum_variance[i]+=
00835         (x-channel_features[BluePixelChannel].sum_entropy[i])*
00836         (x-channel_features[BluePixelChannel].sum_entropy[i])*
00837         density_xy[x].direction[i].blue;
00838       if (image->colorspace == CMYKColorspace)
00839         channel_features[BlackPixelChannel].sum_variance[i]+=
00840           (x-channel_features[BlackPixelChannel].sum_entropy[i])*
00841           (x-channel_features[BlackPixelChannel].sum_entropy[i])*
00842           density_xy[x].direction[i].black;
00843       if (image->matte != MagickFalse)
00844         channel_features[AlphaPixelChannel].sum_variance[i]+=
00845           (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
00846           (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
00847           density_xy[x].direction[i].alpha;
00848     }
00849   }
00850   /*
00851     Compute more texture features.
00852   */
00853 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00854   #pragma omp parallel for schedule(static,4) shared(status)
00855 #endif
00856   for (i=0; i < 4; i++)
00857   {
00858     register ssize_t
00859       y;
00860 
00861     for (y=0; y < (ssize_t) number_grays; y++)
00862     {
00863       register ssize_t
00864         x;
00865 
00866       for (x=0; x < (ssize_t) number_grays; x++)
00867       {
00868         /*
00869           Sum of Squares: Variance
00870         */
00871         variance.direction[i].red+=(y-mean.direction[i].red+1)*
00872           (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
00873         variance.direction[i].green+=(y-mean.direction[i].green+1)*
00874           (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
00875         variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
00876           (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
00877         if (image->colorspace == CMYKColorspace)
00878           variance.direction[i].black+=(y-mean.direction[i].black+1)*
00879             (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black;
00880         if (image->matte != MagickFalse)
00881           variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)*
00882             (y-mean.direction[i].alpha+1)*
00883             cooccurrence[x][y].direction[i].alpha;
00884         /*
00885           Sum average / Difference Variance.
00886         */
00887         density_xy[MagickAbsoluteValue(y-x)].direction[i].red+=
00888           cooccurrence[x][y].direction[i].red;
00889         density_xy[MagickAbsoluteValue(y-x)].direction[i].green+=
00890           cooccurrence[x][y].direction[i].green;
00891         density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+=
00892           cooccurrence[x][y].direction[i].blue;
00893         if (image->colorspace == CMYKColorspace)
00894           density_xy[MagickAbsoluteValue(y-x)].direction[i].black+=
00895             cooccurrence[x][y].direction[i].black;
00896         if (image->matte != MagickFalse)
00897           density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+=
00898             cooccurrence[x][y].direction[i].alpha;
00899         /*
00900           Information Measures of Correlation.
00901         */
00902         entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
00903           log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
00904         entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
00905           log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
00906         entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
00907           log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
00908         if (image->colorspace == CMYKColorspace)
00909           entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black*
00910             log10(cooccurrence[x][y].direction[i].black+MagickEpsilon);
00911         if (image->matte != MagickFalse)
00912           entropy_xy.direction[i].alpha-=
00913             cooccurrence[x][y].direction[i].alpha*log10(
00914             cooccurrence[x][y].direction[i].alpha+MagickEpsilon);
00915         entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
00916           log10(density_x[x].direction[i].red*density_y[y].direction[i].red+
00917           MagickEpsilon));
00918         entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
00919           log10(density_x[x].direction[i].green*density_y[y].direction[i].green+
00920           MagickEpsilon));
00921         entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
00922           log10(density_x[x].direction[i].blue*density_y[y].direction[i].blue+
00923           MagickEpsilon));
00924         if (image->colorspace == CMYKColorspace)
00925           entropy_xy1.direction[i].black-=(
00926             cooccurrence[x][y].direction[i].black*log10(
00927             density_x[x].direction[i].black*density_y[y].direction[i].black+
00928             MagickEpsilon));
00929         if (image->matte != MagickFalse)
00930           entropy_xy1.direction[i].alpha-=(
00931             cooccurrence[x][y].direction[i].alpha*log10(
00932             density_x[x].direction[i].alpha*density_y[y].direction[i].alpha+
00933             MagickEpsilon));
00934         entropy_xy2.direction[i].red-=(density_x[x].direction[i].red*
00935           density_y[y].direction[i].red*log10(density_x[x].direction[i].red*
00936           density_y[y].direction[i].red+MagickEpsilon));
00937         entropy_xy2.direction[i].green-=(density_x[x].direction[i].green*
00938           density_y[y].direction[i].green*log10(density_x[x].direction[i].green*
00939           density_y[y].direction[i].green+MagickEpsilon));
00940         entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue*
00941           density_y[y].direction[i].blue*log10(density_x[x].direction[i].blue*
00942           density_y[y].direction[i].blue+MagickEpsilon));
00943         if (image->colorspace == CMYKColorspace)
00944           entropy_xy2.direction[i].black-=(density_x[x].direction[i].black*
00945             density_y[y].direction[i].black*log10(
00946             density_x[x].direction[i].black*density_y[y].direction[i].black+
00947             MagickEpsilon));
00948         if (image->matte != MagickFalse)
00949           entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha*
00950             density_y[y].direction[i].alpha*log10(
00951             density_x[x].direction[i].alpha*density_y[y].direction[i].alpha+
00952             MagickEpsilon));
00953       }
00954     }
00955     channel_features[RedPixelChannel].variance_sum_of_squares[i]=
00956       variance.direction[i].red;
00957     channel_features[GreenPixelChannel].variance_sum_of_squares[i]=
00958       variance.direction[i].green;
00959     channel_features[BluePixelChannel].variance_sum_of_squares[i]=
00960       variance.direction[i].blue;
00961     if (image->colorspace == CMYKColorspace)
00962       channel_features[BlackPixelChannel].variance_sum_of_squares[i]=
00963         variance.direction[i].black;
00964     if (image->matte != MagickFalse)
00965       channel_features[AlphaPixelChannel].variance_sum_of_squares[i]=
00966         variance.direction[i].alpha;
00967   }
00968   /*
00969     Compute more texture features.
00970   */
00971   (void) ResetMagickMemory(&variance,0,sizeof(variance));
00972   (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
00973 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00974   #pragma omp parallel for schedule(static,4) shared(status)
00975 #endif
00976   for (i=0; i < 4; i++)
00977   {
00978     register ssize_t
00979       x;
00980 
00981     for (x=0; x < (ssize_t) number_grays; x++)
00982     {
00983       /*
00984         Difference variance.
00985       */
00986       variance.direction[i].red+=density_xy[x].direction[i].red;
00987       variance.direction[i].green+=density_xy[x].direction[i].green;
00988       variance.direction[i].blue+=density_xy[x].direction[i].blue;
00989       if (image->colorspace == CMYKColorspace)
00990         variance.direction[i].black+=density_xy[x].direction[i].black;
00991       if (image->matte != MagickFalse)
00992         variance.direction[i].alpha+=density_xy[x].direction[i].alpha;
00993       sum_squares.direction[i].red+=density_xy[x].direction[i].red*
00994         density_xy[x].direction[i].red;
00995       sum_squares.direction[i].green+=density_xy[x].direction[i].green*
00996         density_xy[x].direction[i].green;
00997       sum_squares.direction[i].blue+=density_xy[x].direction[i].blue*
00998         density_xy[x].direction[i].blue;
00999       if (image->colorspace == CMYKColorspace)
01000         sum_squares.direction[i].black+=density_xy[x].direction[i].black*
01001           density_xy[x].direction[i].black;
01002       if (image->matte != MagickFalse)
01003         sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha*
01004           density_xy[x].direction[i].alpha;
01005       /*
01006         Difference entropy.
01007       */
01008       channel_features[RedPixelChannel].difference_entropy[i]-=
01009         density_xy[x].direction[i].red*
01010         log10(density_xy[x].direction[i].red+MagickEpsilon);
01011       channel_features[GreenPixelChannel].difference_entropy[i]-=
01012         density_xy[x].direction[i].green*
01013         log10(density_xy[x].direction[i].green+MagickEpsilon);
01014       channel_features[BluePixelChannel].difference_entropy[i]-=
01015         density_xy[x].direction[i].blue*
01016         log10(density_xy[x].direction[i].blue+MagickEpsilon);
01017       if (image->colorspace == CMYKColorspace)
01018         channel_features[BlackPixelChannel].difference_entropy[i]-=
01019           density_xy[x].direction[i].black*
01020           log10(density_xy[x].direction[i].black+MagickEpsilon);
01021       if (image->matte != MagickFalse)
01022         channel_features[AlphaPixelChannel].difference_entropy[i]-=
01023           density_xy[x].direction[i].alpha*
01024           log10(density_xy[x].direction[i].alpha+MagickEpsilon);
01025       /*
01026         Information Measures of Correlation.
01027       */
01028       entropy_x.direction[i].red-=(density_x[x].direction[i].red*
01029         log10(density_x[x].direction[i].red+MagickEpsilon));
01030       entropy_x.direction[i].green-=(density_x[x].direction[i].green*
01031         log10(density_x[x].direction[i].green+MagickEpsilon));
01032       entropy_x.direction[i].blue-=(density_x[x].direction[i].blue*
01033         log10(density_x[x].direction[i].blue+MagickEpsilon));
01034       if (image->colorspace == CMYKColorspace)
01035         entropy_x.direction[i].black-=(density_x[x].direction[i].black*
01036           log10(density_x[x].direction[i].black+MagickEpsilon));
01037       if (image->matte != MagickFalse)
01038         entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha*
01039           log10(density_x[x].direction[i].alpha+MagickEpsilon));
01040       entropy_y.direction[i].red-=(density_y[x].direction[i].red*
01041         log10(density_y[x].direction[i].red+MagickEpsilon));
01042       entropy_y.direction[i].green-=(density_y[x].direction[i].green*
01043         log10(density_y[x].direction[i].green+MagickEpsilon));
01044       entropy_y.direction[i].blue-=(density_y[x].direction[i].blue*
01045         log10(density_y[x].direction[i].blue+MagickEpsilon));
01046       if (image->colorspace == CMYKColorspace)
01047         entropy_y.direction[i].black-=(density_y[x].direction[i].black*
01048           log10(density_y[x].direction[i].black+MagickEpsilon));
01049       if (image->matte != MagickFalse)
01050         entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha*
01051           log10(density_y[x].direction[i].alpha+MagickEpsilon));
01052     }
01053     /*
01054       Difference variance.
01055     */
01056     channel_features[RedPixelChannel].difference_variance[i]=
01057       (((double) number_grays*number_grays*sum_squares.direction[i].red)-
01058       (variance.direction[i].red*variance.direction[i].red))/
01059       ((double) number_grays*number_grays*number_grays*number_grays);
01060     channel_features[GreenPixelChannel].difference_variance[i]=
01061       (((double) number_grays*number_grays*sum_squares.direction[i].green)-
01062       (variance.direction[i].green*variance.direction[i].green))/
01063       ((double) number_grays*number_grays*number_grays*number_grays);
01064     channel_features[BluePixelChannel].difference_variance[i]=
01065       (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
01066       (variance.direction[i].blue*variance.direction[i].blue))/
01067       ((double) number_grays*number_grays*number_grays*number_grays);
01068     if (image->colorspace == CMYKColorspace)
01069       channel_features[BlackPixelChannel].difference_variance[i]=
01070         (((double) number_grays*number_grays*sum_squares.direction[i].black)-
01071         (variance.direction[i].black*variance.direction[i].black))/
01072         ((double) number_grays*number_grays*number_grays*number_grays);
01073     if (image->matte != MagickFalse)
01074       channel_features[AlphaPixelChannel].difference_variance[i]=
01075         (((double) number_grays*number_grays*sum_squares.direction[i].alpha)-
01076         (variance.direction[i].alpha*variance.direction[i].alpha))/
01077         ((double) number_grays*number_grays*number_grays*number_grays);
01078     /*
01079       Information Measures of Correlation.
01080     */
01081     channel_features[RedPixelChannel].measure_of_correlation_1[i]=
01082       (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/
01083       (entropy_x.direction[i].red > entropy_y.direction[i].red ?
01084        entropy_x.direction[i].red : entropy_y.direction[i].red);
01085     channel_features[GreenPixelChannel].measure_of_correlation_1[i]=
01086       (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/
01087       (entropy_x.direction[i].green > entropy_y.direction[i].green ?
01088        entropy_x.direction[i].green : entropy_y.direction[i].green);
01089     channel_features[BluePixelChannel].measure_of_correlation_1[i]=
01090       (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/
01091       (entropy_x.direction[i].blue > entropy_y.direction[i].blue ?
01092        entropy_x.direction[i].blue : entropy_y.direction[i].blue);
01093     if (image->colorspace == CMYKColorspace)
01094       channel_features[BlackPixelChannel].measure_of_correlation_1[i]=
01095         (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/
01096         (entropy_x.direction[i].black > entropy_y.direction[i].black ?
01097          entropy_x.direction[i].black : entropy_y.direction[i].black);
01098     if (image->matte != MagickFalse)
01099       channel_features[AlphaPixelChannel].measure_of_correlation_1[i]=
01100         (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/
01101         (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ?
01102          entropy_x.direction[i].alpha : entropy_y.direction[i].alpha);
01103     channel_features[RedPixelChannel].measure_of_correlation_2[i]=
01104       (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].red-
01105       entropy_xy.direction[i].red)))));
01106     channel_features[GreenPixelChannel].measure_of_correlation_2[i]=
01107       (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].green-
01108       entropy_xy.direction[i].green)))));
01109     channel_features[BluePixelChannel].measure_of_correlation_2[i]=
01110       (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].blue-
01111       entropy_xy.direction[i].blue)))));
01112     if (image->colorspace == CMYKColorspace)
01113       channel_features[BlackPixelChannel].measure_of_correlation_2[i]=
01114         (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].black-
01115         entropy_xy.direction[i].black)))));
01116     if (image->matte != MagickFalse)
01117       channel_features[AlphaPixelChannel].measure_of_correlation_2[i]=
01118         (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].alpha-
01119         entropy_xy.direction[i].alpha)))));
01120   }
01121   /*
01122     Compute more texture features.
01123   */
01124 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01125   #pragma omp parallel for schedule(static,4) shared(status)
01126 #endif
01127   for (i=0; i < 4; i++)
01128   {
01129     ssize_t
01130       z;
01131 
01132     for (z=0; z < (ssize_t) number_grays; z++)
01133     {
01134       register ssize_t
01135         y;
01136 
01137       ChannelStatistics
01138         pixel;
01139 
01140       (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
01141       for (y=0; y < (ssize_t) number_grays; y++)
01142       {
01143         register ssize_t
01144           x;
01145 
01146         for (x=0; x < (ssize_t) number_grays; x++)
01147         {
01148           /*
01149             Contrast:  amount of local variations present in an image.
01150           */
01151           if (((y-x) == z) || ((x-y) == z))
01152             {
01153               pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
01154               pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
01155               pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
01156               if (image->colorspace == CMYKColorspace)
01157                 pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
01158               if (image->matte != MagickFalse)
01159                 pixel.direction[i].alpha+=
01160                   cooccurrence[x][y].direction[i].alpha;
01161             }
01162           /*
01163             Maximum Correlation Coefficient.
01164           */
01165           Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
01166             cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
01167             density_y[x].direction[i].red;
01168           Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
01169             cooccurrence[y][x].direction[i].green/
01170             density_x[z].direction[i].green/density_y[x].direction[i].red;
01171           Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
01172             cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
01173             density_y[x].direction[i].blue;
01174           if (image->colorspace == CMYKColorspace)
01175             Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
01176               cooccurrence[y][x].direction[i].black/
01177               density_x[z].direction[i].black/density_y[x].direction[i].black;
01178           if (image->matte != MagickFalse)
01179             Q[z][y].direction[i].alpha+=
01180               cooccurrence[z][x].direction[i].alpha*
01181               cooccurrence[y][x].direction[i].alpha/
01182               density_x[z].direction[i].alpha/
01183               density_y[x].direction[i].alpha;
01184         }
01185       }
01186       channel_features[RedPixelChannel].contrast[i]+=z*z*
01187         pixel.direction[i].red;
01188       channel_features[GreenPixelChannel].contrast[i]+=z*z*
01189         pixel.direction[i].green;
01190       channel_features[BluePixelChannel].contrast[i]+=z*z*
01191         pixel.direction[i].blue;
01192       if (image->colorspace == CMYKColorspace)
01193         channel_features[BlackPixelChannel].contrast[i]+=z*z*
01194           pixel.direction[i].black;
01195       if (image->matte != MagickFalse)
01196         channel_features[AlphaPixelChannel].contrast[i]+=z*z*
01197           pixel.direction[i].alpha;
01198     }
01199     /*
01200       Maximum Correlation Coefficient.
01201       Future: return second largest eigenvalue of Q.
01202     */
01203     channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
01204       sqrt((double) -1.0);
01205     channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
01206       sqrt((double) -1.0);
01207     channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
01208       sqrt((double) -1.0);
01209     if (image->colorspace == CMYKColorspace)
01210       channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
01211         sqrt((double) -1.0);
01212     if (image->matte != MagickFalse)
01213       channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
01214         sqrt((double) -1.0);
01215   }
01216   /*
01217     Relinquish resources.
01218   */
01219   sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
01220   for (i=0; i < (ssize_t) number_grays; i++)
01221     Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
01222   Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
01223   density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
01224   density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
01225   density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
01226   for (i=0; i < (ssize_t) number_grays; i++)
01227     cooccurrence[i]=(ChannelStatistics *)
01228       RelinquishMagickMemory(cooccurrence[i]);
01229   cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
01230   return(channel_features);
01231 }