blob: 57c3a1c23cf9492344f5d25bac6b030b31af476e [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
43#include "magick/studio.h"
44#include "magick/property.h"
45#include "magick/animate.h"
46#include "magick/blob.h"
47#include "magick/blob-private.h"
48#include "magick/cache.h"
49#include "magick/cache-private.h"
50#include "magick/cache-view.h"
51#include "magick/client.h"
52#include "magick/color.h"
53#include "magick/color-private.h"
54#include "magick/colorspace.h"
55#include "magick/colorspace-private.h"
56#include "magick/composite.h"
57#include "magick/composite-private.h"
58#include "magick/compress.h"
59#include "magick/constitute.h"
60#include "magick/deprecate.h"
61#include "magick/display.h"
62#include "magick/draw.h"
63#include "magick/enhance.h"
64#include "magick/exception.h"
65#include "magick/exception-private.h"
66#include "magick/feature.h"
67#include "magick/gem.h"
68#include "magick/geometry.h"
69#include "magick/list.h"
70#include "magick/image-private.h"
71#include "magick/magic.h"
72#include "magick/magick.h"
73#include "magick/memory_.h"
74#include "magick/module.h"
75#include "magick/monitor.h"
76#include "magick/monitor-private.h"
77#include "magick/option.h"
78#include "magick/paint.h"
79#include "magick/pixel-private.h"
80#include "magick/profile.h"
81#include "magick/quantize.h"
82#include "magick/random_.h"
83#include "magick/segment.h"
84#include "magick/semaphore.h"
85#include "magick/signature-private.h"
86#include "magick/string_.h"
87#include "magick/thread-private.h"
88#include "magick/timer.h"
89#include "magick/utility.h"
90#include "magick/version.h"
91
92/*
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94% %
95% %
96% %
97% G e t I m a g e C h a n n e l F e a t u r e s %
98% %
99% %
100% %
101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102%
cristy7396d882010-01-27 02:37:56 +0000103% GetImageChannelFeatures() returns features for each channel in the image in
104% 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%
cristy0c1c3fd2011-01-18 15:53:10 +0000111% channel_features=GetImageChannelFeatures(image,1,exception);
cristy549a37e2010-01-26 15:24:15 +0000112% contrast=channel_features[RedChannel].contrast[0];
cristy3e2860c2010-01-24 01:36:30 +0000113%
114% Use MagickRelinquishMemory() to free the features buffer.
115%
116% The format of the GetImageChannelFeatures method is:
117%
118% ChannelFeatures *GetImageChannelFeatures(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
cristy3e2860c2010-01-24 01:36:30 +0000138MagickExport ChannelFeatures *GetImageChannelFeatures(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 {
cristy549a37e2010-01-26 15:24:15 +0000143 DoublePixelPacket
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
cristy2070fa52010-01-24 03:17:57 +0000170 LongPixelPacket
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
184 y,
185 z;
186
cristyecd0ab52010-05-30 14:59:20 +0000187 unsigned int
cristy7396d882010-01-27 02:37:56 +0000188 number_grays;
cristyf2bf2c72010-01-25 19:54:15 +0000189
cristy3e2860c2010-01-24 01:36:30 +0000190 assert(image != (Image *) NULL);
191 assert(image->signature == MagickSignature);
192 if (image->debug != MagickFalse)
193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000194 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
195 return((ChannelFeatures *) NULL);
cristy9a9230e2011-04-26 14:56:14 +0000196 length=CompositeChannels+1UL;
cristy3e2860c2010-01-24 01:36:30 +0000197 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
198 sizeof(*channel_features));
199 if (channel_features == (ChannelFeatures *) NULL)
200 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
201 (void) ResetMagickMemory(channel_features,0,length*
202 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000203 /*
cristy7396d882010-01-27 02:37:56 +0000204 Form grays.
cristy2070fa52010-01-24 03:17:57 +0000205 */
cristy7396d882010-01-27 02:37:56 +0000206 grays=(LongPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
207 if (grays == (LongPixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000208 {
cristy2070fa52010-01-24 03:17:57 +0000209 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
210 channel_features);
cristye1897792010-01-29 02:05:50 +0000211 (void) ThrowMagickException(exception,GetMagickModule(),
212 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy2070fa52010-01-24 03:17:57 +0000213 return(channel_features);
214 }
cristybb503372010-05-27 20:51:26 +0000215 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy2070fa52010-01-24 03:17:57 +0000216 {
cristyecd0ab52010-05-30 14:59:20 +0000217 grays[i].red=(~0U);
218 grays[i].green=(~0U);
219 grays[i].blue=(~0U);
220 grays[i].opacity=(~0U);
221 grays[i].index=(~0U);
cristy2070fa52010-01-24 03:17:57 +0000222 }
223 status=MagickTrue;
224 image_view=AcquireCacheView(image);
225#if defined(MAGICKCORE_OPENMP_SUPPORT)
226 #pragma omp parallel for schedule(dynamic,4) shared(status)
227#endif
cristybb503372010-05-27 20:51:26 +0000228 for (y=0; y < (ssize_t) image->rows; y++)
cristy3e2860c2010-01-24 01:36:30 +0000229 {
230 register const IndexPacket
231 *restrict indexes;
232
233 register const PixelPacket
234 *restrict p;
235
cristybb503372010-05-27 20:51:26 +0000236 register ssize_t
cristy3e2860c2010-01-24 01:36:30 +0000237 x;
238
cristy2070fa52010-01-24 03:17:57 +0000239 if (status == MagickFalse)
240 continue;
241 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3e2860c2010-01-24 01:36:30 +0000242 if (p == (const PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000243 {
244 status=MagickFalse;
245 continue;
246 }
247 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000248 for (x=0; x < (ssize_t) image->columns; x++)
cristy3e2860c2010-01-24 01:36:30 +0000249 {
cristyd05ecd12011-04-22 20:44:42 +0000250 grays[ScaleQuantumToMap(GetRedPixelComponent(p))].red=
251 ScaleQuantumToMap(GetRedPixelComponent(p));
252 grays[ScaleQuantumToMap(GetGreenPixelComponent(p))].green=
253 ScaleQuantumToMap(GetGreenPixelComponent(p));
254 grays[ScaleQuantumToMap(GetBluePixelComponent(p))].blue=
255 ScaleQuantumToMap(GetBluePixelComponent(p));
cristy2070fa52010-01-24 03:17:57 +0000256 if (image->colorspace == CMYKColorspace)
cristybaacd172011-04-30 23:11:03 +0000257 grays[ScaleQuantumToMap(GetIndexPixelComponent(indexes+x))].index=
258 ScaleQuantumToMap(GetIndexPixelComponent(indexes+x));
cristy53a727d2011-06-16 14:53:56 +0000259 if (image->matte != MagickFalse)
260 grays[ScaleQuantumToMap(GetOpacityPixelComponent(p))].opacity=
261 ScaleQuantumToMap(GetOpacityPixelComponent(p));
cristy3e2860c2010-01-24 01:36:30 +0000262 p++;
263 }
264 }
cristy30c510a2010-01-24 03:43:00 +0000265 image_view=DestroyCacheView(image_view);
266 if (status == MagickFalse)
267 {
cristy7396d882010-01-27 02:37:56 +0000268 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000269 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
270 channel_features);
271 return(channel_features);
272 }
cristy7396d882010-01-27 02:37:56 +0000273 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristybb503372010-05-27 20:51:26 +0000274 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy2070fa52010-01-24 03:17:57 +0000275 {
cristyecd0ab52010-05-30 14:59:20 +0000276 if (grays[i].red != ~0U)
cristy53a727d2011-06-16 14:53:56 +0000277 grays[(ssize_t) gray.red++].red=grays[i].red;
cristyecd0ab52010-05-30 14:59:20 +0000278 if (grays[i].green != ~0U)
cristy53a727d2011-06-16 14:53:56 +0000279 grays[(ssize_t) gray.green++].green=grays[i].green;
cristyecd0ab52010-05-30 14:59:20 +0000280 if (grays[i].blue != ~0U)
cristy53a727d2011-06-16 14:53:56 +0000281 grays[(ssize_t) gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000282 if (image->colorspace == CMYKColorspace)
cristyecd0ab52010-05-30 14:59:20 +0000283 if (grays[i].index != ~0U)
cristy53a727d2011-06-16 14:53:56 +0000284 grays[(ssize_t) gray.index++].index=grays[i].index;
285 if (image->matte != MagickFalse)
286 if (grays[i].opacity != ~0U)
287 grays[(ssize_t) gray.opacity++].opacity=grays[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000288 }
cristyf2bf2c72010-01-25 19:54:15 +0000289 /*
290 Allocate spatial dependence matrix.
291 */
cristy7396d882010-01-27 02:37:56 +0000292 number_grays=gray.red;
293 if (gray.green > number_grays)
294 number_grays=gray.green;
295 if (gray.blue > number_grays)
296 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000297 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000298 if (gray.index > number_grays)
299 number_grays=gray.index;
cristy53a727d2011-06-16 14:53:56 +0000300 if (image->matte != MagickFalse)
301 if (gray.opacity > number_grays)
302 number_grays=gray.opacity;
cristy7396d882010-01-27 02:37:56 +0000303 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
304 sizeof(*cooccurrence));
cristy77173e52010-02-02 02:51:35 +0000305 density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000306 sizeof(*density_x));
307 density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
308 sizeof(*density_xy));
cristy77173e52010-02-02 02:51:35 +0000309 density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000310 sizeof(*density_y));
311 Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
312 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
313 if ((cooccurrence == (ChannelStatistics **) NULL) ||
314 (density_x == (ChannelStatistics *) NULL) ||
315 (density_xy == (ChannelStatistics *) NULL) ||
316 (density_y == (ChannelStatistics *) NULL) ||
317 (Q == (ChannelStatistics **) NULL) ||
318 (sum == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000319 {
cristyffa10d02010-01-30 17:53:27 +0000320 if (Q != (ChannelStatistics **) NULL)
321 {
cristybb503372010-05-27 20:51:26 +0000322 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000323 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
324 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
325 }
326 if (sum != (ChannelStatistics *) NULL)
327 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
328 if (density_y != (ChannelStatistics *) NULL)
329 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
330 if (density_xy != (ChannelStatistics *) NULL)
331 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
332 if (density_x != (ChannelStatistics *) NULL)
333 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
334 if (cooccurrence != (ChannelStatistics **) NULL)
335 {
cristybb503372010-05-27 20:51:26 +0000336 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000337 cooccurrence[i]=(ChannelStatistics *)
338 RelinquishMagickMemory(cooccurrence[i]);
339 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
340 cooccurrence);
341 }
cristy7396d882010-01-27 02:37:56 +0000342 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000343 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
344 channel_features);
cristye1897792010-01-29 02:05:50 +0000345 (void) ThrowMagickException(exception,GetMagickModule(),
346 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000347 return(channel_features);
348 }
cristyffa10d02010-01-30 17:53:27 +0000349 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
cristy77173e52010-02-02 02:51:35 +0000350 (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
351 (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
352 (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
cristyffa10d02010-01-30 17:53:27 +0000353 (void) ResetMagickMemory(&mean,0,sizeof(mean));
354 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
355 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
356 (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
357 (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
358 (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
359 (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
360 (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
361 (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
362 (void) ResetMagickMemory(&variance,0,sizeof(variance));
cristybb503372010-05-27 20:51:26 +0000363 for (i=0; i < (ssize_t) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000364 {
cristy7396d882010-01-27 02:37:56 +0000365 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
366 sizeof(**cooccurrence));
cristyffa10d02010-01-30 17:53:27 +0000367 Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
368 if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
369 (Q[i] == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000370 break;
cristy7396d882010-01-27 02:37:56 +0000371 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
cristy3749be42010-02-02 02:46:51 +0000372 sizeof(**cooccurrence));
373 (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
cristyf2bf2c72010-01-25 19:54:15 +0000374 }
cristybb503372010-05-27 20:51:26 +0000375 if (i < (ssize_t) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000376 {
cristyf2bf2c72010-01-25 19:54:15 +0000377 for (i--; i >= 0; i--)
cristyffa10d02010-01-30 17:53:27 +0000378 {
379 if (Q[i] != (ChannelStatistics *) NULL)
380 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
381 if (cooccurrence[i] != (ChannelStatistics *) NULL)
382 cooccurrence[i]=(ChannelStatistics *)
383 RelinquishMagickMemory(cooccurrence[i]);
384 }
385 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
cristy7396d882010-01-27 02:37:56 +0000386 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyffa10d02010-01-30 17:53:27 +0000387 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
388 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
389 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
390 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
cristy7396d882010-01-27 02:37:56 +0000391 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000392 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
393 channel_features);
cristye1897792010-01-29 02:05:50 +0000394 (void) ThrowMagickException(exception,GetMagickModule(),
395 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000396 return(channel_features);
397 }
398 /*
399 Initialize spatial dependence matrix.
400 */
401 status=MagickTrue;
402 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000403 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2bf2c72010-01-25 19:54:15 +0000404 {
cristyf2bf2c72010-01-25 19:54:15 +0000405 register const IndexPacket
406 *restrict indexes;
407
408 register const PixelPacket
409 *restrict p;
410
cristybb503372010-05-27 20:51:26 +0000411 register ssize_t
cristyf2bf2c72010-01-25 19:54:15 +0000412 x;
413
cristy7e9726d2010-01-26 02:08:40 +0000414 ssize_t
cristy53a727d2011-06-16 14:53:56 +0000415 i,
cristy9d314ff2011-03-09 01:30:28 +0000416 offset,
417 u,
418 v;
cristy7e9726d2010-01-26 02:08:40 +0000419
cristyf2bf2c72010-01-25 19:54:15 +0000420 if (status == MagickFalse)
421 continue;
cristybb503372010-05-27 20:51:26 +0000422 p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,y,image->columns+
cristy53a727d2011-06-16 14:53:56 +0000423 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000424 if (p == (const PixelPacket *) NULL)
425 {
426 status=MagickFalse;
427 continue;
428 }
429 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000430 p+=distance;
431 indexes+=distance;
cristybb503372010-05-27 20:51:26 +0000432 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2bf2c72010-01-25 19:54:15 +0000433 {
434 for (i=0; i < 4; i++)
435 {
cristy7e9726d2010-01-26 02:08:40 +0000436 switch (i)
437 {
438 case 0:
cristy549a37e2010-01-26 15:24:15 +0000439 default:
cristy7e9726d2010-01-26 02:08:40 +0000440 {
441 /*
cristy7396d882010-01-27 02:37:56 +0000442 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000443 */
444 offset=(ssize_t) distance;
445 break;
446 }
447 case 1:
448 {
449 /*
cristy7396d882010-01-27 02:37:56 +0000450 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000451 */
cristy7396d882010-01-27 02:37:56 +0000452 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000453 break;
454 }
455 case 2:
456 {
457 /*
cristy7396d882010-01-27 02:37:56 +0000458 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000459 */
cristyd99b0962010-05-29 23:14:26 +0000460 offset=(ssize_t) ((image->columns+2*distance)-distance);
cristy7e9726d2010-01-26 02:08:40 +0000461 break;
462 }
463 case 3:
464 {
465 /*
cristy7396d882010-01-27 02:37:56 +0000466 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000467 */
cristyd99b0962010-05-29 23:14:26 +0000468 offset=(ssize_t) ((image->columns+2*distance)+distance);
cristy7e9726d2010-01-26 02:08:40 +0000469 break;
470 }
471 }
472 u=0;
473 v=0;
cristyd05ecd12011-04-22 20:44:42 +0000474 while (grays[u].red != ScaleQuantumToMap(GetRedPixelComponent(p)))
cristy7e9726d2010-01-26 02:08:40 +0000475 u++;
cristybaacd172011-04-30 23:11:03 +0000476 while (grays[v].red != ScaleQuantumToMap(GetRedPixelComponent(p+offset)))
cristy7e9726d2010-01-26 02:08:40 +0000477 v++;
cristy7396d882010-01-27 02:37:56 +0000478 cooccurrence[u][v].direction[i].red++;
479 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000480 u=0;
481 v=0;
cristyd05ecd12011-04-22 20:44:42 +0000482 while (grays[u].green != ScaleQuantumToMap(GetGreenPixelComponent(p)))
cristy7e9726d2010-01-26 02:08:40 +0000483 u++;
cristybaacd172011-04-30 23:11:03 +0000484 while (grays[v].green != ScaleQuantumToMap(GetGreenPixelComponent(p+offset)))
cristy7e9726d2010-01-26 02:08:40 +0000485 v++;
cristy7396d882010-01-27 02:37:56 +0000486 cooccurrence[u][v].direction[i].green++;
487 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000488 u=0;
489 v=0;
cristyd05ecd12011-04-22 20:44:42 +0000490 while (grays[u].blue != ScaleQuantumToMap(GetBluePixelComponent(p)))
cristy7e9726d2010-01-26 02:08:40 +0000491 u++;
cristy7396d882010-01-27 02:37:56 +0000492 while (grays[v].blue != ScaleQuantumToMap((p+offset)->blue))
cristy7e9726d2010-01-26 02:08:40 +0000493 v++;
cristy7396d882010-01-27 02:37:56 +0000494 cooccurrence[u][v].direction[i].blue++;
495 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000496 if (image->colorspace == CMYKColorspace)
497 {
498 u=0;
499 v=0;
cristybaacd172011-04-30 23:11:03 +0000500 while (grays[u].index != ScaleQuantumToMap(GetIndexPixelComponent(indexes+x)))
cristy7e9726d2010-01-26 02:08:40 +0000501 u++;
cristybaacd172011-04-30 23:11:03 +0000502 while (grays[v].index != ScaleQuantumToMap(GetIndexPixelComponent(indexes+x+offset)))
cristy7e9726d2010-01-26 02:08:40 +0000503 v++;
cristy7396d882010-01-27 02:37:56 +0000504 cooccurrence[u][v].direction[i].index++;
505 cooccurrence[v][u].direction[i].index++;
cristy7e9726d2010-01-26 02:08:40 +0000506 }
cristy53a727d2011-06-16 14:53:56 +0000507 if (image->matte != MagickFalse)
508 {
509 u=0;
510 v=0;
511 while (grays[u].opacity != ScaleQuantumToMap(GetOpacityPixelComponent(p)))
512 u++;
513 while (grays[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
514 v++;
515 cooccurrence[u][v].direction[i].opacity++;
516 cooccurrence[v][u].direction[i].opacity++;
517 }
cristyf2bf2c72010-01-25 19:54:15 +0000518 }
cristy7e9726d2010-01-26 02:08:40 +0000519 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000520 }
521 }
cristyffa10d02010-01-30 17:53:27 +0000522 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000523 image_view=DestroyCacheView(image_view);
524 if (status == MagickFalse)
525 {
cristybb503372010-05-27 20:51:26 +0000526 for (i=0; i < (ssize_t) number_grays; i++)
cristy7396d882010-01-27 02:37:56 +0000527 cooccurrence[i]=(ChannelStatistics *)
528 RelinquishMagickMemory(cooccurrence[i]);
529 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyf2bf2c72010-01-25 19:54:15 +0000530 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
531 channel_features);
cristye1897792010-01-29 02:05:50 +0000532 (void) ThrowMagickException(exception,GetMagickModule(),
533 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000534 return(channel_features);
535 }
536 /*
cristy7e9726d2010-01-26 02:08:40 +0000537 Normalize spatial dependence matrix.
538 */
cristybd822072010-01-27 00:30:00 +0000539 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000540 {
cristy549a37e2010-01-26 15:24:15 +0000541 double
542 normalize;
543
cristy53a727d2011-06-16 14:53:56 +0000544 register ssize_t
545 y;
546
cristybd822072010-01-27 00:30:00 +0000547 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000548 {
cristybd822072010-01-27 00:30:00 +0000549 case 0:
550 default:
cristy7e9726d2010-01-26 02:08:40 +0000551 {
cristybd822072010-01-27 00:30:00 +0000552 /*
cristy7396d882010-01-27 02:37:56 +0000553 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000554 */
555 normalize=2.0*image->rows*(image->columns-distance);
556 break;
557 }
558 case 1:
559 {
560 /*
cristy7396d882010-01-27 02:37:56 +0000561 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000562 */
cristy7396d882010-01-27 02:37:56 +0000563 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000564 break;
565 }
566 case 2:
567 {
568 /*
cristy7396d882010-01-27 02:37:56 +0000569 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000570 */
cristy7396d882010-01-27 02:37:56 +0000571 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000572 break;
573 }
574 case 3:
575 {
576 /*
cristy7396d882010-01-27 02:37:56 +0000577 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000578 */
579 normalize=2.0*(image->rows-distance)*(image->columns-distance);
580 break;
581 }
582 }
cristy53a727d2011-06-16 14:53:56 +0000583 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 : normalize);
cristybb503372010-05-27 20:51:26 +0000584 for (y=0; y < (ssize_t) number_grays; y++)
cristybd822072010-01-27 00:30:00 +0000585 {
cristybb503372010-05-27 20:51:26 +0000586 register ssize_t
cristybd822072010-01-27 00:30:00 +0000587 x;
588
cristybb503372010-05-27 20:51:26 +0000589 for (x=0; x < (ssize_t) number_grays; x++)
cristybd822072010-01-27 00:30:00 +0000590 {
cristy53a727d2011-06-16 14:53:56 +0000591 cooccurrence[x][y].direction[i].red*=normalize;
592 cooccurrence[x][y].direction[i].green*=normalize;
593 cooccurrence[x][y].direction[i].blue*=normalize;
cristy549a37e2010-01-26 15:24:15 +0000594 if (image->colorspace == CMYKColorspace)
cristy53a727d2011-06-16 14:53:56 +0000595 cooccurrence[x][y].direction[i].index*=normalize;
596 if (image->matte != MagickFalse)
597 cooccurrence[x][y].direction[i].opacity*=normalize;
cristy549a37e2010-01-26 15:24:15 +0000598 }
599 }
600 }
601 /*
602 Compute texture features.
603 */
cristy3a82f252010-01-26 20:31:51 +0000604#if defined(MAGICKCORE_OPENMP_SUPPORT)
605 #pragma omp parallel for schedule(dynamic,4) shared(status)
606#endif
cristybd822072010-01-27 00:30:00 +0000607 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000608 {
cristybb503372010-05-27 20:51:26 +0000609 register ssize_t
cristybd822072010-01-27 00:30:00 +0000610 y;
cristy549a37e2010-01-26 15:24:15 +0000611
cristybb503372010-05-27 20:51:26 +0000612 for (y=0; y < (ssize_t) number_grays; y++)
cristy549a37e2010-01-26 15:24:15 +0000613 {
cristybb503372010-05-27 20:51:26 +0000614 register ssize_t
cristybd822072010-01-27 00:30:00 +0000615 x;
616
cristybb503372010-05-27 20:51:26 +0000617 for (x=0; x < (ssize_t) number_grays; x++)
cristy549a37e2010-01-26 15:24:15 +0000618 {
619 /*
cristy3a82f252010-01-26 20:31:51 +0000620 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000621 */
622 channel_features[RedChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000623 cooccurrence[x][y].direction[i].red*
624 cooccurrence[x][y].direction[i].red;
cristy549a37e2010-01-26 15:24:15 +0000625 channel_features[GreenChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000626 cooccurrence[x][y].direction[i].green*
627 cooccurrence[x][y].direction[i].green;
cristy549a37e2010-01-26 15:24:15 +0000628 channel_features[BlueChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000629 cooccurrence[x][y].direction[i].blue*
630 cooccurrence[x][y].direction[i].blue;
cristy549a37e2010-01-26 15:24:15 +0000631 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000632 channel_features[BlackChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000633 cooccurrence[x][y].direction[i].index*
634 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000635 if (image->matte != MagickFalse)
636 channel_features[OpacityChannel].angular_second_moment[i]+=
637 cooccurrence[x][y].direction[i].opacity*
638 cooccurrence[x][y].direction[i].opacity;
cristy7396d882010-01-27 02:37:56 +0000639 /*
640 Correlation: measure of linear-dependencies in the image.
641 */
642 sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000643 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
cristycdf8e1b2010-01-28 01:52:33 +0000644 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristycdf8e1b2010-01-28 01:52:33 +0000645 if (image->colorspace == CMYKColorspace)
646 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000647 if (image->matte != MagickFalse)
648 sum[y].direction[i].opacity+=cooccurrence[x][y].direction[i].opacity;
cristycdf8e1b2010-01-28 01:52:33 +0000649 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000650 correlation.direction[i].green+=x*y*
651 cooccurrence[x][y].direction[i].green;
cristy7396d882010-01-27 02:37:56 +0000652 correlation.direction[i].blue+=x*y*
653 cooccurrence[x][y].direction[i].blue;
cristy7396d882010-01-27 02:37:56 +0000654 if (image->colorspace == CMYKColorspace)
cristycdf8e1b2010-01-28 01:52:33 +0000655 correlation.direction[i].index+=x*y*
656 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000657 if (image->matte != MagickFalse)
658 correlation.direction[i].opacity+=x*y*
659 cooccurrence[x][y].direction[i].opacity;
cristycf5e6492010-01-28 02:45:28 +0000660 /*
661 Inverse Difference Moment.
662 */
cristy9fc6dbf2010-01-28 02:49:50 +0000663 channel_features[RedChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000664 cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
cristy9fc6dbf2010-01-28 02:49:50 +0000665 channel_features[GreenChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000666 cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
cristy9fc6dbf2010-01-28 02:49:50 +0000667 channel_features[BlueChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000668 cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
cristycf5e6492010-01-28 02:45:28 +0000669 if (image->colorspace == CMYKColorspace)
cristy9fc6dbf2010-01-28 02:49:50 +0000670 channel_features[IndexChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000671 cooccurrence[x][y].direction[i].index/((y-x)*(y-x)+1);
cristy53a727d2011-06-16 14:53:56 +0000672 if (image->matte != MagickFalse)
673 channel_features[OpacityChannel].inverse_difference_moment[i]+=
674 cooccurrence[x][y].direction[i].opacity/((y-x)*(y-x)+1);
cristye1897792010-01-29 02:05:50 +0000675 /*
676 Sum average.
677 */
cristyffa10d02010-01-30 17:53:27 +0000678 density_xy[y+x+2].direction[i].red+=
cristye1897792010-01-29 02:05:50 +0000679 cooccurrence[x][y].direction[i].red;
cristyffa10d02010-01-30 17:53:27 +0000680 density_xy[y+x+2].direction[i].green+=
cristye1897792010-01-29 02:05:50 +0000681 cooccurrence[x][y].direction[i].green;
cristyffa10d02010-01-30 17:53:27 +0000682 density_xy[y+x+2].direction[i].blue+=
cristye1897792010-01-29 02:05:50 +0000683 cooccurrence[x][y].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000684 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000685 density_xy[y+x+2].direction[i].index+=
cristye1897792010-01-29 02:05:50 +0000686 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000687 if (image->matte != MagickFalse)
688 density_xy[y+x+2].direction[i].opacity+=
689 cooccurrence[x][y].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000690 /*
691 Entropy.
692 */
693 channel_features[RedChannel].entropy[i]-=
694 cooccurrence[x][y].direction[i].red*
695 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
696 channel_features[GreenChannel].entropy[i]-=
697 cooccurrence[x][y].direction[i].green*
698 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
699 channel_features[BlueChannel].entropy[i]-=
700 cooccurrence[x][y].direction[i].blue*
701 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000702 if (image->colorspace == CMYKColorspace)
703 channel_features[IndexChannel].entropy[i]-=
704 cooccurrence[x][y].direction[i].index*
705 log10(cooccurrence[x][y].direction[i].index+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000706 if (image->matte != MagickFalse)
707 channel_features[OpacityChannel].entropy[i]-=
708 cooccurrence[x][y].direction[i].opacity*
709 log10(cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000710 /*
711 Information Measures of Correlation.
712 */
cristyffa10d02010-01-30 17:53:27 +0000713 density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
714 density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
715 density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristye0acabf2010-01-30 00:52:38 +0000716 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000717 density_x[x].direction[i].index+=
718 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000719 if (image->matte != MagickFalse)
720 density_x[x].direction[i].opacity+=
721 cooccurrence[x][y].direction[i].opacity;
cristyffa10d02010-01-30 17:53:27 +0000722 density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
723 density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
724 density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristye0acabf2010-01-30 00:52:38 +0000725 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000726 density_y[y].direction[i].index+=
727 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000728 if (image->matte != MagickFalse)
729 density_y[y].direction[i].opacity+=
730 cooccurrence[x][y].direction[i].opacity;
cristy7e9726d2010-01-26 02:08:40 +0000731 }
cristycdf8e1b2010-01-28 01:52:33 +0000732 mean.direction[i].red+=y*sum[y].direction[i].red;
733 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
734 mean.direction[i].green+=y*sum[y].direction[i].green;
735 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
736 mean.direction[i].blue+=y*sum[y].direction[i].blue;
737 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
cristycdf8e1b2010-01-28 01:52:33 +0000738 if (image->colorspace == CMYKColorspace)
739 {
740 mean.direction[i].index+=y*sum[y].direction[i].index;
741 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
742 }
cristy53a727d2011-06-16 14:53:56 +0000743 if (image->matte != MagickFalse)
744 {
745 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
746 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
747 }
cristy7e9726d2010-01-26 02:08:40 +0000748 }
cristycdf8e1b2010-01-28 01:52:33 +0000749 /*
750 Correlation: measure of linear-dependencies in the image.
751 */
752 channel_features[RedChannel].correlation[i]=
753 (correlation.direction[i].red-mean.direction[i].red*
754 mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
755 (mean.direction[i].red*mean.direction[i].red))*sqrt(
756 sum_squares.direction[i].red-(mean.direction[i].red*
757 mean.direction[i].red)));
758 channel_features[GreenChannel].correlation[i]=
759 (correlation.direction[i].green-mean.direction[i].green*
760 mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
761 (mean.direction[i].green*mean.direction[i].green))*sqrt(
762 sum_squares.direction[i].green-(mean.direction[i].green*
763 mean.direction[i].green)));
764 channel_features[BlueChannel].correlation[i]=
765 (correlation.direction[i].blue-mean.direction[i].blue*
766 mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
767 (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
768 sum_squares.direction[i].blue-(mean.direction[i].blue*
769 mean.direction[i].blue)));
cristycdf8e1b2010-01-28 01:52:33 +0000770 if (image->colorspace == CMYKColorspace)
771 channel_features[IndexChannel].correlation[i]=
772 (correlation.direction[i].index-mean.direction[i].index*
773 mean.direction[i].index)/(sqrt(sum_squares.direction[i].index-
774 (mean.direction[i].index*mean.direction[i].index))*sqrt(
775 sum_squares.direction[i].index-(mean.direction[i].index*
776 mean.direction[i].index)));
cristy53a727d2011-06-16 14:53:56 +0000777 if (image->matte != MagickFalse)
778 channel_features[OpacityChannel].correlation[i]=
779 (correlation.direction[i].opacity-mean.direction[i].opacity*
780 mean.direction[i].opacity)/(sqrt(sum_squares.direction[i].opacity-
781 (mean.direction[i].opacity*mean.direction[i].opacity))*sqrt(
782 sum_squares.direction[i].opacity-(mean.direction[i].opacity*
783 mean.direction[i].opacity)));
cristy7e9726d2010-01-26 02:08:40 +0000784 }
cristycf5e6492010-01-28 02:45:28 +0000785 /*
786 Compute more texture features.
787 */
cristye1897792010-01-29 02:05:50 +0000788#if defined(MAGICKCORE_OPENMP_SUPPORT)
789 #pragma omp parallel for schedule(dynamic,4) shared(status)
790#endif
791 for (i=0; i < 4; i++)
792 {
cristybb503372010-05-27 20:51:26 +0000793 register ssize_t
cristye1897792010-01-29 02:05:50 +0000794 x;
795
cristybb503372010-05-27 20:51:26 +0000796 for (x=2; x < (ssize_t) (2*number_grays); x++)
cristye1897792010-01-29 02:05:50 +0000797 {
798 /*
799 Sum average.
800 */
801 channel_features[RedChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000802 x*density_xy[x].direction[i].red;
cristye1897792010-01-29 02:05:50 +0000803 channel_features[GreenChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000804 x*density_xy[x].direction[i].green;
cristye1897792010-01-29 02:05:50 +0000805 channel_features[BlueChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000806 x*density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000807 if (image->colorspace == CMYKColorspace)
808 channel_features[IndexChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000809 x*density_xy[x].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000810 if (image->matte != MagickFalse)
811 channel_features[OpacityChannel].sum_average[i]+=
812 x*density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000813 /*
814 Sum entropy.
815 */
816 channel_features[RedChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000817 density_xy[x].direction[i].red*
818 log10(density_xy[x].direction[i].red+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000819 channel_features[GreenChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000820 density_xy[x].direction[i].green*
821 log10(density_xy[x].direction[i].green+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000822 channel_features[BlueChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000823 density_xy[x].direction[i].blue*
824 log10(density_xy[x].direction[i].blue+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000825 if (image->colorspace == CMYKColorspace)
826 channel_features[IndexChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000827 density_xy[x].direction[i].index*
828 log10(density_xy[x].direction[i].index+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000829 if (image->matte != MagickFalse)
830 channel_features[OpacityChannel].sum_entropy[i]-=
831 density_xy[x].direction[i].opacity*
832 log10(density_xy[x].direction[i].opacity+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000833 /*
834 Sum variance.
835 */
836 channel_features[RedChannel].sum_variance[i]+=
837 (x-channel_features[RedChannel].sum_entropy[i])*
838 (x-channel_features[RedChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000839 density_xy[x].direction[i].red;
cristye1897792010-01-29 02:05:50 +0000840 channel_features[GreenChannel].sum_variance[i]+=
841 (x-channel_features[GreenChannel].sum_entropy[i])*
842 (x-channel_features[GreenChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000843 density_xy[x].direction[i].green;
cristye1897792010-01-29 02:05:50 +0000844 channel_features[BlueChannel].sum_variance[i]+=
845 (x-channel_features[BlueChannel].sum_entropy[i])*
846 (x-channel_features[BlueChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000847 density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000848 if (image->colorspace == CMYKColorspace)
849 channel_features[IndexChannel].sum_variance[i]+=
850 (x-channel_features[IndexChannel].sum_entropy[i])*
851 (x-channel_features[IndexChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000852 density_xy[x].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000853 if (image->matte != MagickFalse)
854 channel_features[OpacityChannel].sum_variance[i]+=
855 (x-channel_features[OpacityChannel].sum_entropy[i])*
856 (x-channel_features[OpacityChannel].sum_entropy[i])*
857 density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000858 }
859 }
860 /*
861 Compute more texture features.
862 */
cristycf5e6492010-01-28 02:45:28 +0000863#if defined(MAGICKCORE_OPENMP_SUPPORT)
864 #pragma omp parallel for schedule(dynamic,4) shared(status)
865#endif
866 for (i=0; i < 4; i++)
867 {
cristybb503372010-05-27 20:51:26 +0000868 register ssize_t
cristycf5e6492010-01-28 02:45:28 +0000869 y;
870
cristybb503372010-05-27 20:51:26 +0000871 for (y=0; y < (ssize_t) number_grays; y++)
cristycf5e6492010-01-28 02:45:28 +0000872 {
cristybb503372010-05-27 20:51:26 +0000873 register ssize_t
cristycf5e6492010-01-28 02:45:28 +0000874 x;
875
cristybb503372010-05-27 20:51:26 +0000876 for (x=0; x < (ssize_t) number_grays; x++)
cristycf5e6492010-01-28 02:45:28 +0000877 {
878 /*
879 Sum of Squares: Variance
880 */
881 variance.direction[i].red+=(y-mean.direction[i].red+1)*
882 (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
883 variance.direction[i].green+=(y-mean.direction[i].green+1)*
884 (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
885 variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
886 (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +0000887 if (image->colorspace == CMYKColorspace)
888 variance.direction[i].index+=(y-mean.direction[i].index+1)*
889 (y-mean.direction[i].index+1)*cooccurrence[x][y].direction[i].index;
cristycf5e6492010-01-28 02:45:28 +0000890 if (image->matte != MagickFalse)
891 variance.direction[i].opacity+=(y-mean.direction[i].opacity+1)*
892 (y-mean.direction[i].opacity+1)*
893 cooccurrence[x][y].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000894 /*
cristye0acabf2010-01-30 00:52:38 +0000895 Sum average / Difference Variance.
cristye1897792010-01-29 02:05:50 +0000896 */
cristyffa10d02010-01-30 17:53:27 +0000897 density_xy[MagickAbsoluteValue(y-x)].direction[i].red+=
cristye1897792010-01-29 02:05:50 +0000898 cooccurrence[x][y].direction[i].red;
cristyffa10d02010-01-30 17:53:27 +0000899 density_xy[MagickAbsoluteValue(y-x)].direction[i].green+=
cristye1897792010-01-29 02:05:50 +0000900 cooccurrence[x][y].direction[i].green;
cristyffa10d02010-01-30 17:53:27 +0000901 density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+=
cristye1897792010-01-29 02:05:50 +0000902 cooccurrence[x][y].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000903 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000904 density_xy[MagickAbsoluteValue(y-x)].direction[i].index+=
cristye1897792010-01-29 02:05:50 +0000905 cooccurrence[x][y].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000906 if (image->matte != MagickFalse)
907 density_xy[MagickAbsoluteValue(y-x)].direction[i].opacity+=
908 cooccurrence[x][y].direction[i].opacity;
cristye0acabf2010-01-30 00:52:38 +0000909 /*
910 Information Measures of Correlation.
911 */
cristyffa10d02010-01-30 17:53:27 +0000912 entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
cristye0acabf2010-01-30 00:52:38 +0000913 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000914 entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
cristye0acabf2010-01-30 00:52:38 +0000915 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000916 entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
cristye0acabf2010-01-30 00:52:38 +0000917 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +0000918 if (image->colorspace == CMYKColorspace)
919 entropy_xy.direction[i].index-=cooccurrence[x][y].direction[i].index*
920 log10(cooccurrence[x][y].direction[i].index+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000921 if (image->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000922 entropy_xy.direction[i].opacity-=
cristye0acabf2010-01-30 00:52:38 +0000923 cooccurrence[x][y].direction[i].opacity*log10(
cristyffa10d02010-01-30 17:53:27 +0000924 cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
cristyffa10d02010-01-30 17:53:27 +0000925 entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
926 log10(density_x[x].direction[i].red*density_y[y].direction[i].red+
927 MagickEpsilon));
928 entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
929 log10(density_x[x].direction[i].green*density_y[y].direction[i].green+
930 MagickEpsilon));
931 entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
932 log10(density_x[x].direction[i].blue*density_y[y].direction[i].blue+
933 MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +0000934 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000935 entropy_xy1.direction[i].index-=(
936 cooccurrence[x][y].direction[i].index*log10(
937 density_x[x].direction[i].index*density_y[y].direction[i].index+
938 MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +0000939 if (image->matte != MagickFalse)
940 entropy_xy1.direction[i].opacity-=(
941 cooccurrence[x][y].direction[i].opacity*log10(
942 density_x[x].direction[i].opacity*density_y[y].direction[i].opacity+
943 MagickEpsilon));
cristyffa10d02010-01-30 17:53:27 +0000944 entropy_xy2.direction[i].red-=(density_x[x].direction[i].red*
945 density_y[y].direction[i].red*log10(density_x[x].direction[i].red*
946 density_y[y].direction[i].red+MagickEpsilon));
947 entropy_xy2.direction[i].green-=(density_x[x].direction[i].green*
948 density_y[y].direction[i].green*log10(density_x[x].direction[i].green*
949 density_y[y].direction[i].green+MagickEpsilon));
950 entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue*
951 density_y[y].direction[i].blue*log10(density_x[x].direction[i].blue*
952 density_y[y].direction[i].blue+MagickEpsilon));
cristyffa10d02010-01-30 17:53:27 +0000953 if (image->colorspace == CMYKColorspace)
954 entropy_xy2.direction[i].index-=(density_x[x].direction[i].index*
955 density_y[y].direction[i].index*log10(
956 density_x[x].direction[i].index*density_y[y].direction[i].index+
957 MagickEpsilon));
cristy53a727d2011-06-16 14:53:56 +0000958 if (image->matte != MagickFalse)
959 entropy_xy2.direction[i].opacity-=(density_x[x].direction[i].opacity*
960 density_y[y].direction[i].opacity*log10(
961 density_x[x].direction[i].opacity*density_y[y].direction[i].opacity+
962 MagickEpsilon));
cristycf5e6492010-01-28 02:45:28 +0000963 }
964 }
965 channel_features[RedChannel].variance_sum_of_squares[i]=
966 variance.direction[i].red;
967 channel_features[GreenChannel].variance_sum_of_squares[i]=
968 variance.direction[i].green;
969 channel_features[BlueChannel].variance_sum_of_squares[i]=
970 variance.direction[i].blue;
cristycf5e6492010-01-28 02:45:28 +0000971 if (image->colorspace == CMYKColorspace)
972 channel_features[RedChannel].variance_sum_of_squares[i]=
973 variance.direction[i].index;
cristy53a727d2011-06-16 14:53:56 +0000974 if (image->matte != MagickFalse)
975 channel_features[RedChannel].variance_sum_of_squares[i]=
976 variance.direction[i].opacity;
cristycf5e6492010-01-28 02:45:28 +0000977 }
978 /*
979 Compute more texture features.
980 */
cristye1897792010-01-29 02:05:50 +0000981 (void) ResetMagickMemory(&variance,0,sizeof(variance));
982 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
983#if defined(MAGICKCORE_OPENMP_SUPPORT)
984 #pragma omp parallel for schedule(dynamic,4) shared(status)
985#endif
986 for (i=0; i < 4; i++)
987 {
cristybb503372010-05-27 20:51:26 +0000988 register ssize_t
cristye1897792010-01-29 02:05:50 +0000989 x;
990
cristybb503372010-05-27 20:51:26 +0000991 for (x=0; x < (ssize_t) number_grays; x++)
cristye1897792010-01-29 02:05:50 +0000992 {
cristye0acabf2010-01-30 00:52:38 +0000993 /*
994 Difference variance.
995 */
cristyffa10d02010-01-30 17:53:27 +0000996 variance.direction[i].red+=density_xy[x].direction[i].red;
997 variance.direction[i].green+=density_xy[x].direction[i].green;
998 variance.direction[i].blue+=density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +0000999 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001000 variance.direction[i].index+=density_xy[x].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +00001001 if (image->matte != MagickFalse)
1002 variance.direction[i].opacity+=density_xy[x].direction[i].opacity;
cristyffa10d02010-01-30 17:53:27 +00001003 sum_squares.direction[i].red+=density_xy[x].direction[i].red*
1004 density_xy[x].direction[i].red;
1005 sum_squares.direction[i].green+=density_xy[x].direction[i].green*
1006 density_xy[x].direction[i].green;
1007 sum_squares.direction[i].blue+=density_xy[x].direction[i].blue*
1008 density_xy[x].direction[i].blue;
cristye1897792010-01-29 02:05:50 +00001009 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001010 sum_squares.direction[i].index+=density_xy[x].direction[i].index*
1011 density_xy[x].direction[i].index;
cristy53a727d2011-06-16 14:53:56 +00001012 if (image->matte != MagickFalse)
1013 sum_squares.direction[i].opacity+=density_xy[x].direction[i].opacity*
1014 density_xy[x].direction[i].opacity;
cristyf6214de2010-01-29 02:47:41 +00001015 /*
1016 Difference entropy.
1017 */
1018 channel_features[RedChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001019 density_xy[x].direction[i].red*
1020 log10(density_xy[x].direction[i].red+MagickEpsilon);
cristyf6214de2010-01-29 02:47:41 +00001021 channel_features[GreenChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001022 density_xy[x].direction[i].green*
1023 log10(density_xy[x].direction[i].green+MagickEpsilon);
cristyf6214de2010-01-29 02:47:41 +00001024 channel_features[BlueChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001025 density_xy[x].direction[i].blue*
1026 log10(density_xy[x].direction[i].blue+MagickEpsilon);
cristyf6214de2010-01-29 02:47:41 +00001027 if (image->colorspace == CMYKColorspace)
1028 channel_features[IndexChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001029 density_xy[x].direction[i].index*
1030 log10(density_xy[x].direction[i].index+MagickEpsilon);
cristy53a727d2011-06-16 14:53:56 +00001031 if (image->matte != MagickFalse)
1032 channel_features[OpacityChannel].difference_entropy[i]-=
1033 density_xy[x].direction[i].opacity*
1034 log10(density_xy[x].direction[i].opacity+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +00001035 /*
1036 Information Measures of Correlation.
1037 */
cristyffa10d02010-01-30 17:53:27 +00001038 entropy_x.direction[i].red-=(density_x[x].direction[i].red*
1039 log10(density_x[x].direction[i].red+MagickEpsilon));
1040 entropy_x.direction[i].green-=(density_x[x].direction[i].green*
1041 log10(density_x[x].direction[i].green+MagickEpsilon));
1042 entropy_x.direction[i].blue-=(density_x[x].direction[i].blue*
1043 log10(density_x[x].direction[i].blue+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001044 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001045 entropy_x.direction[i].index-=(density_x[x].direction[i].index*
1046 log10(density_x[x].direction[i].index+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001047 if (image->matte != MagickFalse)
cristy53a727d2011-06-16 14:53:56 +00001048 entropy_x.direction[i].opacity-=(density_x[x].direction[i].opacity*
1049 log10(density_x[x].direction[i].opacity+MagickEpsilon));
1050 entropy_y.direction[i].red-=(density_y[x].direction[i].red*
1051 log10(density_y[x].direction[i].red+MagickEpsilon));
1052 entropy_y.direction[i].green-=(density_y[x].direction[i].green*
1053 log10(density_y[x].direction[i].green+MagickEpsilon));
1054 entropy_y.direction[i].blue-=(density_y[x].direction[i].blue*
1055 log10(density_y[x].direction[i].blue+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001056 if (image->colorspace == CMYKColorspace)
cristy53a727d2011-06-16 14:53:56 +00001057 entropy_y.direction[i].index-=(density_y[x].direction[i].index*
1058 log10(density_y[x].direction[i].index+MagickEpsilon));
1059 if (image->matte != MagickFalse)
1060 entropy_y.direction[i].opacity-=(density_y[x].direction[i].opacity*
1061 log10(density_y[x].direction[i].opacity+MagickEpsilon));
cristye1897792010-01-29 02:05:50 +00001062 }
cristyf6214de2010-01-29 02:47:41 +00001063 /*
1064 Difference variance.
1065 */
cristye1897792010-01-29 02:05:50 +00001066 channel_features[RedChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001067 (((double) number_grays*number_grays*sum_squares.direction[i].red)-
cristye1897792010-01-29 02:05:50 +00001068 (variance.direction[i].red*variance.direction[i].red))/
1069 ((double) number_grays*number_grays*number_grays*number_grays);
1070 channel_features[GreenChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001071 (((double) number_grays*number_grays*sum_squares.direction[i].green)-
cristye1897792010-01-29 02:05:50 +00001072 (variance.direction[i].green*variance.direction[i].green))/
1073 ((double) number_grays*number_grays*number_grays*number_grays);
1074 channel_features[BlueChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001075 (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
cristye1897792010-01-29 02:05:50 +00001076 (variance.direction[i].blue*variance.direction[i].blue))/
1077 ((double) number_grays*number_grays*number_grays*number_grays);
1078 if (image->matte != MagickFalse)
1079 channel_features[OpacityChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001080 (((double) number_grays*number_grays*sum_squares.direction[i].opacity)-
cristye1897792010-01-29 02:05:50 +00001081 (variance.direction[i].opacity*variance.direction[i].opacity))/
1082 ((double) number_grays*number_grays*number_grays*number_grays);
1083 if (image->colorspace == CMYKColorspace)
1084 channel_features[IndexChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001085 (((double) number_grays*number_grays*sum_squares.direction[i].index)-
cristye1897792010-01-29 02:05:50 +00001086 (variance.direction[i].index*variance.direction[i].index))/
1087 ((double) number_grays*number_grays*number_grays*number_grays);
cristye0acabf2010-01-30 00:52:38 +00001088 /*
1089 Information Measures of Correlation.
1090 */
1091 channel_features[RedChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001092 (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/
1093 (entropy_x.direction[i].red > entropy_y.direction[i].red ?
1094 entropy_x.direction[i].red : entropy_y.direction[i].red);
cristye0acabf2010-01-30 00:52:38 +00001095 channel_features[GreenChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001096 (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/
1097 (entropy_x.direction[i].green > entropy_y.direction[i].green ?
1098 entropy_x.direction[i].green : entropy_y.direction[i].green);
cristye0acabf2010-01-30 00:52:38 +00001099 channel_features[BlueChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001100 (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/
1101 (entropy_x.direction[i].blue > entropy_y.direction[i].blue ?
1102 entropy_x.direction[i].blue : entropy_y.direction[i].blue);
cristye0acabf2010-01-30 00:52:38 +00001103 if (image->colorspace == CMYKColorspace)
1104 channel_features[IndexChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001105 (entropy_xy.direction[i].index-entropy_xy1.direction[i].index)/
1106 (entropy_x.direction[i].index > entropy_y.direction[i].index ?
1107 entropy_x.direction[i].index : entropy_y.direction[i].index);
cristy53a727d2011-06-16 14:53:56 +00001108 if (image->matte != MagickFalse)
1109 channel_features[OpacityChannel].measure_of_correlation_1[i]=
1110 (entropy_xy.direction[i].opacity-entropy_xy1.direction[i].opacity)/
1111 (entropy_x.direction[i].opacity > entropy_y.direction[i].opacity ?
1112 entropy_x.direction[i].opacity : entropy_y.direction[i].opacity);
cristye0acabf2010-01-30 00:52:38 +00001113 channel_features[RedChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001114 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].red-
1115 entropy_xy.direction[i].red)))));
cristye0acabf2010-01-30 00:52:38 +00001116 channel_features[GreenChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001117 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].green-
1118 entropy_xy.direction[i].green)))));
cristye0acabf2010-01-30 00:52:38 +00001119 channel_features[BlueChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001120 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].blue-
1121 entropy_xy.direction[i].blue)))));
cristye0acabf2010-01-30 00:52:38 +00001122 if (image->colorspace == CMYKColorspace)
1123 channel_features[IndexChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001124 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].index-
1125 entropy_xy.direction[i].index)))));
cristy53a727d2011-06-16 14:53:56 +00001126 if (image->matte != MagickFalse)
1127 channel_features[OpacityChannel].measure_of_correlation_2[i]=
1128 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].opacity-
1129 entropy_xy.direction[i].opacity)))));
cristye1897792010-01-29 02:05:50 +00001130 }
1131 /*
1132 Compute more texture features.
1133 */
cristy3a82f252010-01-26 20:31:51 +00001134#if defined(MAGICKCORE_OPENMP_SUPPORT)
1135 #pragma omp parallel for schedule(dynamic,4) shared(status)
1136#endif
cristybd822072010-01-27 00:30:00 +00001137 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +00001138 {
cristybb503372010-05-27 20:51:26 +00001139 for (z=0; z < (ssize_t) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +00001140 {
cristybb503372010-05-27 20:51:26 +00001141 register ssize_t
cristybd822072010-01-27 00:30:00 +00001142 y;
cristy3a82f252010-01-26 20:31:51 +00001143
cristy7396d882010-01-27 02:37:56 +00001144 ChannelStatistics
cristybd822072010-01-27 00:30:00 +00001145 pixel;
1146
1147 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristybb503372010-05-27 20:51:26 +00001148 for (y=0; y < (ssize_t) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +00001149 {
cristybb503372010-05-27 20:51:26 +00001150 register ssize_t
cristybd822072010-01-27 00:30:00 +00001151 x;
1152
cristybb503372010-05-27 20:51:26 +00001153 for (x=0; x < (ssize_t) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +00001154 {
1155 /*
1156 Contrast: amount of local variations present in an image.
1157 */
1158 if (((y-x) == z) || ((x-y) == z))
1159 {
cristy7396d882010-01-27 02:37:56 +00001160 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
1161 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
1162 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +00001163 if (image->colorspace == CMYKColorspace)
1164 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy3a82f252010-01-26 20:31:51 +00001165 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +00001166 pixel.direction[i].opacity+=
1167 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001168 }
cristyffa10d02010-01-30 17:53:27 +00001169 /*
1170 Maximum Correlation Coefficient.
1171 */
1172 Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
1173 cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
1174 density_y[x].direction[i].red;
1175 Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
1176 cooccurrence[y][x].direction[i].green/
1177 density_x[z].direction[i].green/density_y[x].direction[i].red;
1178 Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
1179 cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
1180 density_y[x].direction[i].blue;
cristy53a727d2011-06-16 14:53:56 +00001181 if (image->colorspace == CMYKColorspace)
1182 Q[z][y].direction[i].index+=cooccurrence[z][x].direction[i].index*
1183 cooccurrence[y][x].direction[i].index/
1184 density_x[z].direction[i].index/density_y[x].direction[i].index;
cristyffa10d02010-01-30 17:53:27 +00001185 if (image->matte != MagickFalse)
1186 Q[z][y].direction[i].opacity+=
1187 cooccurrence[z][x].direction[i].opacity*
1188 cooccurrence[y][x].direction[i].opacity/
1189 density_x[z].direction[i].opacity/
1190 density_y[x].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001191 }
1192 }
cristy7396d882010-01-27 02:37:56 +00001193 channel_features[RedChannel].contrast[i]+=z*z*pixel.direction[i].red;
1194 channel_features[GreenChannel].contrast[i]+=z*z*pixel.direction[i].green;
1195 channel_features[BlueChannel].contrast[i]+=z*z*pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +00001196 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +00001197 channel_features[BlackChannel].contrast[i]+=z*z*
1198 pixel.direction[i].index;
cristy53a727d2011-06-16 14:53:56 +00001199 if (image->matte != MagickFalse)
1200 channel_features[OpacityChannel].contrast[i]+=z*z*
1201 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001202 }
cristye0acabf2010-01-30 00:52:38 +00001203 /*
1204 Maximum Correlation Coefficient.
cristyffa10d02010-01-30 17:53:27 +00001205 Future: return second largest eigenvalue of Q.
cristye0acabf2010-01-30 00:52:38 +00001206 */
1207 channel_features[RedChannel].maximum_correlation_coefficient[i]=
1208 sqrt((double) -1.0);
1209 channel_features[GreenChannel].maximum_correlation_coefficient[i]=
1210 sqrt((double) -1.0);
1211 channel_features[BlueChannel].maximum_correlation_coefficient[i]=
1212 sqrt((double) -1.0);
cristye0acabf2010-01-30 00:52:38 +00001213 if (image->colorspace == CMYKColorspace)
1214 channel_features[IndexChannel].maximum_correlation_coefficient[i]=
1215 sqrt((double) -1.0);
cristy53a727d2011-06-16 14:53:56 +00001216 if (image->matte != MagickFalse)
1217 channel_features[OpacityChannel].maximum_correlation_coefficient[i]=
1218 sqrt((double) -1.0);
cristy3a82f252010-01-26 20:31:51 +00001219 }
cristy7e9726d2010-01-26 02:08:40 +00001220 /*
cristyf2bf2c72010-01-25 19:54:15 +00001221 Relinquish resources.
1222 */
cristy7396d882010-01-27 02:37:56 +00001223 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
cristybb503372010-05-27 20:51:26 +00001224 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +00001225 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
1226 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
1227 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
1228 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
1229 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
cristybb503372010-05-27 20:51:26 +00001230 for (i=0; i < (ssize_t) number_grays; i++)
cristy7396d882010-01-27 02:37:56 +00001231 cooccurrence[i]=(ChannelStatistics *)
1232 RelinquishMagickMemory(cooccurrence[i]);
1233 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristy3e2860c2010-01-24 01:36:30 +00001234 return(channel_features);
1235}