blob: 9205ebefcba2f8089da56c75e7fa27977c1b9e1c [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% %
20% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21% 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%
cristy7e9726d2010-01-26 02:08:40 +0000111% channel_features=GetImageChannelFeatures(image,1,excepton);
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
cristybb503372010-05-27 20:51:26 +0000174 ssize_t
cristy3a82f252010-01-26 20:31:51 +0000175 y,
176 z;
cristy3e2860c2010-01-24 01:36:30 +0000177
cristy2070fa52010-01-24 03:17:57 +0000178 MagickBooleanType
179 status;
180
cristybb503372010-05-27 20:51:26 +0000181 register ssize_t
cristy2070fa52010-01-24 03:17:57 +0000182 i;
183
cristy3e2860c2010-01-24 01:36:30 +0000184 size_t
185 length;
186
cristybb503372010-05-27 20:51:26 +0000187 size_t
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);
cristy3e2860c2010-01-24 01:36:30 +0000196 length=AllChannels+1UL;
197 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 {
cristy7396d882010-01-27 02:37:56 +0000217 grays[i].red=(~0UL);
218 grays[i].green=(~0UL);
219 grays[i].blue=(~0UL);
220 grays[i].opacity=(~0UL);
221 grays[i].index=(~0UL);
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 {
cristy7396d882010-01-27 02:37:56 +0000250 grays[ScaleQuantumToMap(p->red)].red=ScaleQuantumToMap(p->red);
251 grays[ScaleQuantumToMap(p->green)].green=ScaleQuantumToMap(p->green);
252 grays[ScaleQuantumToMap(p->blue)].blue=ScaleQuantumToMap(p->blue);
cristy2070fa52010-01-24 03:17:57 +0000253 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000254 grays[ScaleQuantumToMap(p->opacity)].opacity=
cristy2070fa52010-01-24 03:17:57 +0000255 ScaleQuantumToMap(p->opacity);
256 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000257 grays[ScaleQuantumToMap(indexes[x])].index=
cristy2070fa52010-01-24 03:17:57 +0000258 ScaleQuantumToMap(indexes[x]);
cristy3e2860c2010-01-24 01:36:30 +0000259 p++;
260 }
261 }
cristy30c510a2010-01-24 03:43:00 +0000262 image_view=DestroyCacheView(image_view);
263 if (status == MagickFalse)
264 {
cristy7396d882010-01-27 02:37:56 +0000265 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000266 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
267 channel_features);
268 return(channel_features);
269 }
cristy7396d882010-01-27 02:37:56 +0000270 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristybb503372010-05-27 20:51:26 +0000271 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy2070fa52010-01-24 03:17:57 +0000272 {
cristy7396d882010-01-27 02:37:56 +0000273 if (grays[i].red != ~0UL)
274 grays[gray.red++].red=grays[i].red;
275 if (grays[i].green != ~0UL)
276 grays[gray.green++].green=grays[i].green;
277 if (grays[i].blue != ~0UL)
278 grays[gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000279 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000280 if (grays[i].opacity != ~0UL)
281 grays[gray.opacity++].opacity=grays[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000282 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000283 if (grays[i].index != ~0UL)
284 grays[gray.index++].index=grays[i].index;
cristy2070fa52010-01-24 03:17:57 +0000285 }
cristyf2bf2c72010-01-25 19:54:15 +0000286 /*
287 Allocate spatial dependence matrix.
288 */
cristy7396d882010-01-27 02:37:56 +0000289 number_grays=gray.red;
290 if (gray.green > number_grays)
291 number_grays=gray.green;
292 if (gray.blue > number_grays)
293 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000294 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000295 if (gray.opacity > number_grays)
296 number_grays=gray.opacity;
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;
300 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
301 sizeof(*cooccurrence));
cristy77173e52010-02-02 02:51:35 +0000302 density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000303 sizeof(*density_x));
304 density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
305 sizeof(*density_xy));
cristy77173e52010-02-02 02:51:35 +0000306 density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
cristyffa10d02010-01-30 17:53:27 +0000307 sizeof(*density_y));
308 Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
309 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
310 if ((cooccurrence == (ChannelStatistics **) NULL) ||
311 (density_x == (ChannelStatistics *) NULL) ||
312 (density_xy == (ChannelStatistics *) NULL) ||
313 (density_y == (ChannelStatistics *) NULL) ||
314 (Q == (ChannelStatistics **) NULL) ||
315 (sum == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000316 {
cristyffa10d02010-01-30 17:53:27 +0000317 if (Q != (ChannelStatistics **) NULL)
318 {
cristybb503372010-05-27 20:51:26 +0000319 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000320 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
321 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
322 }
323 if (sum != (ChannelStatistics *) NULL)
324 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
325 if (density_y != (ChannelStatistics *) NULL)
326 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
327 if (density_xy != (ChannelStatistics *) NULL)
328 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
329 if (density_x != (ChannelStatistics *) NULL)
330 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
331 if (cooccurrence != (ChannelStatistics **) NULL)
332 {
cristybb503372010-05-27 20:51:26 +0000333 for (i=0; i < (ssize_t) number_grays; i++)
cristyffa10d02010-01-30 17:53:27 +0000334 cooccurrence[i]=(ChannelStatistics *)
335 RelinquishMagickMemory(cooccurrence[i]);
336 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
337 cooccurrence);
338 }
cristy7396d882010-01-27 02:37:56 +0000339 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000340 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
341 channel_features);
cristye1897792010-01-29 02:05:50 +0000342 (void) ThrowMagickException(exception,GetMagickModule(),
343 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000344 return(channel_features);
345 }
cristyffa10d02010-01-30 17:53:27 +0000346 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
cristy77173e52010-02-02 02:51:35 +0000347 (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
348 (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
349 (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
cristyffa10d02010-01-30 17:53:27 +0000350 (void) ResetMagickMemory(&mean,0,sizeof(mean));
351 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
352 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
353 (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
354 (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
355 (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
356 (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
357 (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
358 (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
359 (void) ResetMagickMemory(&variance,0,sizeof(variance));
cristybb503372010-05-27 20:51:26 +0000360 for (i=0; i < (ssize_t) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000361 {
cristy7396d882010-01-27 02:37:56 +0000362 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
363 sizeof(**cooccurrence));
cristyffa10d02010-01-30 17:53:27 +0000364 Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
365 if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
366 (Q[i] == (ChannelStatistics *) NULL))
cristyf2bf2c72010-01-25 19:54:15 +0000367 break;
cristy7396d882010-01-27 02:37:56 +0000368 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
cristy3749be42010-02-02 02:46:51 +0000369 sizeof(**cooccurrence));
370 (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
cristyf2bf2c72010-01-25 19:54:15 +0000371 }
cristybb503372010-05-27 20:51:26 +0000372 if (i < (ssize_t) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000373 {
cristyf2bf2c72010-01-25 19:54:15 +0000374 for (i--; i >= 0; i--)
cristyffa10d02010-01-30 17:53:27 +0000375 {
376 if (Q[i] != (ChannelStatistics *) NULL)
377 Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
378 if (cooccurrence[i] != (ChannelStatistics *) NULL)
379 cooccurrence[i]=(ChannelStatistics *)
380 RelinquishMagickMemory(cooccurrence[i]);
381 }
382 Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
cristy7396d882010-01-27 02:37:56 +0000383 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyffa10d02010-01-30 17:53:27 +0000384 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
385 density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
386 density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
387 density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
cristy7396d882010-01-27 02:37:56 +0000388 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000389 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
390 channel_features);
cristye1897792010-01-29 02:05:50 +0000391 (void) ThrowMagickException(exception,GetMagickModule(),
392 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000393 return(channel_features);
394 }
395 /*
396 Initialize spatial dependence matrix.
397 */
398 status=MagickTrue;
399 image_view=AcquireCacheView(image);
400#if defined(MAGICKCORE_OPENMP_SUPPORT)
401 #pragma omp parallel for schedule(dynamic,4) shared(status)
402#endif
cristybb503372010-05-27 20:51:26 +0000403 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2bf2c72010-01-25 19:54:15 +0000404 {
cristybb503372010-05-27 20:51:26 +0000405 ssize_t
cristyf2bf2c72010-01-25 19:54:15 +0000406 u,
407 v;
408
409 register const IndexPacket
410 *restrict indexes;
411
412 register const PixelPacket
413 *restrict p;
414
cristybb503372010-05-27 20:51:26 +0000415 register ssize_t
cristyf2bf2c72010-01-25 19:54:15 +0000416 x;
417
cristy7e9726d2010-01-26 02:08:40 +0000418 ssize_t
419 offset;
420
cristyf2bf2c72010-01-25 19:54:15 +0000421 if (status == MagickFalse)
422 continue;
cristybb503372010-05-27 20:51:26 +0000423 p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,y,image->columns+
cristy7e9726d2010-01-26 02:08:40 +0000424 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000425 if (p == (const PixelPacket *) NULL)
426 {
427 status=MagickFalse;
428 continue;
429 }
430 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000431 p+=distance;
432 indexes+=distance;
cristybb503372010-05-27 20:51:26 +0000433 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2bf2c72010-01-25 19:54:15 +0000434 {
435 for (i=0; i < 4; i++)
436 {
cristy7e9726d2010-01-26 02:08:40 +0000437 switch (i)
438 {
439 case 0:
cristy549a37e2010-01-26 15:24:15 +0000440 default:
cristy7e9726d2010-01-26 02:08:40 +0000441 {
442 /*
cristy7396d882010-01-27 02:37:56 +0000443 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000444 */
445 offset=(ssize_t) distance;
446 break;
447 }
448 case 1:
449 {
450 /*
cristy7396d882010-01-27 02:37:56 +0000451 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000452 */
cristy7396d882010-01-27 02:37:56 +0000453 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000454 break;
455 }
456 case 2:
457 {
458 /*
cristy7396d882010-01-27 02:37:56 +0000459 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000460 */
cristyd99b0962010-05-29 23:14:26 +0000461 offset=(ssize_t) ((image->columns+2*distance)-distance);
cristy7e9726d2010-01-26 02:08:40 +0000462 break;
463 }
464 case 3:
465 {
466 /*
cristy7396d882010-01-27 02:37:56 +0000467 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000468 */
cristyd99b0962010-05-29 23:14:26 +0000469 offset=(ssize_t) ((image->columns+2*distance)+distance);
cristy7e9726d2010-01-26 02:08:40 +0000470 break;
471 }
472 }
473 u=0;
474 v=0;
cristy7396d882010-01-27 02:37:56 +0000475 while (grays[u].red != ScaleQuantumToMap(p->red))
cristy7e9726d2010-01-26 02:08:40 +0000476 u++;
cristy7396d882010-01-27 02:37:56 +0000477 while (grays[v].red != ScaleQuantumToMap((p+offset)->red))
cristy7e9726d2010-01-26 02:08:40 +0000478 v++;
cristy7396d882010-01-27 02:37:56 +0000479 cooccurrence[u][v].direction[i].red++;
480 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000481 u=0;
482 v=0;
cristy7396d882010-01-27 02:37:56 +0000483 while (grays[u].green != ScaleQuantumToMap(p->green))
cristy7e9726d2010-01-26 02:08:40 +0000484 u++;
cristy7396d882010-01-27 02:37:56 +0000485 while (grays[v].green != ScaleQuantumToMap((p+offset)->green))
cristy7e9726d2010-01-26 02:08:40 +0000486 v++;
cristy7396d882010-01-27 02:37:56 +0000487 cooccurrence[u][v].direction[i].green++;
488 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000489 u=0;
490 v=0;
cristy7396d882010-01-27 02:37:56 +0000491 while (grays[u].blue != ScaleQuantumToMap(p->blue))
cristy7e9726d2010-01-26 02:08:40 +0000492 u++;
cristy7396d882010-01-27 02:37:56 +0000493 while (grays[v].blue != ScaleQuantumToMap((p+offset)->blue))
cristy7e9726d2010-01-26 02:08:40 +0000494 v++;
cristy7396d882010-01-27 02:37:56 +0000495 cooccurrence[u][v].direction[i].blue++;
496 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000497 if (image->matte != MagickFalse)
498 {
499 u=0;
500 v=0;
cristy7396d882010-01-27 02:37:56 +0000501 while (grays[u].opacity != ScaleQuantumToMap(p->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000502 u++;
cristy7396d882010-01-27 02:37:56 +0000503 while (grays[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000504 v++;
cristy7396d882010-01-27 02:37:56 +0000505 cooccurrence[u][v].direction[i].opacity++;
506 cooccurrence[v][u].direction[i].opacity++;
cristy7e9726d2010-01-26 02:08:40 +0000507 }
508 if (image->colorspace == CMYKColorspace)
509 {
510 u=0;
511 v=0;
cristy7396d882010-01-27 02:37:56 +0000512 while (grays[u].index != ScaleQuantumToMap(indexes[x]))
cristy7e9726d2010-01-26 02:08:40 +0000513 u++;
cristy7396d882010-01-27 02:37:56 +0000514 while (grays[v].index != ScaleQuantumToMap(indexes[x+offset]))
cristy7e9726d2010-01-26 02:08:40 +0000515 v++;
cristy7396d882010-01-27 02:37:56 +0000516 cooccurrence[u][v].direction[i].index++;
517 cooccurrence[v][u].direction[i].index++;
cristy7e9726d2010-01-26 02:08:40 +0000518 }
cristyf2bf2c72010-01-25 19:54:15 +0000519 }
cristy7e9726d2010-01-26 02:08:40 +0000520 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000521 }
522 }
cristyffa10d02010-01-30 17:53:27 +0000523 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000524 image_view=DestroyCacheView(image_view);
525 if (status == MagickFalse)
526 {
cristybb503372010-05-27 20:51:26 +0000527 for (i=0; i < (ssize_t) number_grays; i++)
cristy7396d882010-01-27 02:37:56 +0000528 cooccurrence[i]=(ChannelStatistics *)
529 RelinquishMagickMemory(cooccurrence[i]);
530 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
cristyf2bf2c72010-01-25 19:54:15 +0000531 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
532 channel_features);
cristye1897792010-01-29 02:05:50 +0000533 (void) ThrowMagickException(exception,GetMagickModule(),
534 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000535 return(channel_features);
536 }
537 /*
cristy7e9726d2010-01-26 02:08:40 +0000538 Normalize spatial dependence matrix.
539 */
540#if defined(MAGICKCORE_OPENMP_SUPPORT)
541 #pragma omp parallel for schedule(dynamic,4) shared(status)
542#endif
cristybd822072010-01-27 00:30:00 +0000543 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000544 {
cristy549a37e2010-01-26 15:24:15 +0000545 double
546 normalize;
547
cristybd822072010-01-27 00:30:00 +0000548 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000549 {
cristybd822072010-01-27 00:30:00 +0000550 case 0:
551 default:
cristy7e9726d2010-01-26 02:08:40 +0000552 {
cristybd822072010-01-27 00:30:00 +0000553 /*
cristy7396d882010-01-27 02:37:56 +0000554 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000555 */
556 normalize=2.0*image->rows*(image->columns-distance);
557 break;
558 }
559 case 1:
560 {
561 /*
cristy7396d882010-01-27 02:37:56 +0000562 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000563 */
cristy7396d882010-01-27 02:37:56 +0000564 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000565 break;
566 }
567 case 2:
568 {
569 /*
cristy7396d882010-01-27 02:37:56 +0000570 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000571 */
cristy7396d882010-01-27 02:37:56 +0000572 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000573 break;
574 }
575 case 3:
576 {
577 /*
cristy7396d882010-01-27 02:37:56 +0000578 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000579 */
580 normalize=2.0*(image->rows-distance)*(image->columns-distance);
581 break;
582 }
583 }
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 {
cristy7396d882010-01-27 02:37: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->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000595 cooccurrence[x][y].direction[i].opacity/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000596 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000597 cooccurrence[x][y].direction[i].index/=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->matte != MagickFalse)
632 channel_features[OpacityChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000633 cooccurrence[x][y].direction[i].opacity*
634 cooccurrence[x][y].direction[i].opacity;
cristy549a37e2010-01-26 15:24:15 +0000635 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000636 channel_features[BlackChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000637 cooccurrence[x][y].direction[i].index*
638 cooccurrence[x][y].direction[i].index;
639 /*
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;
645 if (image->matte != MagickFalse)
646 sum[y].direction[i].opacity+=cooccurrence[x][y].direction[i].opacity;
647 if (image->colorspace == CMYKColorspace)
648 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
649 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->matte != MagickFalse)
cristycdf8e1b2010-01-28 01:52:33 +0000655 correlation.direction[i].opacity+=x*y*
656 cooccurrence[x][y].direction[i].opacity;
cristy7396d882010-01-27 02:37:56 +0000657 if (image->colorspace == CMYKColorspace)
cristycdf8e1b2010-01-28 01:52:33 +0000658 correlation.direction[i].index+=x*y*
659 cooccurrence[x][y].direction[i].index;
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);
669 if (image->matte != MagickFalse)
cristy9fc6dbf2010-01-28 02:49:50 +0000670 channel_features[OpacityChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000671 cooccurrence[x][y].direction[i].opacity/((y-x)*(y-x)+1);
672 if (image->colorspace == CMYKColorspace)
cristy9fc6dbf2010-01-28 02:49:50 +0000673 channel_features[IndexChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000674 cooccurrence[x][y].direction[i].index/((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;
684 if (image->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000685 density_xy[y+x+2].direction[i].opacity+=
cristye1897792010-01-29 02:05:50 +0000686 cooccurrence[x][y].direction[i].opacity;
687 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000688 density_xy[y+x+2].direction[i].index+=
cristye1897792010-01-29 02:05:50 +0000689 cooccurrence[x][y].direction[i].index;
690 /*
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);
702 if (image->matte != MagickFalse)
703 channel_features[OpacityChannel].entropy[i]-=
704 cooccurrence[x][y].direction[i].opacity*
705 log10(cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
706 if (image->colorspace == CMYKColorspace)
707 channel_features[IndexChannel].entropy[i]-=
708 cooccurrence[x][y].direction[i].index*
709 log10(cooccurrence[x][y].direction[i].index+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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000717 density_x[x].direction[i].opacity+=
cristye0acabf2010-01-30 00:52:38 +0000718 cooccurrence[x][y].direction[i].opacity;
719 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000720 density_x[x].direction[i].index+=
721 cooccurrence[x][y].direction[i].index;
722 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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000726 density_y[y].direction[i].opacity+=
cristye0acabf2010-01-30 00:52:38 +0000727 cooccurrence[x][y].direction[i].opacity;
728 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000729 density_y[y].direction[i].index+=
730 cooccurrence[x][y].direction[i].index;
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;
738 if (image->matte != MagickFalse)
739 {
740 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
741 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
742 }
743 if (image->colorspace == CMYKColorspace)
744 {
745 mean.direction[i].index+=y*sum[y].direction[i].index;
746 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
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)));
770 if (image->matte != MagickFalse)
771 channel_features[OpacityChannel].correlation[i]=
772 (correlation.direction[i].opacity-mean.direction[i].opacity*
773 mean.direction[i].opacity)/(sqrt(sum_squares.direction[i].opacity-
774 (mean.direction[i].opacity*mean.direction[i].opacity))*sqrt(
775 sum_squares.direction[i].opacity-(mean.direction[i].opacity*
776 mean.direction[i].opacity)));
777 if (image->colorspace == CMYKColorspace)
778 channel_features[IndexChannel].correlation[i]=
779 (correlation.direction[i].index-mean.direction[i].index*
780 mean.direction[i].index)/(sqrt(sum_squares.direction[i].index-
781 (mean.direction[i].index*mean.direction[i].index))*sqrt(
782 sum_squares.direction[i].index-(mean.direction[i].index*
783 mean.direction[i].index)));
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->matte != MagickFalse)
808 channel_features[OpacityChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000809 x*density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000810 if (image->colorspace == CMYKColorspace)
811 channel_features[IndexChannel].sum_average[i]+=
cristyffa10d02010-01-30 17:53:27 +0000812 x*density_xy[x].direction[i].index;
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->matte != MagickFalse)
826 channel_features[OpacityChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000827 density_xy[x].direction[i].opacity*
828 log10(density_xy[x].direction[i].opacity+MagickEpsilon);
cristye1897792010-01-29 02:05:50 +0000829 if (image->colorspace == CMYKColorspace)
830 channel_features[IndexChannel].sum_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +0000831 density_xy[x].direction[i].index*
832 log10(density_xy[x].direction[i].index+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->matte != MagickFalse)
849 channel_features[OpacityChannel].sum_variance[i]+=
850 (x-channel_features[OpacityChannel].sum_entropy[i])*
851 (x-channel_features[OpacityChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000852 density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +0000853 if (image->colorspace == CMYKColorspace)
854 channel_features[IndexChannel].sum_variance[i]+=
855 (x-channel_features[IndexChannel].sum_entropy[i])*
856 (x-channel_features[IndexChannel].sum_entropy[i])*
cristyffa10d02010-01-30 17:53:27 +0000857 density_xy[x].direction[i].index;
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;
887 if (image->matte != MagickFalse)
888 variance.direction[i].opacity+=(y-mean.direction[i].opacity+1)*
889 (y-mean.direction[i].opacity+1)*
890 cooccurrence[x][y].direction[i].opacity;
891 if (image->colorspace == CMYKColorspace)
892 variance.direction[i].index+=(y-mean.direction[i].index+1)*
893 (y-mean.direction[i].index+1)*cooccurrence[x][y].direction[i].index;
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;
903 if (image->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000904 density_xy[MagickAbsoluteValue(y-x)].direction[i].opacity+=
cristye1897792010-01-29 02:05:50 +0000905 cooccurrence[x][y].direction[i].opacity;
906 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000907 density_xy[MagickAbsoluteValue(y-x)].direction[i].index+=
cristye1897792010-01-29 02:05:50 +0000908 cooccurrence[x][y].direction[i].index;
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);
918 if (image->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000919 entropy_xy.direction[i].opacity-=
cristye0acabf2010-01-30 00:52:38 +0000920 cooccurrence[x][y].direction[i].opacity*log10(
cristyffa10d02010-01-30 17:53:27 +0000921 cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000922 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000923 entropy_xy.direction[i].index-=cooccurrence[x][y].direction[i].index*
924 log10(cooccurrence[x][y].direction[i].index+MagickEpsilon);
925 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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +0000935 entropy_xy1.direction[i].opacity-=(
936 cooccurrence[x][y].direction[i].opacity*log10(
937 density_x[x].direction[i].opacity*density_y[y].direction[i].opacity+
938 MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +0000939 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +0000940 entropy_xy1.direction[i].index-=(
941 cooccurrence[x][y].direction[i].index*log10(
942 density_x[x].direction[i].index*density_y[y].direction[i].index+
943 MagickEpsilon));
944 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));
953 if (image->matte != MagickFalse)
954 entropy_xy2.direction[i].opacity-=(density_x[x].direction[i].opacity*
955 density_y[y].direction[i].opacity*log10(
956 density_x[x].direction[i].opacity*density_y[y].direction[i].opacity+
957 MagickEpsilon));
958 if (image->colorspace == CMYKColorspace)
959 entropy_xy2.direction[i].index-=(density_x[x].direction[i].index*
960 density_y[y].direction[i].index*log10(
961 density_x[x].direction[i].index*density_y[y].direction[i].index+
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;
971 if (image->matte != MagickFalse)
972 channel_features[RedChannel].variance_sum_of_squares[i]=
973 variance.direction[i].opacity;
974 if (image->colorspace == CMYKColorspace)
975 channel_features[RedChannel].variance_sum_of_squares[i]=
976 variance.direction[i].index;
977 }
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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +00001000 variance.direction[i].opacity+=density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +00001001 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001002 variance.direction[i].index+=density_xy[x].direction[i].index;
1003 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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +00001010 sum_squares.direction[i].opacity+=density_xy[x].direction[i].opacity*
1011 density_xy[x].direction[i].opacity;
cristye1897792010-01-29 02:05:50 +00001012 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001013 sum_squares.direction[i].index+=density_xy[x].direction[i].index*
1014 density_xy[x].direction[i].index;
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->matte != MagickFalse)
1028 channel_features[OpacityChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001029 density_xy[x].direction[i].opacity*
1030 log10(density_xy[x].direction[i].opacity+MagickEpsilon);
cristyf6214de2010-01-29 02:47:41 +00001031 if (image->colorspace == CMYKColorspace)
1032 channel_features[IndexChannel].difference_entropy[i]-=
cristyffa10d02010-01-30 17:53:27 +00001033 density_xy[x].direction[i].index*
1034 log10(density_xy[x].direction[i].index+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->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +00001045 entropy_x.direction[i].opacity-=(density_x[x].direction[i].opacity*
1046 log10(density_x[x].direction[i].opacity+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001047 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001048 entropy_x.direction[i].index-=(density_x[x].direction[i].index*
1049 log10(density_x[x].direction[i].index+MagickEpsilon));
1050 entropy_y.direction[i].red-=(density_y[y].direction[i].red*
1051 log10(density_y[y].direction[i].red+MagickEpsilon));
1052 entropy_y.direction[i].green-=(density_y[y].direction[i].green*
1053 log10(density_y[y].direction[i].green+MagickEpsilon));
1054 entropy_y.direction[i].blue-=(density_y[y].direction[i].blue*
1055 log10(density_y[y].direction[i].blue+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001056 if (image->matte != MagickFalse)
cristyffa10d02010-01-30 17:53:27 +00001057 entropy_y.direction[i].opacity-=(density_y[y].direction[i].opacity*
1058 log10(density_y[y].direction[i].opacity+MagickEpsilon));
cristye0acabf2010-01-30 00:52:38 +00001059 if (image->colorspace == CMYKColorspace)
cristyffa10d02010-01-30 17:53:27 +00001060 entropy_y.direction[i].index-=(density_y[y].direction[i].index*
1061 log10(density_y[y].direction[i].index+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->matte != MagickFalse)
1104 channel_features[OpacityChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001105 (entropy_xy.direction[i].opacity-entropy_xy1.direction[i].opacity)/
1106 (entropy_x.direction[i].opacity > entropy_y.direction[i].opacity ?
1107 entropy_x.direction[i].opacity : entropy_y.direction[i].opacity);
cristye0acabf2010-01-30 00:52:38 +00001108 if (image->colorspace == CMYKColorspace)
1109 channel_features[IndexChannel].measure_of_correlation_1[i]=
cristyffa10d02010-01-30 17:53:27 +00001110 (entropy_xy.direction[i].index-entropy_xy1.direction[i].index)/
1111 (entropy_x.direction[i].index > entropy_y.direction[i].index ?
1112 entropy_x.direction[i].index : entropy_y.direction[i].index);
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->matte != MagickFalse)
1123 channel_features[OpacityChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001124 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].opacity-
1125 entropy_xy.direction[i].opacity)))));
cristye0acabf2010-01-30 00:52:38 +00001126 if (image->colorspace == CMYKColorspace)
1127 channel_features[IndexChannel].measure_of_correlation_2[i]=
cristyffa10d02010-01-30 17:53:27 +00001128 (sqrt(fabs(1.0-exp(-2.0*(entropy_xy2.direction[i].index-
1129 entropy_xy.direction[i].index)))));
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;
cristy3a82f252010-01-26 20:31:51 +00001163 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +00001164 pixel.direction[i].opacity+=
1165 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001166 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +00001167 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
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;
1181 if (image->matte != MagickFalse)
1182 Q[z][y].direction[i].opacity+=
1183 cooccurrence[z][x].direction[i].opacity*
1184 cooccurrence[y][x].direction[i].opacity/
1185 density_x[z].direction[i].opacity/
1186 density_y[x].direction[i].opacity;
1187 if (image->colorspace == CMYKColorspace)
1188 Q[z][y].direction[i].index+=cooccurrence[z][x].direction[i].index*
1189 cooccurrence[y][x].direction[i].index/
1190 density_x[z].direction[i].index/density_y[x].direction[i].index;
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->matte != MagickFalse)
1197 channel_features[OpacityChannel].contrast[i]+=z*z*
cristy7396d882010-01-27 02:37:56 +00001198 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001199 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +00001200 channel_features[BlackChannel].contrast[i]+=z*z*
1201 pixel.direction[i].index;
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);
1213 if (image->matte != MagickFalse)
1214 channel_features[OpacityChannel].maximum_correlation_coefficient[i]=
1215 sqrt((double) -1.0);
1216 if (image->colorspace == CMYKColorspace)
1217 channel_features[IndexChannel].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}