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