blob: bba5438204b5d319f06fde19373f569915f7495f [file] [log] [blame]
cristy3e2860c2010-01-24 01:36:30 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF EEEEE AAA TTTTT U U RRRR EEEEE %
7% F E A A T U U R R E %
8% FFF EEE AAAAA T U U RRRR EEE %
9% F E A A T U U R R E %
10% F EEEEE A A T UUU R R EEEEE %
11% %
12% %
13% MagickCore Image Feature Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3e2860c2010-01-24 01:36:30 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/animate.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/cache-private.h"
50#include "MagickCore/cache-view.h"
51#include "MagickCore/client.h"
52#include "MagickCore/color.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/composite.h"
57#include "MagickCore/composite-private.h"
58#include "MagickCore/compress.h"
59#include "MagickCore/constitute.h"
60#include "MagickCore/display.h"
61#include "MagickCore/draw.h"
62#include "MagickCore/enhance.h"
63#include "MagickCore/exception.h"
64#include "MagickCore/exception-private.h"
65#include "MagickCore/feature.h"
66#include "MagickCore/gem.h"
67#include "MagickCore/geometry.h"
68#include "MagickCore/list.h"
69#include "MagickCore/image-private.h"
70#include "MagickCore/magic.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/monitor.h"
75#include "MagickCore/monitor-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel-accessor.h"
79#include "MagickCore/profile.h"
80#include "MagickCore/quantize.h"
81#include "MagickCore/quantum-private.h"
82#include "MagickCore/random_.h"
83#include "MagickCore/segment.h"
84#include "MagickCore/semaphore.h"
85#include "MagickCore/signature-private.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/thread-private.h"
88#include "MagickCore/timer.h"
89#include "MagickCore/utility.h"
90#include "MagickCore/version.h"
cristy3e2860c2010-01-24 01:36:30 +000091
92/*
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94% %
95% %
96% %
cristy490408a2011-07-07 14:42:05 +000097% G e t I m a g e F e a t u r e s %
cristy3e2860c2010-01-24 01:36:30 +000098% %
99% %
100% %
101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102%
cristy490408a2011-07-07 14:42:05 +0000103% GetImageFeatures() returns features for each channel in the image in
cristy7396d882010-01-27 02:37:56 +0000104% each of four directions (horizontal, vertical, left and right diagonals)
105% for the specified distance. The features include the angular second
106% moment, contrast, correlation, sum of squares: variance, inverse difference
107% moment, sum average, sum varience, sum entropy, entropy, difference variance,% difference entropy, information measures of correlation 1, information
108% measures of correlation 2, and maximum correlation coefficient. You can
109% access the red channel contrast, for example, like this:
cristy3e2860c2010-01-24 01:36:30 +0000110%
cristy490408a2011-07-07 14:42:05 +0000111% channel_features=GetImageFeatures(image,1,exception);
cristyd3090f92011-10-18 00:05:15 +0000112% contrast=channel_features[RedPixelChannel].contrast[0];
cristy3e2860c2010-01-24 01:36:30 +0000113%
114% Use MagickRelinquishMemory() to free the features buffer.
115%
cristy490408a2011-07-07 14:42:05 +0000116% The format of the GetImageFeatures method is:
cristy3e2860c2010-01-24 01:36:30 +0000117%
cristy490408a2011-07-07 14:42:05 +0000118% ChannelFeatures *GetImageFeatures(const Image *image,
cristybb503372010-05-27 20:51:26 +0000119% const size_t distance,ExceptionInfo *exception)
cristy3e2860c2010-01-24 01:36:30 +0000120%
121% A description of each parameter follows:
122%
123% o image: the image.
124%
cristy7e9726d2010-01-26 02:08:40 +0000125% o distance: the distance.
126%
cristy3e2860c2010-01-24 01:36:30 +0000127% o exception: return any errors or warnings in this structure.
128%
129*/
cristye1897792010-01-29 02:05:50 +0000130
cristybb503372010-05-27 20:51:26 +0000131static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristye1897792010-01-29 02:05:50 +0000132{
133 if (x < 0)
134 return(-x);
135 return(x);
136}
137
cristy490408a2011-07-07 14:42:05 +0000138MagickExport ChannelFeatures *GetImageFeatures(const Image *image,
cristybb503372010-05-27 20:51:26 +0000139 const size_t distance,ExceptionInfo *exception)
cristy3e2860c2010-01-24 01:36:30 +0000140{
cristy7396d882010-01-27 02:37:56 +0000141 typedef struct _ChannelStatistics
cristyf2bf2c72010-01-25 19:54:15 +0000142 {
cristy101ab702011-10-13 13:06:32 +0000143 PixelInfo
cristy7396d882010-01-27 02:37:56 +0000144 direction[4]; /* horizontal, vertical, left and right diagonals */
145 } ChannelStatistics;
cristyf2bf2c72010-01-25 19:54:15 +0000146
cristy2070fa52010-01-24 03:17:57 +0000147 CacheView
148 *image_view;
149
cristy3e2860c2010-01-24 01:36:30 +0000150 ChannelFeatures
151 *channel_features;
152
cristy7396d882010-01-27 02:37:56 +0000153 ChannelStatistics
154 **cooccurrence,
155 correlation,
cristyffa10d02010-01-30 17:53:27 +0000156 *density_x,
157 *density_xy,
158 *density_y,
159 entropy_x,
160 entropy_xy,
161 entropy_xy1,
162 entropy_xy2,
163 entropy_y,
cristy7396d882010-01-27 02:37:56 +0000164 mean,
cristyffa10d02010-01-30 17:53:27 +0000165 **Q,
cristy7396d882010-01-27 02:37:56 +0000166 *sum,
cristycf5e6492010-01-28 02:45:28 +0000167 sum_squares,
168 variance;
cristy7396d882010-01-27 02:37:56 +0000169
cristy101ab702011-10-13 13:06:32 +0000170 PixelPacket
cristy7396d882010-01-27 02:37:56 +0000171 gray,
172 *grays;
cristy2070fa52010-01-24 03:17:57 +0000173
cristy2070fa52010-01-24 03:17:57 +0000174 MagickBooleanType
175 status;
176
cristybb503372010-05-27 20:51:26 +0000177 register ssize_t
cristy2070fa52010-01-24 03:17:57 +0000178 i;
179
cristy3e2860c2010-01-24 01:36:30 +0000180 size_t
181 length;
182
cristy9d314ff2011-03-09 01:30:28 +0000183 ssize_t
cristy564a5692012-01-20 23:56:26 +0000184 y;
cristy9d314ff2011-03-09 01:30:28 +0000185
cristyecd0ab52010-05-30 14:59:20 +0000186 unsigned int
cristy7396d882010-01-27 02:37:56 +0000187 number_grays;
cristyf2bf2c72010-01-25 19:54:15 +0000188
cristy3e2860c2010-01-24 01:36:30 +0000189 assert(image != (Image *) NULL);
190 assert(image->signature == MagickSignature);
191 if (image->debug != MagickFalse)
192 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000193 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
194 return((ChannelFeatures *) NULL);
cristy9a9230e2011-04-26 14:56:14 +0000195 length=CompositeChannels+1UL;
cristy3e2860c2010-01-24 01:36:30 +0000196 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
197 sizeof(*channel_features));
198 if (channel_features == (ChannelFeatures *) NULL)
199 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
200 (void) ResetMagickMemory(channel_features,0,length*
201 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000202 /*
cristy7396d882010-01-27 02:37:56 +0000203 Form grays.
cristy2070fa52010-01-24 03:17:57 +0000204 */
cristy101ab702011-10-13 13:06:32 +0000205 grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
206 if (grays == (PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000207 {
cristy2070fa52010-01-24 03:17:57 +0000208 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
209 channel_features);
cristye1897792010-01-29 02:05:50 +0000210 (void) ThrowMagickException(exception,GetMagickModule(),
211 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy2070fa52010-01-24 03:17:57 +0000212 return(channel_features);
213 }
cristybb503372010-05-27 20:51:26 +0000214 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy2070fa52010-01-24 03:17:57 +0000215 {
cristyecd0ab52010-05-30 14:59:20 +0000216 grays[i].red=(~0U);
217 grays[i].green=(~0U);
218 grays[i].blue=(~0U);
cristy4c08aed2011-07-01 19:47:50 +0000219 grays[i].alpha=(~0U);
220 grays[i].black=(~0U);
cristy2070fa52010-01-24 03:17:57 +0000221 }
222 status=MagickTrue;
223 image_view=AcquireCacheView(image);
224#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000225 #pragma omp parallel for schedule(static,4) shared(status)
cristy2070fa52010-01-24 03:17:57 +0000226#endif
cristybb503372010-05-27 20:51:26 +0000227 for (y=0; y < (ssize_t) image->rows; y++)
cristy3e2860c2010-01-24 01:36:30 +0000228 {
cristy4c08aed2011-07-01 19:47:50 +0000229 register const Quantum
cristy3e2860c2010-01-24 01:36:30 +0000230 *restrict p;
231
cristybb503372010-05-27 20:51:26 +0000232 register ssize_t
cristy3e2860c2010-01-24 01:36:30 +0000233 x;
234
cristy2070fa52010-01-24 03:17:57 +0000235 if (status == MagickFalse)
236 continue;
237 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000238 if (p == (const Quantum *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000239 {
240 status=MagickFalse;
241 continue;
242 }
cristybb503372010-05-27 20:51:26 +0000243 for (x=0; x < (ssize_t) image->columns; x++)
cristy3e2860c2010-01-24 01:36:30 +0000244 {
cristy4c08aed2011-07-01 19:47:50 +0000245 grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
246 ScaleQuantumToMap(GetPixelRed(image,p));
247 grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
248 ScaleQuantumToMap(GetPixelGreen(image,p));
249 grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
250 ScaleQuantumToMap(GetPixelBlue(image,p));
cristy2070fa52010-01-24 03:17:57 +0000251 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000252 grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
253 ScaleQuantumToMap(GetPixelBlack(image,p));
cristy53a727d2011-06-16 14:53:56 +0000254 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000255 grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
256 ScaleQuantumToMap(GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +0000257 p+=GetPixelChannels(image);
cristy3e2860c2010-01-24 01:36:30 +0000258 }
259 }
cristy30c510a2010-01-24 03:43:00 +0000260 image_view=DestroyCacheView(image_view);
261 if (status == MagickFalse)
262 {
cristy101ab702011-10-13 13:06:32 +0000263 grays=(PixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000264 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
265 channel_features);
266 return(channel_features);
267 }
cristy7396d882010-01-27 02:37:56 +0000268 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristybb503372010-05-27 20:51:26 +0000269 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy2070fa52010-01-24 03:17:57 +0000270 {
cristyecd0ab52010-05-30 14:59:20 +0000271 if (grays[i].red != ~0U)
cristy4c08aed2011-07-01 19:47:50 +0000272 grays[gray.red++].red=grays[i].red;
cristyecd0ab52010-05-30 14:59:20 +0000273 if (grays[i].green != ~0U)
cristy4c08aed2011-07-01 19:47:50 +0000274 grays[gray.green++].green=grays[i].green;
cristyecd0ab52010-05-30 14:59:20 +0000275 if (grays[i].blue != ~0U)
cristy4c08aed2011-07-01 19:47:50 +0000276 grays[gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000277 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000278 if (grays[i].black != ~0U)
279 grays[gray.black++].black=grays[i].black;
cristy53a727d2011-06-16 14:53:56 +0000280 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000281 if (grays[i].alpha != ~0U)
282 grays[gray.alpha++].alpha=grays[i].alpha;
cristy2070fa52010-01-24 03:17:57 +0000283 }
cristyf2bf2c72010-01-25 19:54:15 +0000284 /*
285 Allocate spatial dependence matrix.
286 */
cristy7396d882010-01-27 02:37:56 +0000287 number_grays=gray.red;
288 if (gray.green > number_grays)
289 number_grays=gray.green;
290 if (gray.blue > number_grays)
291 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000292 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000293 if (gray.black > number_grays)
294 number_grays=gray.black;
cristy53a727d2011-06-16 14:53:56 +0000295 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000296 if (gray.alpha > number_grays)
297 number_grays=gray.alpha;
cristy7396d882010-01-27 02:37:56 +0000298 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
299 sizeof(*cooccurrence));
cristy77173e52010-02-02 02:51:35 +0000300 density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000301 sizeof(*density_x));
302 density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
303 sizeof(*density_xy));
cristy77173e52010-02-02 02:51:35 +0000304 density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000305 sizeof(*density_y));
306 Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
307 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
308 if ((cooccurrence == (ChannelStatistics **) NULL) ||
309 (density_x == (ChannelStatistics *) NULL) ||
310 (density_xy == (ChannelStatistics *) NULL) ||
311 (density_y == (ChannelStatistics *) NULL) ||
312 (Q == (ChannelStatistics **) NULL) ||
313 (sum == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000314 {
cristyffa10d02010-01-30 17:53:27 +0000315 if (Q != (ChannelStatistics **) NULL)
316 {
cristybb503372010-05-27 20:51:26 +0000317 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000318 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
319 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
320 }
321 if (sum != (ChannelStatistics *) NULL)
322 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
323 if (density_y != (ChannelStatistics *) NULL)
324 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
325 if (density_xy != (ChannelStatistics *) NULL)
326 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
327 if (density_x != (ChannelStatistics *) NULL)
328 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
329 if (cooccurrence != (ChannelStatistics **) NULL)
330 {
cristybb503372010-05-27 20:51:26 +0000331 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000332 cooccurrence[i]=(ChannelStatistics *)
333 RelinquishMagickMemory(cooccurrence[i]);
334 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
335 cooccurrence);
336 }
cristy101ab702011-10-13 13:06:32 +0000337 grays=(PixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000338 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
339 channel_features);
cristye1897792010-01-29 02:05:50 +0000340 (void) ThrowMagickException(exception,GetMagickModule(),
341 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000342 return(channel_features);
343 }
cristyffa10d02010-01-30 17:53:27 +0000344 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
cristy77173e52010-02-02 02:51:35 +0000345 (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
346 (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
347 (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
cristyffa10d02010-01-30 17:53:27 +0000348 (void) ResetMagickMemory(&mean,0,sizeof(mean));
349 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
350 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
351 (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
352 (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
353 (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
354 (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
355 (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
356 (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
357 (void) ResetMagickMemory(&variance,0,sizeof(variance));
cristybb503372010-05-27 20:51:26 +0000358 for (i=0; i < (ssize_t) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000359 {
cristy7396d882010-01-27 02:37:56 +0000360 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
361 sizeof(**cooccurrence));
cristyffa10d02010-01-30 17:53:27 +0000362 Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
363 if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
364 (Q[i] == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000365 break;
cristy7396d882010-01-27 02:37:56 +0000366 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
cristy3749be42010-02-02 02:46:51 +0000367 sizeof(**cooccurrence));
368 (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
cristyf2bf2c72010-01-25 19:54:15 +0000369 }
cristybb503372010-05-27 20:51:26 +0000370 if (i < (ssize_t) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000371 {
cristyf2bf2c72010-01-25 19:54:15 +0000372 for (i--; i >= 0; i--)
cristyffa10d02010-01-30 17:53:27 +0000373 {
374 if (Q[i] != (ChannelStatistics *) NULL)
375 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
376 if (cooccurrence[i] != (ChannelStatistics *) NULL)
377 cooccurrence[i]=(ChannelStatistics *)
378 RelinquishMagickMemory(cooccurrence[i]);
379 }
380 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
cristy7396d882010-01-27 02:37:56 +0000381 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyffa10d02010-01-30 17:53:27 +0000382 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
383 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
384 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
385 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
cristy101ab702011-10-13 13:06:32 +0000386 grays=(PixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000387 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
388 channel_features);
cristye1897792010-01-29 02:05:50 +0000389 (void) ThrowMagickException(exception,GetMagickModule(),
390 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000391 return(channel_features);
392 }
393 /*
394 Initialize spatial dependence matrix.
395 */
396 status=MagickTrue;
397 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000398 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2bf2c72010-01-25 19:54:15 +0000399 {
cristy4c08aed2011-07-01 19:47:50 +0000400 register const Quantum
cristyf2bf2c72010-01-25 19:54:15 +0000401 *restrict p;
402
cristybb503372010-05-27 20:51:26 +0000403 register ssize_t
cristyf2bf2c72010-01-25 19:54:15 +0000404 x;
405
cristy7e9726d2010-01-26 02:08:40 +0000406 ssize_t
cristy53a727d2011-06-16 14:53:56 +0000407 i,
cristy9d314ff2011-03-09 01:30:28 +0000408 offset,
409 u,
410 v;
cristy7e9726d2010-01-26 02:08:40 +0000411
cristyf2bf2c72010-01-25 19:54:15 +0000412 if (status == MagickFalse)
413 continue;
cristybb503372010-05-27 20:51:26 +0000414 p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,y,image->columns+
cristya1d2bd32011-10-19 22:12:25 +0000415 2*distance,distance+2,exception);
cristy4c08aed2011-07-01 19:47:50 +0000416 if (p == (const Quantum *) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000417 {
418 status=MagickFalse;
419 continue;
420 }
cristy8a11cb12011-10-19 23:53:34 +0000421 p+=distance*GetPixelChannels(image);;
cristybb503372010-05-27 20:51:26 +0000422 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2bf2c72010-01-25 19:54:15 +0000423 {
424 for (i=0; i < 4; i++)
425 {
cristy7e9726d2010-01-26 02:08:40 +0000426 switch (i)
427 {
428 case 0:
cristy549a37e2010-01-26 15:24:15 +0000429 default:
cristy7e9726d2010-01-26 02:08:40 +0000430 {
431 /*
cristy7396d882010-01-27 02:37:56 +0000432 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000433 */
434 offset=(ssize_t) distance;
435 break;
436 }
437 case 1:
438 {
439 /*
cristy7396d882010-01-27 02:37:56 +0000440 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000441 */
cristy7396d882010-01-27 02:37:56 +0000442 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000443 break;
444 }
445 case 2:
446 {
447 /*
cristy7396d882010-01-27 02:37:56 +0000448 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000449 */
cristyd99b0962010-05-29 23:14:26 +0000450 offset=(ssize_t) ((image->columns+2*distance)-distance);
cristy7e9726d2010-01-26 02:08:40 +0000451 break;
452 }
453 case 3:
454 {
455 /*
cristy7396d882010-01-27 02:37:56 +0000456 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000457 */
cristyd99b0962010-05-29 23:14:26 +0000458 offset=(ssize_t) ((image->columns+2*distance)+distance);
cristy7e9726d2010-01-26 02:08:40 +0000459 break;
460 }
461 }
462 u=0;
463 v=0;
cristy4c08aed2011-07-01 19:47:50 +0000464 while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p)))
cristy7e9726d2010-01-26 02:08:40 +0000465 u++;
cristyed231572011-07-14 02:18:59 +0000466 while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image))))
cristy7e9726d2010-01-26 02:08:40 +0000467 v++;
cristy7396d882010-01-27 02:37:56 +0000468 cooccurrence[u][v].direction[i].red++;
469 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000470 u=0;
471 v=0;
cristy4c08aed2011-07-01 19:47:50 +0000472 while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p)))
cristy7e9726d2010-01-26 02:08:40 +0000473 u++;
cristyed231572011-07-14 02:18:59 +0000474 while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image))))
cristy7e9726d2010-01-26 02:08:40 +0000475 v++;
cristy7396d882010-01-27 02:37:56 +0000476 cooccurrence[u][v].direction[i].green++;
477 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000478 u=0;
479 v=0;
cristy4c08aed2011-07-01 19:47:50 +0000480 while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p)))
cristy7e9726d2010-01-26 02:08:40 +0000481 u++;
cristyed231572011-07-14 02:18:59 +0000482 while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image))))
cristy7e9726d2010-01-26 02:08:40 +0000483 v++;
cristy7396d882010-01-27 02:37:56 +0000484 cooccurrence[u][v].direction[i].blue++;
485 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000486 if (image->colorspace == CMYKColorspace)
487 {
488 u=0;
489 v=0;
cristy4c08aed2011-07-01 19:47:50 +0000490 while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p)))
cristy7e9726d2010-01-26 02:08:40 +0000491 u++;
cristyed231572011-07-14 02:18:59 +0000492 while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image))))
cristy7e9726d2010-01-26 02:08:40 +0000493 v++;
cristy4c08aed2011-07-01 19:47:50 +0000494 cooccurrence[u][v].direction[i].black++;
495 cooccurrence[v][u].direction[i].black++;
cristy7e9726d2010-01-26 02:08:40 +0000496 }
cristy53a727d2011-06-16 14:53:56 +0000497 if (image->matte != MagickFalse)
498 {
499 u=0;
500 v=0;
cristy4c08aed2011-07-01 19:47:50 +0000501 while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p)))
cristy53a727d2011-06-16 14:53:56 +0000502 u++;
cristyed231572011-07-14 02:18:59 +0000503 while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image))))
cristy53a727d2011-06-16 14:53:56 +0000504 v++;
cristy4c08aed2011-07-01 19:47:50 +0000505 cooccurrence[u][v].direction[i].alpha++;
506 cooccurrence[v][u].direction[i].alpha++;
cristy53a727d2011-06-16 14:53:56 +0000507 }
cristyf2bf2c72010-01-25 19:54:15 +0000508 }
cristyed231572011-07-14 02:18:59 +0000509 p+=GetPixelChannels(image);
cristyf2bf2c72010-01-25 19:54:15 +0000510 }
511 }
cristy101ab702011-10-13 13:06:32 +0000512 grays=(PixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000513 image_view=DestroyCacheView(image_view);
514 if (status == MagickFalse)
515 {
cristybb503372010-05-27 20:51:26 +0000516 for (i=0; i < (ssize_t) number_grays; i++)
cristy7396d882010-01-27 02:37:56 +0000517 cooccurrence[i]=(ChannelStatistics *)
518 RelinquishMagickMemory(cooccurrence[i]);
519 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyf2bf2c72010-01-25 19:54:15 +0000520 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
521 channel_features);
cristye1897792010-01-29 02:05:50 +0000522 (void) ThrowMagickException(exception,GetMagickModule(),
523 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000524 return(channel_features);
525 }
526 /*
cristy7e9726d2010-01-26 02:08:40 +0000527 Normalize spatial dependence matrix.
528 */
cristybd822072010-01-27 00:30:00 +0000529 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000530 {
cristy549a37e2010-01-26 15:24:15 +0000531 double
532 normalize;
533
cristy53a727d2011-06-16 14:53:56 +0000534 register ssize_t
535 y;
536
cristybd822072010-01-27 00:30:00 +0000537 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000538 {
cristybd822072010-01-27 00:30:00 +0000539 case 0:
540 default:
cristy7e9726d2010-01-26 02:08:40 +0000541 {
cristybd822072010-01-27 00:30:00 +0000542 /*
cristy7396d882010-01-27 02:37:56 +0000543 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000544 */
545 normalize=2.0*image->rows*(image->columns-distance);
546 break;
547 }
548 case 1:
549 {
550 /*
cristy7396d882010-01-27 02:37:56 +0000551 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000552 */
cristy7396d882010-01-27 02:37:56 +0000553 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000554 break;
555 }
556 case 2:
557 {
558 /*
cristy7396d882010-01-27 02:37:56 +0000559 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000560 */
cristy7396d882010-01-27 02:37:56 +0000561 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000562 break;
563 }
564 case 3:
565 {
566 /*
cristy7396d882010-01-27 02:37:56 +0000567 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000568 */
569 normalize=2.0*(image->rows-distance)*(image->columns-distance);
570 break;
571 }
572 }
cristy53a727d2011-06-16 14:53:56 +0000573 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 : normalize);
cristybb503372010-05-27 20:51:26 +0000574 for (y=0; y < (ssize_t) number_grays; y++)
cristybd822072010-01-27 00:30:00 +0000575 {
cristybb503372010-05-27 20:51:26 +0000576 register ssize_t
cristybd822072010-01-27 00:30:00 +0000577 x;
578
cristybb503372010-05-27 20:51:26 +0000579 for (x=0; x < (ssize_t) number_grays; x++)
cristybd822072010-01-27 00:30:00 +0000580 {
cristy53a727d2011-06-16 14:53:56 +0000581 cooccurrence[x][y].direction[i].red*=normalize;
582 cooccurrence[x][y].direction[i].green*=normalize;
583 cooccurrence[x][y].direction[i].blue*=normalize;
cristy549a37e2010-01-26 15:24:15 +0000584 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000585 cooccurrence[x][y].direction[i].black*=normalize;
cristy53a727d2011-06-16 14:53:56 +0000586 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000587 cooccurrence[x][y].direction[i].alpha*=normalize;
cristy549a37e2010-01-26 15:24:15 +0000588 }
589 }
590 }
591 /*
592 Compute texture features.
593 */
cristy3a82f252010-01-26 20:31:51 +0000594#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000595 #pragma omp parallel for schedule(static,4) shared(status)
cristy3a82f252010-01-26 20:31:51 +0000596#endif
cristybd822072010-01-27 00:30:00 +0000597 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000598 {
cristybb503372010-05-27 20:51:26 +0000599 register ssize_t
cristybd822072010-01-27 00:30:00 +0000600 y;
cristy549a37e2010-01-26 15:24:15 +0000601
cristybb503372010-05-27 20:51:26 +0000602 for (y=0; y < (ssize_t) number_grays; y++)
cristy549a37e2010-01-26 15:24:15 +0000603 {
cristybb503372010-05-27 20:51:26 +0000604 register ssize_t
cristybd822072010-01-27 00:30:00 +0000605 x;
606
cristybb503372010-05-27 20:51:26 +0000607 for (x=0; x < (ssize_t) number_grays; x++)
cristy549a37e2010-01-26 15:24:15 +0000608 {
609 /*
cristy3a82f252010-01-26 20:31:51 +0000610 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000611 */
cristyd3090f92011-10-18 00:05:15 +0000612 channel_features[RedPixelChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000613 cooccurrence[x][y].direction[i].red*
614 cooccurrence[x][y].direction[i].red;
cristyd3090f92011-10-18 00:05:15 +0000615 channel_features[GreenPixelChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000616 cooccurrence[x][y].direction[i].green*
617 cooccurrence[x][y].direction[i].green;
cristyd3090f92011-10-18 00:05:15 +0000618 channel_features[BluePixelChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000619 cooccurrence[x][y].direction[i].blue*
620 cooccurrence[x][y].direction[i].blue;
cristy549a37e2010-01-26 15:24:15 +0000621 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000622 channel_features[BlackPixelChannel].angular_second_moment[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000623 cooccurrence[x][y].direction[i].black*
624 cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000625 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000626 channel_features[AlphaPixelChannel].angular_second_moment[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000627 cooccurrence[x][y].direction[i].alpha*
628 cooccurrence[x][y].direction[i].alpha;
cristy7396d882010-01-27 02:37:56 +0000629 /*
630 Correlation: measure of linear-dependencies in the image.
631 */
632 sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000633 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
cristycdf8e1b2010-01-28 01:52:33 +0000634 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristycdf8e1b2010-01-28 01:52:33 +0000635 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000636 sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000637 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000638 sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha;
cristycdf8e1b2010-01-28 01:52:33 +0000639 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000640 correlation.direction[i].green+=x*y*
641 cooccurrence[x][y].direction[i].green;
cristy7396d882010-01-27 02:37:56 +0000642 correlation.direction[i].blue+=x*y*
643 cooccurrence[x][y].direction[i].blue;
cristy7396d882010-01-27 02:37:56 +0000644 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000645 correlation.direction[i].black+=x*y*
646 cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000647 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000648 correlation.direction[i].alpha+=x*y*
649 cooccurrence[x][y].direction[i].alpha;
cristycf5e6492010-01-28 02:45:28 +0000650 /*
651 Inverse Difference Moment.
652 */
cristyd3090f92011-10-18 00:05:15 +0000653 channel_features[RedPixelChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000654 cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
cristyd3090f92011-10-18 00:05:15 +0000655 channel_features[GreenPixelChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000656 cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
cristyd3090f92011-10-18 00:05:15 +0000657 channel_features[BluePixelChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000658 cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
cristycf5e6492010-01-28 02:45:28 +0000659 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000660 channel_features[BlackPixelChannel].inverse_difference_moment[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000661 cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1);
cristy53a727d2011-06-16 14:53:56 +0000662 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000663 channel_features[AlphaPixelChannel].inverse_difference_moment[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000664 cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1);
cristye1897792010-01-29 02:05:50 +0000665 /*
666 Sum average.
667 */
cristyffa10d02010-01-30 17:53:27 +0000668 density_xy[y+x+2].direction[i].red+=
cristye1897792010-01-29 02:05:50 +0000669 cooccurrence[x][y].direction[i].red;
cristyffa10d02010-01-30 17:53:27 +0000670 density_xy[y+x+2].direction[i].green+=
cristye1897792010-01-29 02:05:50 +0000671 cooccurrence[x][y].direction[i].green;
cristyffa10d02010-01-30 17:53:27 +0000672 density_xy[y+x+2].direction[i].blue+=
cristye1897792010-01-29 02:05:50 +0000673 cooccurrence[x][y].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000674 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000675 density_xy[y+x+2].direction[i].black+=
676 cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000677 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000678 density_xy[y+x+2].direction[i].alpha+=
679 cooccurrence[x][y].direction[i].alpha;
cristye1897792010-01-29 02:05:50 +0000680 /*
681 Entropy.
682 */
cristyd3090f92011-10-18 00:05:15 +0000683 channel_features[RedPixelChannel].entropy[i]-=
cristye1897792010-01-29 02:05:50 +0000684 cooccurrence[x][y].direction[i].red*
685 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +0000686 channel_features[GreenPixelChannel].entropy[i]-=
cristye1897792010-01-29 02:05:50 +0000687 cooccurrence[x][y].direction[i].green*
688 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +0000689 channel_features[BluePixelChannel].entropy[i]-=
cristye1897792010-01-29 02:05:50 +0000690 cooccurrence[x][y].direction[i].blue*
691 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000692 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000693 channel_features[BlackPixelChannel].entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +0000694 cooccurrence[x][y].direction[i].black*
695 log10(cooccurrence[x][y].direction[i].black+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000696 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000697 channel_features[AlphaPixelChannel].entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +0000698 cooccurrence[x][y].direction[i].alpha*
699 log10(cooccurrence[x][y].direction[i].alpha+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000700 /*
701 Information Measures of Correlation.
702 */
cristyffa10d02010-01-30 17:53:27 +0000703 density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
704 density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
705 density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +0000706 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000707 density_x[x].direction[i].alpha+=
708 cooccurrence[x][y].direction[i].alpha;
709 if (image->colorspace == CMYKColorspace)
710 density_x[x].direction[i].black+=
711 cooccurrence[x][y].direction[i].black;
cristyffa10d02010-01-30 17:53:27 +0000712 density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
713 density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
714 density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristye0acabf2010-01-30 00:52:38 +0000715 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000716 density_y[y].direction[i].black+=
717 cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000718 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000719 density_y[y].direction[i].alpha+=
720 cooccurrence[x][y].direction[i].alpha;
cristy7e9726d2010-01-26 02:08:40 +0000721 }
cristycdf8e1b2010-01-28 01:52:33 +0000722 mean.direction[i].red+=y*sum[y].direction[i].red;
723 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
724 mean.direction[i].green+=y*sum[y].direction[i].green;
725 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
726 mean.direction[i].blue+=y*sum[y].direction[i].blue;
727 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
cristycdf8e1b2010-01-28 01:52:33 +0000728 if (image->colorspace == CMYKColorspace)
729 {
cristy4c08aed2011-07-01 19:47:50 +0000730 mean.direction[i].black+=y*sum[y].direction[i].black;
731 sum_squares.direction[i].black+=y*y*sum[y].direction[i].black;
cristycdf8e1b2010-01-28 01:52:33 +0000732 }
cristy53a727d2011-06-16 14:53:56 +0000733 if (image->matte != MagickFalse)
734 {
cristy4c08aed2011-07-01 19:47:50 +0000735 mean.direction[i].alpha+=y*sum[y].direction[i].alpha;
736 sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha;
cristy53a727d2011-06-16 14:53:56 +0000737 }
cristy7e9726d2010-01-26 02:08:40 +0000738 }
cristycdf8e1b2010-01-28 01:52:33 +0000739 /*
740 Correlation: measure of linear-dependencies in the image.
741 */
cristyd3090f92011-10-18 00:05:15 +0000742 channel_features[RedPixelChannel].correlation[i]=
cristycdf8e1b2010-01-28 01:52:33 +0000743 (correlation.direction[i].red-mean.direction[i].red*
744 mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
745 (mean.direction[i].red*mean.direction[i].red))*sqrt(
746 sum_squares.direction[i].red-(mean.direction[i].red*
747 mean.direction[i].red)));
cristyd3090f92011-10-18 00:05:15 +0000748 channel_features[GreenPixelChannel].correlation[i]=
cristycdf8e1b2010-01-28 01:52:33 +0000749 (correlation.direction[i].green-mean.direction[i].green*
750 mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
751 (mean.direction[i].green*mean.direction[i].green))*sqrt(
752 sum_squares.direction[i].green-(mean.direction[i].green*
753 mean.direction[i].green)));
cristyd3090f92011-10-18 00:05:15 +0000754 channel_features[BluePixelChannel].correlation[i]=
cristycdf8e1b2010-01-28 01:52:33 +0000755 (correlation.direction[i].blue-mean.direction[i].blue*
756 mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
757 (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
758 sum_squares.direction[i].blue-(mean.direction[i].blue*
759 mean.direction[i].blue)));
cristycdf8e1b2010-01-28 01:52:33 +0000760 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000761 channel_features[BlackPixelChannel].correlation[i]=
cristy4c08aed2011-07-01 19:47:50 +0000762 (correlation.direction[i].black-mean.direction[i].black*
763 mean.direction[i].black)/(sqrt(sum_squares.direction[i].black-
764 (mean.direction[i].black*mean.direction[i].black))*sqrt(
765 sum_squares.direction[i].black-(mean.direction[i].black*
766 mean.direction[i].black)));
cristy53a727d2011-06-16 14:53:56 +0000767 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000768 channel_features[AlphaPixelChannel].correlation[i]=
cristy4c08aed2011-07-01 19:47:50 +0000769 (correlation.direction[i].alpha-mean.direction[i].alpha*
770 mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha-
771 (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt(
772 sum_squares.direction[i].alpha-(mean.direction[i].alpha*
773 mean.direction[i].alpha)));
cristy7e9726d2010-01-26 02:08:40 +0000774 }
cristycf5e6492010-01-28 02:45:28 +0000775 /*
776 Compute more texture features.
777 */
cristye1897792010-01-29 02:05:50 +0000778#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000779 #pragma omp parallel for schedule(static,4) shared(status)
cristye1897792010-01-29 02:05:50 +0000780#endif
781 for (i=0; i < 4; i++)
782 {
cristybb503372010-05-27 20:51:26 +0000783 register ssize_t
cristye1897792010-01-29 02:05:50 +0000784 x;
785
cristybb503372010-05-27 20:51:26 +0000786 for (x=2; x < (ssize_t) (2*number_grays); x++)
cristye1897792010-01-29 02:05:50 +0000787 {
788 /*
789 Sum average.
790 */
cristyd3090f92011-10-18 00:05:15 +0000791 channel_features[RedPixelChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000792 x*density_xy[x].direction[i].red;
cristyd3090f92011-10-18 00:05:15 +0000793 channel_features[GreenPixelChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000794 x*density_xy[x].direction[i].green;
cristyd3090f92011-10-18 00:05:15 +0000795 channel_features[BluePixelChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000796 x*density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000797 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000798 channel_features[BlackPixelChannel].sum_average[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000799 x*density_xy[x].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000800 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000801 channel_features[AlphaPixelChannel].sum_average[i]+=
cristy4c08aed2011-07-01 19:47:50 +0000802 x*density_xy[x].direction[i].alpha;
cristye1897792010-01-29 02:05:50 +0000803 /*
804 Sum entropy.
805 */
cristyd3090f92011-10-18 00:05:15 +0000806 channel_features[RedPixelChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000807 density_xy[x].direction[i].red*
808 log10(density_xy[x].direction[i].red+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +0000809 channel_features[GreenPixelChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000810 density_xy[x].direction[i].green*
811 log10(density_xy[x].direction[i].green+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +0000812 channel_features[BluePixelChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000813 density_xy[x].direction[i].blue*
814 log10(density_xy[x].direction[i].blue+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000815 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000816 channel_features[BlackPixelChannel].sum_entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +0000817 density_xy[x].direction[i].black*
818 log10(density_xy[x].direction[i].black+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000819 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000820 channel_features[AlphaPixelChannel].sum_entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +0000821 density_xy[x].direction[i].alpha*
822 log10(density_xy[x].direction[i].alpha+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000823 /*
824 Sum variance.
825 */
cristyd3090f92011-10-18 00:05:15 +0000826 channel_features[RedPixelChannel].sum_variance[i]+=
827 (x-channel_features[RedPixelChannel].sum_entropy[i])*
828 (x-channel_features[RedPixelChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000829 density_xy[x].direction[i].red;
cristyd3090f92011-10-18 00:05:15 +0000830 channel_features[GreenPixelChannel].sum_variance[i]+=
831 (x-channel_features[GreenPixelChannel].sum_entropy[i])*
832 (x-channel_features[GreenPixelChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000833 density_xy[x].direction[i].green;
cristyd3090f92011-10-18 00:05:15 +0000834 channel_features[BluePixelChannel].sum_variance[i]+=
835 (x-channel_features[BluePixelChannel].sum_entropy[i])*
836 (x-channel_features[BluePixelChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000837 density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000838 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000839 channel_features[BlackPixelChannel].sum_variance[i]+=
840 (x-channel_features[BlackPixelChannel].sum_entropy[i])*
841 (x-channel_features[BlackPixelChannel].sum_entropy[i])*
cristy4c08aed2011-07-01 19:47:50 +0000842 density_xy[x].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000843 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000844 channel_features[AlphaPixelChannel].sum_variance[i]+=
845 (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
846 (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
cristy4c08aed2011-07-01 19:47:50 +0000847 density_xy[x].direction[i].alpha;
cristye1897792010-01-29 02:05:50 +0000848 }
849 }
850 /*
851 Compute more texture features.
852 */
cristycf5e6492010-01-28 02:45:28 +0000853#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000854 #pragma omp parallel for schedule(static,4) shared(status)
cristycf5e6492010-01-28 02:45:28 +0000855#endif
856 for (i=0; i < 4; i++)
857 {
cristybb503372010-05-27 20:51:26 +0000858 register ssize_t
cristycf5e6492010-01-28 02:45:28 +0000859 y;
860
cristybb503372010-05-27 20:51:26 +0000861 for (y=0; y < (ssize_t) number_grays; y++)
cristycf5e6492010-01-28 02:45:28 +0000862 {
cristybb503372010-05-27 20:51:26 +0000863 register ssize_t
cristycf5e6492010-01-28 02:45:28 +0000864 x;
865
cristybb503372010-05-27 20:51:26 +0000866 for (x=0; x < (ssize_t) number_grays; x++)
cristycf5e6492010-01-28 02:45:28 +0000867 {
868 /*
869 Sum of Squares: Variance
870 */
871 variance.direction[i].red+=(y-mean.direction[i].red+1)*
872 (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
873 variance.direction[i].green+=(y-mean.direction[i].green+1)*
874 (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
875 variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
876 (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +0000877 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000878 variance.direction[i].black+=(y-mean.direction[i].black+1)*
879 (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black;
cristycf5e6492010-01-28 02:45:28 +0000880 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000881 variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)*
882 (y-mean.direction[i].alpha+1)*
883 cooccurrence[x][y].direction[i].alpha;
cristye1897792010-01-29 02:05:50 +0000884 /*
cristye0acabf2010-01-30 00:52:38 +0000885 Sum average / Difference Variance.
cristye1897792010-01-29 02:05:50 +0000886 */
cristyffa10d02010-01-30 17:53:27 +0000887 density_xy[MagickAbsoluteValue(y-x)].direction[i].red+=
cristye1897792010-01-29 02:05:50 +0000888 cooccurrence[x][y].direction[i].red;
cristyffa10d02010-01-30 17:53:27 +0000889 density_xy[MagickAbsoluteValue(y-x)].direction[i].green+=
cristye1897792010-01-29 02:05:50 +0000890 cooccurrence[x][y].direction[i].green;
cristyffa10d02010-01-30 17:53:27 +0000891 density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+=
cristye1897792010-01-29 02:05:50 +0000892 cooccurrence[x][y].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000893 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000894 density_xy[MagickAbsoluteValue(y-x)].direction[i].black+=
895 cooccurrence[x][y].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000896 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000897 density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+=
898 cooccurrence[x][y].direction[i].alpha;
cristye0acabf2010-01-30 00:52:38 +0000899 /*
900 Information Measures of Correlation.
901 */
cristyffa10d02010-01-30 17:53:27 +0000902 entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
cristye0acabf2010-01-30 00:52:38 +0000903 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000904 entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
cristye0acabf2010-01-30 00:52:38 +0000905 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000906 entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
cristye0acabf2010-01-30 00:52:38 +0000907 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000908 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000909 entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black*
910 log10(cooccurrence[x][y].direction[i].black+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000911 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000912 entropy_xy.direction[i].alpha-=
913 cooccurrence[x][y].direction[i].alpha*log10(
914 cooccurrence[x][y].direction[i].alpha+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000915 entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
916 log10(density_x[x].direction[i].red*density_y[y].direction[i].red+
917 MagickEpsilon));
918 entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
919 log10(density_x[x].direction[i].green*density_y[y].direction[i].green+
920 MagickEpsilon));
921 entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
922 log10(density_x[x].direction[i].blue*density_y[y].direction[i].blue+
923 MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +0000924 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000925 entropy_xy1.direction[i].black-=(
926 cooccurrence[x][y].direction[i].black*log10(
927 density_x[x].direction[i].black*density_y[y].direction[i].black+
cristyffa10d02010-01-30 17:53:27 +0000928 MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +0000929 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000930 entropy_xy1.direction[i].alpha-=(
931 cooccurrence[x][y].direction[i].alpha*log10(
932 density_x[x].direction[i].alpha*density_y[y].direction[i].alpha+
cristy53a727d2011-06-16 14:53:56 +0000933 MagickEpsilon));
cristyffa10d02010-01-30 17:53:27 +0000934 entropy_xy2.direction[i].red-=(density_x[x].direction[i].red*
935 density_y[y].direction[i].red*log10(density_x[x].direction[i].red*
936 density_y[y].direction[i].red+MagickEpsilon));
937 entropy_xy2.direction[i].green-=(density_x[x].direction[i].green*
938 density_y[y].direction[i].green*log10(density_x[x].direction[i].green*
939 density_y[y].direction[i].green+MagickEpsilon));
940 entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue*
941 density_y[y].direction[i].blue*log10(density_x[x].direction[i].blue*
942 density_y[y].direction[i].blue+MagickEpsilon));
cristyffa10d02010-01-30 17:53:27 +0000943 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000944 entropy_xy2.direction[i].black-=(density_x[x].direction[i].black*
945 density_y[y].direction[i].black*log10(
946 density_x[x].direction[i].black*density_y[y].direction[i].black+
cristyffa10d02010-01-30 17:53:27 +0000947 MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +0000948 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000949 entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha*
950 density_y[y].direction[i].alpha*log10(
951 density_x[x].direction[i].alpha*density_y[y].direction[i].alpha+
cristy53a727d2011-06-16 14:53:56 +0000952 MagickEpsilon));
cristycf5e6492010-01-28 02:45:28 +0000953 }
954 }
cristyd3090f92011-10-18 00:05:15 +0000955 channel_features[RedPixelChannel].variance_sum_of_squares[i]=
cristycf5e6492010-01-28 02:45:28 +0000956 variance.direction[i].red;
cristyd3090f92011-10-18 00:05:15 +0000957 channel_features[GreenPixelChannel].variance_sum_of_squares[i]=
cristycf5e6492010-01-28 02:45:28 +0000958 variance.direction[i].green;
cristyd3090f92011-10-18 00:05:15 +0000959 channel_features[BluePixelChannel].variance_sum_of_squares[i]=
cristycf5e6492010-01-28 02:45:28 +0000960 variance.direction[i].blue;
cristycf5e6492010-01-28 02:45:28 +0000961 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +0000962 channel_features[BlackPixelChannel].variance_sum_of_squares[i]=
cristy4c08aed2011-07-01 19:47:50 +0000963 variance.direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000964 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +0000965 channel_features[AlphaPixelChannel].variance_sum_of_squares[i]=
cristy4c08aed2011-07-01 19:47:50 +0000966 variance.direction[i].alpha;
cristycf5e6492010-01-28 02:45:28 +0000967 }
968 /*
969 Compute more texture features.
970 */
cristye1897792010-01-29 02:05:50 +0000971 (void) ResetMagickMemory(&variance,0,sizeof(variance));
972 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
973#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000974 #pragma omp parallel for schedule(static,4) shared(status)
cristye1897792010-01-29 02:05:50 +0000975#endif
976 for (i=0; i < 4; i++)
977 {
cristybb503372010-05-27 20:51:26 +0000978 register ssize_t
cristye1897792010-01-29 02:05:50 +0000979 x;
980
cristybb503372010-05-27 20:51:26 +0000981 for (x=0; x < (ssize_t) number_grays; x++)
cristye1897792010-01-29 02:05:50 +0000982 {
cristye0acabf2010-01-30 00:52:38 +0000983 /*
984 Difference variance.
985 */
cristyffa10d02010-01-30 17:53:27 +0000986 variance.direction[i].red+=density_xy[x].direction[i].red;
987 variance.direction[i].green+=density_xy[x].direction[i].green;
988 variance.direction[i].blue+=density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000989 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000990 variance.direction[i].black+=density_xy[x].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +0000991 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000992 variance.direction[i].alpha+=density_xy[x].direction[i].alpha;
cristyffa10d02010-01-30 17:53:27 +0000993 sum_squares.direction[i].red+=density_xy[x].direction[i].red*
994 density_xy[x].direction[i].red;
995 sum_squares.direction[i].green+=density_xy[x].direction[i].green*
996 density_xy[x].direction[i].green;
997 sum_squares.direction[i].blue+=density_xy[x].direction[i].blue*
998 density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000999 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001000 sum_squares.direction[i].black+=density_xy[x].direction[i].black*
1001 density_xy[x].direction[i].black;
cristy53a727d2011-06-16 14:53:56 +00001002 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001003 sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha*
1004 density_xy[x].direction[i].alpha;
cristyf6214de2010-01-29 02:47:41 +00001005 /*
1006 Difference entropy.
1007 */
cristyd3090f92011-10-18 00:05:15 +00001008 channel_features[RedPixelChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001009 density_xy[x].direction[i].red*
1010 log10(density_xy[x].direction[i].red+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +00001011 channel_features[GreenPixelChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001012 density_xy[x].direction[i].green*
1013 log10(density_xy[x].direction[i].green+MagickEpsilon);
cristyd3090f92011-10-18 00:05:15 +00001014 channel_features[BluePixelChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001015 density_xy[x].direction[i].blue*
1016 log10(density_xy[x].direction[i].blue+MagickEpsilon);
cristyf6214de2010-01-29 02:47:41 +00001017 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001018 channel_features[BlackPixelChannel].difference_entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +00001019 density_xy[x].direction[i].black*
1020 log10(density_xy[x].direction[i].black+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +00001021 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001022 channel_features[AlphaPixelChannel].difference_entropy[i]-=
cristy4c08aed2011-07-01 19:47:50 +00001023 density_xy[x].direction[i].alpha*
1024 log10(density_xy[x].direction[i].alpha+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +00001025 /*
1026 Information Measures of Correlation.
1027 */
cristyffa10d02010-01-30 17:53:27 +00001028 entropy_x.direction[i].red-=(density_x[x].direction[i].red*
1029 log10(density_x[x].direction[i].red+MagickEpsilon));
1030 entropy_x.direction[i].green-=(density_x[x].direction[i].green*
1031 log10(density_x[x].direction[i].green+MagickEpsilon));
1032 entropy_x.direction[i].blue-=(density_x[x].direction[i].blue*
1033 log10(density_x[x].direction[i].blue+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001034 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001035 entropy_x.direction[i].black-=(density_x[x].direction[i].black*
1036 log10(density_x[x].direction[i].black+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001037 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001038 entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha*
1039 log10(density_x[x].direction[i].alpha+MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +00001040 entropy_y.direction[i].red-=(density_y[x].direction[i].red*
1041 log10(density_y[x].direction[i].red+MagickEpsilon));
1042 entropy_y.direction[i].green-=(density_y[x].direction[i].green*
1043 log10(density_y[x].direction[i].green+MagickEpsilon));
1044 entropy_y.direction[i].blue-=(density_y[x].direction[i].blue*
1045 log10(density_y[x].direction[i].blue+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001046 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001047 entropy_y.direction[i].black-=(density_y[x].direction[i].black*
1048 log10(density_y[x].direction[i].black+MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +00001049 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001050 entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha*
1051 log10(density_y[x].direction[i].alpha+MagickEpsilon));
cristye1897792010-01-29 02:05:50 +00001052 }
cristyf6214de2010-01-29 02:47:41 +00001053 /*
1054 Difference variance.
1055 */
cristyd3090f92011-10-18 00:05:15 +00001056 channel_features[RedPixelChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001057 (((double) number_grays*number_grays*sum_squares.direction[i].red)-
cristye1897792010-01-29 02:05:50 +00001058 (variance.direction[i].red*variance.direction[i].red))/
1059 ((double) number_grays*number_grays*number_grays*number_grays);
cristyd3090f92011-10-18 00:05:15 +00001060 channel_features[GreenPixelChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001061 (((double) number_grays*number_grays*sum_squares.direction[i].green)-
cristye1897792010-01-29 02:05:50 +00001062 (variance.direction[i].green*variance.direction[i].green))/
1063 ((double) number_grays*number_grays*number_grays*number_grays);
cristyd3090f92011-10-18 00:05:15 +00001064 channel_features[BluePixelChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001065 (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
cristye1897792010-01-29 02:05:50 +00001066 (variance.direction[i].blue*variance.direction[i].blue))/
1067 ((double) number_grays*number_grays*number_grays*number_grays);
cristy4c08aed2011-07-01 19:47:50 +00001068 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001069 channel_features[BlackPixelChannel].difference_variance[i]=
cristy4c08aed2011-07-01 19:47:50 +00001070 (((double) number_grays*number_grays*sum_squares.direction[i].black)-
1071 (variance.direction[i].black*variance.direction[i].black))/
1072 ((double) number_grays*number_grays*number_grays*number_grays);
cristye1897792010-01-29 02:05:50 +00001073 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001074 channel_features[AlphaPixelChannel].difference_variance[i]=
cristy4c08aed2011-07-01 19:47:50 +00001075 (((double) number_grays*number_grays*sum_squares.direction[i].alpha)-
1076 (variance.direction[i].alpha*variance.direction[i].alpha))/
cristye1897792010-01-29 02:05:50 +00001077 ((double) number_grays*number_grays*number_grays*number_grays);
cristye0acabf2010-01-30 00:52:38 +00001078 /*
1079 Information Measures of Correlation.
1080 */
cristyd3090f92011-10-18 00:05:15 +00001081 channel_features[RedPixelChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001082 (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/
1083 (entropy_x.direction[i].red > entropy_y.direction[i].red ?
1084 entropy_x.direction[i].red : entropy_y.direction[i].red);
cristyd3090f92011-10-18 00:05:15 +00001085 channel_features[GreenPixelChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001086 (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/
1087 (entropy_x.direction[i].green > entropy_y.direction[i].green ?
1088 entropy_x.direction[i].green : entropy_y.direction[i].green);
cristyd3090f92011-10-18 00:05:15 +00001089 channel_features[BluePixelChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001090 (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/
1091 (entropy_x.direction[i].blue > entropy_y.direction[i].blue ?
1092 entropy_x.direction[i].blue : entropy_y.direction[i].blue);
cristye0acabf2010-01-30 00:52:38 +00001093 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001094 channel_features[BlackPixelChannel].measure_of_correlation_1[i]=
cristy4c08aed2011-07-01 19:47:50 +00001095 (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/
1096 (entropy_x.direction[i].black > entropy_y.direction[i].black ?
1097 entropy_x.direction[i].black : entropy_y.direction[i].black);
cristy53a727d2011-06-16 14:53:56 +00001098 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001099 channel_features[AlphaPixelChannel].measure_of_correlation_1[i]=
cristy4c08aed2011-07-01 19:47:50 +00001100 (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/
1101 (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ?
1102 entropy_x.direction[i].alpha : entropy_y.direction[i].alpha);
cristyd3090f92011-10-18 00:05:15 +00001103 channel_features[RedPixelChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001104 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].red-
1105 entropy_xy.direction[i].red)))));
cristyd3090f92011-10-18 00:05:15 +00001106 channel_features[GreenPixelChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001107 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].green-
1108 entropy_xy.direction[i].green)))));
cristyd3090f92011-10-18 00:05:15 +00001109 channel_features[BluePixelChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001110 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].blue-
1111 entropy_xy.direction[i].blue)))));
cristye0acabf2010-01-30 00:52:38 +00001112 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001113 channel_features[BlackPixelChannel].measure_of_correlation_2[i]=
cristy4c08aed2011-07-01 19:47:50 +00001114 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].black-
1115 entropy_xy.direction[i].black)))));
cristy53a727d2011-06-16 14:53:56 +00001116 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001117 channel_features[AlphaPixelChannel].measure_of_correlation_2[i]=
cristy4c08aed2011-07-01 19:47:50 +00001118 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].alpha-
1119 entropy_xy.direction[i].alpha)))));
cristye1897792010-01-29 02:05:50 +00001120 }
1121 /*
1122 Compute more texture features.
1123 */
cristy3a82f252010-01-26 20:31:51 +00001124#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001125 #pragma omp parallel for schedule(static,4) shared(status)
cristy3a82f252010-01-26 20:31:51 +00001126#endif
cristybd822072010-01-27 00:30:00 +00001127 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +00001128 {
cristy564a5692012-01-20 23:56:26 +00001129 ssize_t
1130 z;
1131
cristybb503372010-05-27 20:51:26 +00001132 for (z=0; z < (ssize_t) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +00001133 {
cristybb503372010-05-27 20:51:26 +00001134 register ssize_t
cristybd822072010-01-27 00:30:00 +00001135 y;
cristy3a82f252010-01-26 20:31:51 +00001136
cristy7396d882010-01-27 02:37:56 +00001137 ChannelStatistics
cristybd822072010-01-27 00:30:00 +00001138 pixel;
1139
1140 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristybb503372010-05-27 20:51:26 +00001141 for (y=0; y < (ssize_t) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +00001142 {
cristybb503372010-05-27 20:51:26 +00001143 register ssize_t
cristybd822072010-01-27 00:30:00 +00001144 x;
1145
cristybb503372010-05-27 20:51:26 +00001146 for (x=0; x < (ssize_t) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +00001147 {
1148 /*
1149 Contrast: amount of local variations present in an image.
1150 */
1151 if (((y-x) == z) || ((x-y) == z))
1152 {
cristy7396d882010-01-27 02:37:56 +00001153 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
1154 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
1155 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +00001156 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001157 pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
cristy3a82f252010-01-26 20:31:51 +00001158 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001159 pixel.direction[i].alpha+=
1160 cooccurrence[x][y].direction[i].alpha;
cristy3a82f252010-01-26 20:31:51 +00001161 }
cristyffa10d02010-01-30 17:53:27 +00001162 /*
1163 Maximum Correlation Coefficient.
1164 */
1165 Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
1166 cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
1167 density_y[x].direction[i].red;
1168 Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
1169 cooccurrence[y][x].direction[i].green/
1170 density_x[z].direction[i].green/density_y[x].direction[i].red;
1171 Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
1172 cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
1173 density_y[x].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +00001174 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001175 Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
1176 cooccurrence[y][x].direction[i].black/
1177 density_x[z].direction[i].black/density_y[x].direction[i].black;
cristyffa10d02010-01-30 17:53:27 +00001178 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001179 Q[z][y].direction[i].alpha+=
1180 cooccurrence[z][x].direction[i].alpha*
1181 cooccurrence[y][x].direction[i].alpha/
1182 density_x[z].direction[i].alpha/
1183 density_y[x].direction[i].alpha;
cristy3a82f252010-01-26 20:31:51 +00001184 }
1185 }
cristyd3090f92011-10-18 00:05:15 +00001186 channel_features[RedPixelChannel].contrast[i]+=z*z*
1187 pixel.direction[i].red;
1188 channel_features[GreenPixelChannel].contrast[i]+=z*z*
1189 pixel.direction[i].green;
1190 channel_features[BluePixelChannel].contrast[i]+=z*z*
1191 pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +00001192 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001193 channel_features[BlackPixelChannel].contrast[i]+=z*z*
cristy4c08aed2011-07-01 19:47:50 +00001194 pixel.direction[i].black;
cristy53a727d2011-06-16 14:53:56 +00001195 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001196 channel_features[AlphaPixelChannel].contrast[i]+=z*z*
cristy4c08aed2011-07-01 19:47:50 +00001197 pixel.direction[i].alpha;
cristy3a82f252010-01-26 20:31:51 +00001198 }
cristye0acabf2010-01-30 00:52:38 +00001199 /*
1200 Maximum Correlation Coefficient.
cristyffa10d02010-01-30 17:53:27 +00001201 Future: return second largest eigenvalue of Q.
cristye0acabf2010-01-30 00:52:38 +00001202 */
cristyd3090f92011-10-18 00:05:15 +00001203 channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
cristye0acabf2010-01-30 00:52:38 +00001204 sqrt((double) -1.0);
cristyd3090f92011-10-18 00:05:15 +00001205 channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
cristye0acabf2010-01-30 00:52:38 +00001206 sqrt((double) -1.0);
cristyd3090f92011-10-18 00:05:15 +00001207 channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
cristye0acabf2010-01-30 00:52:38 +00001208 sqrt((double) -1.0);
cristye0acabf2010-01-30 00:52:38 +00001209 if (image->colorspace == CMYKColorspace)
cristyd3090f92011-10-18 00:05:15 +00001210 channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
cristye0acabf2010-01-30 00:52:38 +00001211 sqrt((double) -1.0);
cristy53a727d2011-06-16 14:53:56 +00001212 if (image->matte != MagickFalse)
cristyd3090f92011-10-18 00:05:15 +00001213 channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
cristy53a727d2011-06-16 14:53:56 +00001214 sqrt((double) -1.0);
cristy3a82f252010-01-26 20:31:51 +00001215 }
cristy7e9726d2010-01-26 02:08:40 +00001216 /*
cristyf2bf2c72010-01-25 19:54:15 +00001217 Relinquish resources.
1218 */
cristy7396d882010-01-27 02:37:56 +00001219 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
cristybb503372010-05-27 20:51:26 +00001220 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +00001221 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
1222 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
1223 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
1224 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
1225 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
cristybb503372010-05-27 20:51:26 +00001226 for (i=0; i < (ssize_t) number_grays; i++)
cristy7396d882010-01-27 02:37:56 +00001227 cooccurrence[i]=(ChannelStatistics *)
1228 RelinquishMagickMemory(cooccurrence[i]);
1229 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristy3e2860c2010-01-24 01:36:30 +00001230 return(channel_features);
1231}