blob: 162431e5faf6a5de69b2882375f014cc0696131e [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,
cristy549a37e2010-01-26 15:24:15 +0000119% const unsigned long 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
131static inline long MagickAbsoluteValue(const long x)
132{
133 if (x < 0)
134 return(-x);
135 return(x);
136}
137
cristy3e2860c2010-01-24 01:36:30 +0000138MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image,
cristy7e9726d2010-01-26 02:08:40 +0000139 const unsigned long 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,
cristye0acabf2010-01-30 00:52:38 +0000156 icm_x,
157 icm_y,
158 icm_xy,
159 icm_xy1,
160 icm_xy2,
161 *mpm_x,
162 *mpm_y,
cristy7396d882010-01-27 02:37:56 +0000163 mean,
164 *sum,
cristye1897792010-01-29 02:05:50 +0000165 *sum_average,
cristycf5e6492010-01-28 02:45:28 +0000166 sum_squares,
167 variance;
cristy7396d882010-01-27 02:37:56 +0000168
cristy2070fa52010-01-24 03:17:57 +0000169 LongPixelPacket
cristy7396d882010-01-27 02:37:56 +0000170 gray,
171 *grays;
cristy2070fa52010-01-24 03:17:57 +0000172
cristy3e2860c2010-01-24 01:36:30 +0000173 long
cristy3a82f252010-01-26 20:31:51 +0000174 y,
175 z;
cristy3e2860c2010-01-24 01:36:30 +0000176
cristy2070fa52010-01-24 03:17:57 +0000177 MagickBooleanType
178 status;
179
180 register long
181 i;
182
cristy3e2860c2010-01-24 01:36:30 +0000183 size_t
184 length;
185
cristyf2bf2c72010-01-25 19:54:15 +0000186 unsigned long
cristy7396d882010-01-27 02:37:56 +0000187 number_grays;
cristyf2bf2c72010-01-25 19:54:15 +0000188
cristy3e2860c2010-01-24 01:36:30 +0000189 assert(image != (Image *) NULL);
190 assert(image->signature == MagickSignature);
191 if (image->debug != MagickFalse)
192 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000193 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
194 return((ChannelFeatures *) NULL);
cristy3e2860c2010-01-24 01:36:30 +0000195 length=AllChannels+1UL;
196 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
197 sizeof(*channel_features));
198 if (channel_features == (ChannelFeatures *) NULL)
199 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
200 (void) ResetMagickMemory(channel_features,0,length*
201 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000202 /*
cristy7396d882010-01-27 02:37:56 +0000203 Form grays.
cristy2070fa52010-01-24 03:17:57 +0000204 */
cristy7396d882010-01-27 02:37:56 +0000205 grays=(LongPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
206 if (grays == (LongPixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000207 {
cristy2070fa52010-01-24 03:17:57 +0000208 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
209 channel_features);
cristye1897792010-01-29 02:05:50 +0000210 (void) ThrowMagickException(exception,GetMagickModule(),
211 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy2070fa52010-01-24 03:17:57 +0000212 return(channel_features);
213 }
214 for (i=0; i <= (long) MaxMap; i++)
215 {
cristy7396d882010-01-27 02:37:56 +0000216 grays[i].red=(~0UL);
217 grays[i].green=(~0UL);
218 grays[i].blue=(~0UL);
219 grays[i].opacity=(~0UL);
220 grays[i].index=(~0UL);
cristy2070fa52010-01-24 03:17:57 +0000221 }
222 status=MagickTrue;
223 image_view=AcquireCacheView(image);
224#if defined(MAGICKCORE_OPENMP_SUPPORT)
225 #pragma omp parallel for schedule(dynamic,4) shared(status)
226#endif
cristy3e2860c2010-01-24 01:36:30 +0000227 for (y=0; y < (long) image->rows; y++)
228 {
229 register const IndexPacket
230 *restrict indexes;
231
232 register const PixelPacket
233 *restrict p;
234
235 register long
236 x;
237
cristy2070fa52010-01-24 03:17:57 +0000238 if (status == MagickFalse)
239 continue;
240 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3e2860c2010-01-24 01:36:30 +0000241 if (p == (const PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000242 {
243 status=MagickFalse;
244 continue;
245 }
246 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3e2860c2010-01-24 01:36:30 +0000247 for (x=0; x < (long) image->columns; x++)
248 {
cristy7396d882010-01-27 02:37:56 +0000249 grays[ScaleQuantumToMap(p->red)].red=ScaleQuantumToMap(p->red);
250 grays[ScaleQuantumToMap(p->green)].green=ScaleQuantumToMap(p->green);
251 grays[ScaleQuantumToMap(p->blue)].blue=ScaleQuantumToMap(p->blue);
cristy2070fa52010-01-24 03:17:57 +0000252 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000253 grays[ScaleQuantumToMap(p->opacity)].opacity=
cristy2070fa52010-01-24 03:17:57 +0000254 ScaleQuantumToMap(p->opacity);
255 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000256 grays[ScaleQuantumToMap(indexes[x])].index=
cristy2070fa52010-01-24 03:17:57 +0000257 ScaleQuantumToMap(indexes[x]);
cristy3e2860c2010-01-24 01:36:30 +0000258 p++;
259 }
260 }
cristy30c510a2010-01-24 03:43:00 +0000261 image_view=DestroyCacheView(image_view);
262 if (status == MagickFalse)
263 {
cristy7396d882010-01-27 02:37:56 +0000264 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000265 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
266 channel_features);
267 return(channel_features);
268 }
cristy7396d882010-01-27 02:37:56 +0000269 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristy2070fa52010-01-24 03:17:57 +0000270 for (i=0; i <= (long) MaxMap; i++)
271 {
cristy7396d882010-01-27 02:37:56 +0000272 if (grays[i].red != ~0UL)
273 grays[gray.red++].red=grays[i].red;
274 if (grays[i].green != ~0UL)
275 grays[gray.green++].green=grays[i].green;
276 if (grays[i].blue != ~0UL)
277 grays[gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000278 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000279 if (grays[i].opacity != ~0UL)
280 grays[gray.opacity++].opacity=grays[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000281 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000282 if (grays[i].index != ~0UL)
283 grays[gray.index++].index=grays[i].index;
cristy2070fa52010-01-24 03:17:57 +0000284 }
cristyf2bf2c72010-01-25 19:54:15 +0000285 /*
286 Allocate spatial dependence matrix.
287 */
cristy7396d882010-01-27 02:37:56 +0000288 number_grays=gray.red;
289 if (gray.green > number_grays)
290 number_grays=gray.green;
291 if (gray.blue > number_grays)
292 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000293 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000294 if (gray.opacity > number_grays)
295 number_grays=gray.opacity;
cristyf2bf2c72010-01-25 19:54:15 +0000296 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000297 if (gray.index > number_grays)
298 number_grays=gray.index;
299 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
300 sizeof(*cooccurrence));
301 if (cooccurrence == (ChannelStatistics **) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000302 {
cristy7396d882010-01-27 02:37:56 +0000303 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000304 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
305 channel_features);
cristye1897792010-01-29 02:05:50 +0000306 (void) ThrowMagickException(exception,GetMagickModule(),
307 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000308 return(channel_features);
309 }
cristy7396d882010-01-27 02:37:56 +0000310 for (i=0; i < (long) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000311 {
cristy7396d882010-01-27 02:37:56 +0000312 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
313 sizeof(**cooccurrence));
314 if (cooccurrence[i] == (ChannelStatistics *) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000315 break;
cristy7396d882010-01-27 02:37:56 +0000316 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
317 sizeof(*cooccurrence));
cristyf2bf2c72010-01-25 19:54:15 +0000318 }
cristy7396d882010-01-27 02:37:56 +0000319 if (i < (long) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000320 {
cristyf2bf2c72010-01-25 19:54:15 +0000321 for (i--; i >= 0; i--)
cristy7396d882010-01-27 02:37:56 +0000322 cooccurrence[i]=(ChannelStatistics *)
323 RelinquishMagickMemory(cooccurrence[i]);
324 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
325 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000326 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
327 channel_features);
cristye1897792010-01-29 02:05:50 +0000328 (void) ThrowMagickException(exception,GetMagickModule(),
329 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000330 return(channel_features);
331 }
332 /*
333 Initialize spatial dependence matrix.
334 */
335 status=MagickTrue;
336 image_view=AcquireCacheView(image);
337#if defined(MAGICKCORE_OPENMP_SUPPORT)
338 #pragma omp parallel for schedule(dynamic,4) shared(status)
339#endif
340 for (y=0; y < (long) image->rows; y++)
341 {
342 long
343 u,
344 v;
345
346 register const IndexPacket
347 *restrict indexes;
348
349 register const PixelPacket
350 *restrict p;
351
352 register long
353 x;
354
cristy7e9726d2010-01-26 02:08:40 +0000355 ssize_t
356 offset;
357
cristyf2bf2c72010-01-25 19:54:15 +0000358 if (status == MagickFalse)
359 continue;
cristy7e9726d2010-01-26 02:08:40 +0000360 p=GetCacheViewVirtualPixels(image_view,-(long) distance,y,image->columns+
361 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000362 if (p == (const PixelPacket *) NULL)
363 {
364 status=MagickFalse;
365 continue;
366 }
367 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000368 p+=distance;
369 indexes+=distance;
cristyf2bf2c72010-01-25 19:54:15 +0000370 for (x=0; x < (long) image->columns; x++)
371 {
372 for (i=0; i < 4; i++)
373 {
cristy7e9726d2010-01-26 02:08:40 +0000374 switch (i)
375 {
376 case 0:
cristy549a37e2010-01-26 15:24:15 +0000377 default:
cristy7e9726d2010-01-26 02:08:40 +0000378 {
379 /*
cristy7396d882010-01-27 02:37:56 +0000380 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000381 */
382 offset=(ssize_t) distance;
383 break;
384 }
385 case 1:
386 {
387 /*
cristy7396d882010-01-27 02:37:56 +0000388 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000389 */
cristy7396d882010-01-27 02:37:56 +0000390 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000391 break;
392 }
393 case 2:
394 {
395 /*
cristy7396d882010-01-27 02:37:56 +0000396 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000397 */
cristy7396d882010-01-27 02:37:56 +0000398 offset=(ssize_t) (image->columns+2*distance)-distance;
cristy7e9726d2010-01-26 02:08:40 +0000399 break;
400 }
401 case 3:
402 {
403 /*
cristy7396d882010-01-27 02:37:56 +0000404 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000405 */
406 offset=(ssize_t) (image->columns+2*distance)+distance;
407 break;
408 }
409 }
410 u=0;
411 v=0;
cristy7396d882010-01-27 02:37:56 +0000412 while (grays[u].red != ScaleQuantumToMap(p->red))
cristy7e9726d2010-01-26 02:08:40 +0000413 u++;
cristy7396d882010-01-27 02:37:56 +0000414 while (grays[v].red != ScaleQuantumToMap((p+offset)->red))
cristy7e9726d2010-01-26 02:08:40 +0000415 v++;
cristy7396d882010-01-27 02:37:56 +0000416 cooccurrence[u][v].direction[i].red++;
417 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000418 u=0;
419 v=0;
cristy7396d882010-01-27 02:37:56 +0000420 while (grays[u].green != ScaleQuantumToMap(p->green))
cristy7e9726d2010-01-26 02:08:40 +0000421 u++;
cristy7396d882010-01-27 02:37:56 +0000422 while (grays[v].green != ScaleQuantumToMap((p+offset)->green))
cristy7e9726d2010-01-26 02:08:40 +0000423 v++;
cristy7396d882010-01-27 02:37:56 +0000424 cooccurrence[u][v].direction[i].green++;
425 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000426 u=0;
427 v=0;
cristy7396d882010-01-27 02:37:56 +0000428 while (grays[u].blue != ScaleQuantumToMap(p->blue))
cristy7e9726d2010-01-26 02:08:40 +0000429 u++;
cristy7396d882010-01-27 02:37:56 +0000430 while (grays[v].blue != ScaleQuantumToMap((p+offset)->blue))
cristy7e9726d2010-01-26 02:08:40 +0000431 v++;
cristy7396d882010-01-27 02:37:56 +0000432 cooccurrence[u][v].direction[i].blue++;
433 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000434 if (image->matte != MagickFalse)
435 {
436 u=0;
437 v=0;
cristy7396d882010-01-27 02:37:56 +0000438 while (grays[u].opacity != ScaleQuantumToMap(p->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000439 u++;
cristy7396d882010-01-27 02:37:56 +0000440 while (grays[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000441 v++;
cristy7396d882010-01-27 02:37:56 +0000442 cooccurrence[u][v].direction[i].opacity++;
443 cooccurrence[v][u].direction[i].opacity++;
cristy7e9726d2010-01-26 02:08:40 +0000444 }
445 if (image->colorspace == CMYKColorspace)
446 {
447 u=0;
448 v=0;
cristy7396d882010-01-27 02:37:56 +0000449 while (grays[u].index != ScaleQuantumToMap(indexes[x]))
cristy7e9726d2010-01-26 02:08:40 +0000450 u++;
cristy7396d882010-01-27 02:37:56 +0000451 while (grays[v].index != ScaleQuantumToMap(indexes[x+offset]))
cristy7e9726d2010-01-26 02:08:40 +0000452 v++;
cristy7396d882010-01-27 02:37:56 +0000453 cooccurrence[u][v].direction[i].index++;
454 cooccurrence[v][u].direction[i].index++;
cristy7e9726d2010-01-26 02:08:40 +0000455 }
cristyf2bf2c72010-01-25 19:54:15 +0000456 }
cristy7e9726d2010-01-26 02:08:40 +0000457 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000458 }
459 }
460 image_view=DestroyCacheView(image_view);
461 if (status == MagickFalse)
462 {
cristy7396d882010-01-27 02:37:56 +0000463 for (i=0; i < (long) number_grays; i++)
464 cooccurrence[i]=(ChannelStatistics *)
465 RelinquishMagickMemory(cooccurrence[i]);
466 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
467 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000468 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
469 channel_features);
cristye1897792010-01-29 02:05:50 +0000470 (void) ThrowMagickException(exception,GetMagickModule(),
471 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyf2bf2c72010-01-25 19:54:15 +0000472 return(channel_features);
473 }
474 /*
cristy7e9726d2010-01-26 02:08:40 +0000475 Normalize spatial dependence matrix.
476 */
477#if defined(MAGICKCORE_OPENMP_SUPPORT)
478 #pragma omp parallel for schedule(dynamic,4) shared(status)
479#endif
cristybd822072010-01-27 00:30:00 +0000480 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000481 {
cristy549a37e2010-01-26 15:24:15 +0000482 double
483 normalize;
484
cristybd822072010-01-27 00:30:00 +0000485 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000486 {
cristybd822072010-01-27 00:30:00 +0000487 case 0:
488 default:
cristy7e9726d2010-01-26 02:08:40 +0000489 {
cristybd822072010-01-27 00:30:00 +0000490 /*
cristy7396d882010-01-27 02:37:56 +0000491 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000492 */
493 normalize=2.0*image->rows*(image->columns-distance);
494 break;
495 }
496 case 1:
497 {
498 /*
cristy7396d882010-01-27 02:37:56 +0000499 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000500 */
cristy7396d882010-01-27 02:37:56 +0000501 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000502 break;
503 }
504 case 2:
505 {
506 /*
cristy7396d882010-01-27 02:37:56 +0000507 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000508 */
cristy7396d882010-01-27 02:37:56 +0000509 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000510 break;
511 }
512 case 3:
513 {
514 /*
cristy7396d882010-01-27 02:37:56 +0000515 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000516 */
517 normalize=2.0*(image->rows-distance)*(image->columns-distance);
518 break;
519 }
520 }
cristy7396d882010-01-27 02:37:56 +0000521 for (y=0; y < (long) number_grays; y++)
cristybd822072010-01-27 00:30:00 +0000522 {
523 register long
524 x;
525
cristy7396d882010-01-27 02:37:56 +0000526 for (x=0; x < (long) number_grays; x++)
cristybd822072010-01-27 00:30:00 +0000527 {
cristy7396d882010-01-27 02:37:56 +0000528 cooccurrence[x][y].direction[i].red/=normalize;
529 cooccurrence[x][y].direction[i].green/=normalize;
530 cooccurrence[x][y].direction[i].blue/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000531 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000532 cooccurrence[x][y].direction[i].opacity/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000533 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000534 cooccurrence[x][y].direction[i].index/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000535 }
536 }
537 }
538 /*
539 Compute texture features.
540 */
cristy7396d882010-01-27 02:37:56 +0000541 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
cristye1897792010-01-29 02:05:50 +0000542 sum_average=(ChannelStatistics *) AcquireQuantumMemory(2*number_grays,
543 sizeof(*sum_average));
cristye0acabf2010-01-30 00:52:38 +0000544 mpm_x=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*mpm_x));
545 mpm_y=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*mpm_y));
cristye1897792010-01-29 02:05:50 +0000546 if ((sum == (ChannelStatistics *) NULL) ||
cristye0acabf2010-01-30 00:52:38 +0000547 (sum_average == (ChannelStatistics *) NULL) ||
548 (mpm_x == (ChannelStatistics *) NULL) ||
549 (mpm_y == (ChannelStatistics *) NULL))
cristy7396d882010-01-27 02:37:56 +0000550 {
cristye0acabf2010-01-30 00:52:38 +0000551 if (mpm_y != (ChannelStatistics *) NULL)
552 mpm_y=(ChannelStatistics *) RelinquishMagickMemory(mpm_y);
553 if (mpm_x != (ChannelStatistics *) NULL)
554 mpm_x=(ChannelStatistics *) RelinquishMagickMemory(mpm_x);
cristye1897792010-01-29 02:05:50 +0000555 if (sum_average != (ChannelStatistics *) NULL)
556 sum_average=(ChannelStatistics *) RelinquishMagickMemory(sum_average);
557 if (sum != (ChannelStatistics *) NULL)
558 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
cristy7396d882010-01-27 02:37:56 +0000559 for (i=0; i < (long) number_grays; i++)
560 cooccurrence[i]=(ChannelStatistics *)
561 RelinquishMagickMemory(cooccurrence[i]);
562 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
563 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
564 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
565 channel_features);
cristye1897792010-01-29 02:05:50 +0000566 (void) ThrowMagickException(exception,GetMagickModule(),
567 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7396d882010-01-27 02:37:56 +0000568 return(channel_features);
569 }
570 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
cristye1897792010-01-29 02:05:50 +0000571 (void) ResetMagickMemory(sum_average,0,2*number_grays*sizeof(*sum_average));
cristye0acabf2010-01-30 00:52:38 +0000572 (void) ResetMagickMemory(mpm_x,0,number_grays*sizeof(*mpm_x));
573 (void) ResetMagickMemory(mpm_y,0,number_grays*sizeof(*mpm_y));
cristy7396d882010-01-27 02:37:56 +0000574 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
575 (void) ResetMagickMemory(&mean,0,sizeof(mean));
576 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
cristy3a82f252010-01-26 20:31:51 +0000577#if defined(MAGICKCORE_OPENMP_SUPPORT)
578 #pragma omp parallel for schedule(dynamic,4) shared(status)
579#endif
cristybd822072010-01-27 00:30:00 +0000580 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000581 {
582 register long
cristybd822072010-01-27 00:30:00 +0000583 y;
cristy549a37e2010-01-26 15:24:15 +0000584
cristy7396d882010-01-27 02:37:56 +0000585 for (y=0; y < (long) number_grays; y++)
cristy549a37e2010-01-26 15:24:15 +0000586 {
cristybd822072010-01-27 00:30:00 +0000587 register long
588 x;
589
cristy7396d882010-01-27 02:37:56 +0000590 for (x=0; x < (long) number_grays; x++)
cristy549a37e2010-01-26 15:24:15 +0000591 {
592 /*
cristy3a82f252010-01-26 20:31:51 +0000593 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000594 */
595 channel_features[RedChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000596 cooccurrence[x][y].direction[i].red*
597 cooccurrence[x][y].direction[i].red;
cristy549a37e2010-01-26 15:24:15 +0000598 channel_features[GreenChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000599 cooccurrence[x][y].direction[i].green*
600 cooccurrence[x][y].direction[i].green;
cristy549a37e2010-01-26 15:24:15 +0000601 channel_features[BlueChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000602 cooccurrence[x][y].direction[i].blue*
603 cooccurrence[x][y].direction[i].blue;
cristy549a37e2010-01-26 15:24:15 +0000604 if (image->matte != MagickFalse)
605 channel_features[OpacityChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000606 cooccurrence[x][y].direction[i].opacity*
607 cooccurrence[x][y].direction[i].opacity;
cristy549a37e2010-01-26 15:24:15 +0000608 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000609 channel_features[BlackChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000610 cooccurrence[x][y].direction[i].index*
611 cooccurrence[x][y].direction[i].index;
612 /*
613 Correlation: measure of linear-dependencies in the image.
614 */
615 sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000616 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
cristycdf8e1b2010-01-28 01:52:33 +0000617 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
618 if (image->matte != MagickFalse)
619 sum[y].direction[i].opacity+=cooccurrence[x][y].direction[i].opacity;
620 if (image->colorspace == CMYKColorspace)
621 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
622 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000623 correlation.direction[i].green+=x*y*
624 cooccurrence[x][y].direction[i].green;
cristy7396d882010-01-27 02:37:56 +0000625 correlation.direction[i].blue+=x*y*
626 cooccurrence[x][y].direction[i].blue;
cristy7396d882010-01-27 02:37:56 +0000627 if (image->matte != MagickFalse)
cristycdf8e1b2010-01-28 01:52:33 +0000628 correlation.direction[i].opacity+=x*y*
629 cooccurrence[x][y].direction[i].opacity;
cristy7396d882010-01-27 02:37:56 +0000630 if (image->colorspace == CMYKColorspace)
cristycdf8e1b2010-01-28 01:52:33 +0000631 correlation.direction[i].index+=x*y*
632 cooccurrence[x][y].direction[i].index;
cristycf5e6492010-01-28 02:45:28 +0000633 /*
634 Inverse Difference Moment.
635 */
cristy9fc6dbf2010-01-28 02:49:50 +0000636 channel_features[RedChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000637 cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
cristy9fc6dbf2010-01-28 02:49:50 +0000638 channel_features[GreenChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000639 cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
cristy9fc6dbf2010-01-28 02:49:50 +0000640 channel_features[BlueChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000641 cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
642 if (image->matte != MagickFalse)
cristy9fc6dbf2010-01-28 02:49:50 +0000643 channel_features[OpacityChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000644 cooccurrence[x][y].direction[i].opacity/((y-x)*(y-x)+1);
645 if (image->colorspace == CMYKColorspace)
cristy9fc6dbf2010-01-28 02:49:50 +0000646 channel_features[IndexChannel].inverse_difference_moment[i]+=
cristycf5e6492010-01-28 02:45:28 +0000647 cooccurrence[x][y].direction[i].index/((y-x)*(y-x)+1);
cristye1897792010-01-29 02:05:50 +0000648 /*
649 Sum average.
650 */
651 sum_average[y+x+2].direction[i].red+=
652 cooccurrence[x][y].direction[i].red;
653 sum_average[y+x+2].direction[i].green+=
654 cooccurrence[x][y].direction[i].green;
655 sum_average[y+x+2].direction[i].blue+=
656 cooccurrence[x][y].direction[i].blue;
657 if (image->matte != MagickFalse)
658 sum_average[y+x+2].direction[i].opacity+=
659 cooccurrence[x][y].direction[i].opacity;
660 if (image->colorspace == CMYKColorspace)
661 sum_average[y+x+2].direction[i].index+=
662 cooccurrence[x][y].direction[i].index;
663 /*
664 Entropy.
665 */
666 channel_features[RedChannel].entropy[i]-=
667 cooccurrence[x][y].direction[i].red*
668 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
669 channel_features[GreenChannel].entropy[i]-=
670 cooccurrence[x][y].direction[i].green*
671 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
672 channel_features[BlueChannel].entropy[i]-=
673 cooccurrence[x][y].direction[i].blue*
674 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
675 if (image->matte != MagickFalse)
676 channel_features[OpacityChannel].entropy[i]-=
677 cooccurrence[x][y].direction[i].opacity*
678 log10(cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
679 if (image->colorspace == CMYKColorspace)
680 channel_features[IndexChannel].entropy[i]-=
681 cooccurrence[x][y].direction[i].index*
682 log10(cooccurrence[x][y].direction[i].index+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +0000683 /*
684 Information Measures of Correlation.
685 */
686 mpm_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
687 mpm_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
688 mpm_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
689 if (image->matte != MagickFalse)
690 mpm_x[x].direction[i].opacity+=
691 cooccurrence[x][y].direction[i].opacity;
692 if (image->colorspace == CMYKColorspace)
693 mpm_x[x].direction[i].index+=cooccurrence[x][y].direction[i].index;
694 mpm_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
695 mpm_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
696 mpm_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
697 if (image->matte != MagickFalse)
698 mpm_y[y].direction[i].opacity+=
699 cooccurrence[x][y].direction[i].opacity;
700 if (image->colorspace == CMYKColorspace)
701 mpm_y[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy7e9726d2010-01-26 02:08:40 +0000702 }
cristycdf8e1b2010-01-28 01:52:33 +0000703 mean.direction[i].red+=y*sum[y].direction[i].red;
704 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
705 mean.direction[i].green+=y*sum[y].direction[i].green;
706 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
707 mean.direction[i].blue+=y*sum[y].direction[i].blue;
708 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
709 if (image->matte != MagickFalse)
710 {
711 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
712 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
713 }
714 if (image->colorspace == CMYKColorspace)
715 {
716 mean.direction[i].index+=y*sum[y].direction[i].index;
717 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
718 }
cristy7e9726d2010-01-26 02:08:40 +0000719 }
cristycdf8e1b2010-01-28 01:52:33 +0000720 /*
721 Correlation: measure of linear-dependencies in the image.
722 */
723 channel_features[RedChannel].correlation[i]=
724 (correlation.direction[i].red-mean.direction[i].red*
725 mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
726 (mean.direction[i].red*mean.direction[i].red))*sqrt(
727 sum_squares.direction[i].red-(mean.direction[i].red*
728 mean.direction[i].red)));
729 channel_features[GreenChannel].correlation[i]=
730 (correlation.direction[i].green-mean.direction[i].green*
731 mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
732 (mean.direction[i].green*mean.direction[i].green))*sqrt(
733 sum_squares.direction[i].green-(mean.direction[i].green*
734 mean.direction[i].green)));
735 channel_features[BlueChannel].correlation[i]=
736 (correlation.direction[i].blue-mean.direction[i].blue*
737 mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
738 (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
739 sum_squares.direction[i].blue-(mean.direction[i].blue*
740 mean.direction[i].blue)));
741 if (image->matte != MagickFalse)
742 channel_features[OpacityChannel].correlation[i]=
743 (correlation.direction[i].opacity-mean.direction[i].opacity*
744 mean.direction[i].opacity)/(sqrt(sum_squares.direction[i].opacity-
745 (mean.direction[i].opacity*mean.direction[i].opacity))*sqrt(
746 sum_squares.direction[i].opacity-(mean.direction[i].opacity*
747 mean.direction[i].opacity)));
748 if (image->colorspace == CMYKColorspace)
749 channel_features[IndexChannel].correlation[i]=
750 (correlation.direction[i].index-mean.direction[i].index*
751 mean.direction[i].index)/(sqrt(sum_squares.direction[i].index-
752 (mean.direction[i].index*mean.direction[i].index))*sqrt(
753 sum_squares.direction[i].index-(mean.direction[i].index*
754 mean.direction[i].index)));
cristy7e9726d2010-01-26 02:08:40 +0000755 }
cristycf5e6492010-01-28 02:45:28 +0000756 /*
757 Compute more texture features.
758 */
cristye1897792010-01-29 02:05:50 +0000759#if defined(MAGICKCORE_OPENMP_SUPPORT)
760 #pragma omp parallel for schedule(dynamic,4) shared(status)
761#endif
762 for (i=0; i < 4; i++)
763 {
764 register long
765 x;
766
767 for (x=2; x < (long) (2*number_grays); x++)
768 {
769 /*
770 Sum average.
771 */
772 channel_features[RedChannel].sum_average[i]+=
773 x*sum_average[x].direction[i].red;
774 channel_features[GreenChannel].sum_average[i]+=
775 x*sum_average[x].direction[i].green;
776 channel_features[BlueChannel].sum_average[i]+=
777 x*sum_average[x].direction[i].blue;
778 if (image->matte != MagickFalse)
779 channel_features[OpacityChannel].sum_average[i]+=
780 x*sum_average[x].direction[i].opacity;
781 if (image->colorspace == CMYKColorspace)
782 channel_features[IndexChannel].sum_average[i]+=
783 x*sum_average[x].direction[i].index;
784 /*
785 Sum entropy.
786 */
787 channel_features[RedChannel].sum_entropy[i]-=
788 sum_average[x].direction[i].red*
789 log10(sum_average[x].direction[i].red+MagickEpsilon);
790 channel_features[GreenChannel].sum_entropy[i]-=
791 sum_average[x].direction[i].green*
792 log10(sum_average[x].direction[i].green+MagickEpsilon);
793 channel_features[BlueChannel].sum_entropy[i]-=
794 sum_average[x].direction[i].blue*
795 log10(sum_average[x].direction[i].blue+MagickEpsilon);
796 if (image->matte != MagickFalse)
797 channel_features[OpacityChannel].sum_entropy[i]-=
798 sum_average[x].direction[i].opacity*
799 log10(sum_average[x].direction[i].opacity+MagickEpsilon);
800 if (image->colorspace == CMYKColorspace)
801 channel_features[IndexChannel].sum_entropy[i]-=
802 sum_average[x].direction[i].index*
803 log10(sum_average[x].direction[i].index+MagickEpsilon);
804 /*
805 Sum variance.
806 */
807 channel_features[RedChannel].sum_variance[i]+=
808 (x-channel_features[RedChannel].sum_entropy[i])*
809 (x-channel_features[RedChannel].sum_entropy[i])*
810 sum_average[x].direction[i].red;
811 channel_features[GreenChannel].sum_variance[i]+=
812 (x-channel_features[GreenChannel].sum_entropy[i])*
813 (x-channel_features[GreenChannel].sum_entropy[i])*
814 sum_average[x].direction[i].green;
815 channel_features[BlueChannel].sum_variance[i]+=
816 (x-channel_features[BlueChannel].sum_entropy[i])*
817 (x-channel_features[BlueChannel].sum_entropy[i])*
818 sum_average[x].direction[i].blue;
819 if (image->matte != MagickFalse)
820 channel_features[OpacityChannel].sum_variance[i]+=
821 (x-channel_features[OpacityChannel].sum_entropy[i])*
822 (x-channel_features[OpacityChannel].sum_entropy[i])*
823 sum_average[x].direction[i].opacity;
824 if (image->colorspace == CMYKColorspace)
825 channel_features[IndexChannel].sum_variance[i]+=
826 (x-channel_features[IndexChannel].sum_entropy[i])*
827 (x-channel_features[IndexChannel].sum_entropy[i])*
828 sum_average[x].direction[i].index;
829 }
830 }
831 /*
832 Compute more texture features.
833 */
cristye0acabf2010-01-30 00:52:38 +0000834 (void) ResetMagickMemory(&icm_x,0,sizeof(icm_x));
835 (void) ResetMagickMemory(&icm_y,0,sizeof(icm_y));
836 (void) ResetMagickMemory(&icm_xy,0,sizeof(icm_xy));
837 (void) ResetMagickMemory(&icm_xy1,0,sizeof(icm_xy1));
838 (void) ResetMagickMemory(&icm_xy2,0,sizeof(icm_xy2));
cristye1897792010-01-29 02:05:50 +0000839 (void) ResetMagickMemory(sum_average,0,2*number_grays*sizeof(*sum_average));
cristye0acabf2010-01-30 00:52:38 +0000840 (void) ResetMagickMemory(&variance,0,sizeof(variance));
cristycf5e6492010-01-28 02:45:28 +0000841#if defined(MAGICKCORE_OPENMP_SUPPORT)
842 #pragma omp parallel for schedule(dynamic,4) shared(status)
843#endif
844 for (i=0; i < 4; i++)
845 {
846 register long
847 y;
848
849 for (y=0; y < (long) number_grays; y++)
850 {
851 register long
852 x;
853
854 for (x=0; x < (long) number_grays; x++)
855 {
856 /*
857 Sum of Squares: Variance
858 */
859 variance.direction[i].red+=(y-mean.direction[i].red+1)*
860 (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
861 variance.direction[i].green+=(y-mean.direction[i].green+1)*
862 (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
863 variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
864 (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
865 if (image->matte != MagickFalse)
866 variance.direction[i].opacity+=(y-mean.direction[i].opacity+1)*
867 (y-mean.direction[i].opacity+1)*
868 cooccurrence[x][y].direction[i].opacity;
869 if (image->colorspace == CMYKColorspace)
870 variance.direction[i].index+=(y-mean.direction[i].index+1)*
871 (y-mean.direction[i].index+1)*cooccurrence[x][y].direction[i].index;
cristye1897792010-01-29 02:05:50 +0000872 /*
cristye0acabf2010-01-30 00:52:38 +0000873 Sum average / Difference Variance.
cristye1897792010-01-29 02:05:50 +0000874 */
875 sum_average[MagickAbsoluteValue(y-x)].direction[i].red+=
876 cooccurrence[x][y].direction[i].red;
877 sum_average[MagickAbsoluteValue(y-x)].direction[i].green+=
878 cooccurrence[x][y].direction[i].green;
879 sum_average[MagickAbsoluteValue(y-x)].direction[i].blue+=
880 cooccurrence[x][y].direction[i].blue;
881 if (image->matte != MagickFalse)
882 sum_average[MagickAbsoluteValue(y-x)].direction[i].opacity+=
883 cooccurrence[x][y].direction[i].opacity;
884 if (image->colorspace == CMYKColorspace)
885 sum_average[MagickAbsoluteValue(y-x)].direction[i].index+=
886 cooccurrence[x][y].direction[i].index;
cristye0acabf2010-01-30 00:52:38 +0000887 /*
888 Information Measures of Correlation.
889 */
890 icm_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
891 log10(cooccurrence[x][y].direction[i].red+MagickEpsilon);
892 icm_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
893 log10(cooccurrence[x][y].direction[i].green+MagickEpsilon);
894 icm_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
895 log10(cooccurrence[x][y].direction[i].blue+MagickEpsilon);
896 if (image->matte != MagickFalse)
897 icm_xy.direction[i].opacity-=cooccurrence[x][y].direction[i].opacity*
898 log10(cooccurrence[x][y].direction[i].opacity+MagickEpsilon);
899 if (image->colorspace == CMYKColorspace)
900 icm_xy.direction[i].index-=cooccurrence[x][y].direction[i].index*
901 log10(cooccurrence[x][y].direction[i].index+MagickEpsilon);
902 icm_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
903 log10(mpm_x[x].direction[i].red*mpm_y[y].direction[i].red+
904 MagickEpsilon));
905 icm_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
906 log10(mpm_x[x].direction[i].green*mpm_y[y].direction[i].green+
907 MagickEpsilon));
908 icm_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
909 log10(mpm_x[x].direction[i].blue*mpm_y[y].direction[i].blue+
910 MagickEpsilon));
911 if (image->matte != MagickFalse)
912 icm_xy1.direction[i].opacity-=(
913 cooccurrence[x][y].direction[i].opacity*log10(
914 mpm_x[x].direction[i].opacity*mpm_y[y].direction[i].opacity+
915 MagickEpsilon));
916 if (image->colorspace == CMYKColorspace)
917 icm_xy1.direction[i].index-=(cooccurrence[x][y].direction[i].index*
918 log10(mpm_x[x].direction[i].index*mpm_y[y].direction[i].index+
919 MagickEpsilon));
920 icm_xy2.direction[i].red-=(mpm_x[x].direction[i].red*
921 mpm_y[y].direction[i].red*log10(mpm_x[x].direction[i].red*
922 mpm_y[y].direction[i].red+MagickEpsilon));
923 icm_xy2.direction[i].green-=(mpm_x[x].direction[i].green*
924 mpm_y[y].direction[i].green*log10(mpm_x[x].direction[i].green*
925 mpm_y[y].direction[i].green+MagickEpsilon));
926 icm_xy2.direction[i].blue-=(mpm_x[x].direction[i].blue*
927 mpm_y[y].direction[i].blue*log10(mpm_x[x].direction[i].blue*
928 mpm_y[y].direction[i].blue+MagickEpsilon));
929 if (image->matte != MagickFalse)
930 icm_xy2.direction[i].opacity-=(mpm_x[x].direction[i].opacity*
931 mpm_y[y].direction[i].opacity*log10(mpm_x[x].direction[i].opacity*
932 mpm_y[y].direction[i].opacity+MagickEpsilon));
933 if (image->colorspace == CMYKColorspace)
934 icm_xy2.direction[i].index-=(mpm_x[x].direction[i].index*
935 mpm_y[y].direction[i].index*log10(mpm_x[x].direction[i].index*
936 mpm_y[y].direction[i].index+MagickEpsilon));
cristycf5e6492010-01-28 02:45:28 +0000937 }
938 }
939 channel_features[RedChannel].variance_sum_of_squares[i]=
940 variance.direction[i].red;
941 channel_features[GreenChannel].variance_sum_of_squares[i]=
942 variance.direction[i].green;
943 channel_features[BlueChannel].variance_sum_of_squares[i]=
944 variance.direction[i].blue;
945 if (image->matte != MagickFalse)
946 channel_features[RedChannel].variance_sum_of_squares[i]=
947 variance.direction[i].opacity;
948 if (image->colorspace == CMYKColorspace)
949 channel_features[RedChannel].variance_sum_of_squares[i]=
950 variance.direction[i].index;
951 }
952 /*
953 Compute more texture features.
954 */
cristye1897792010-01-29 02:05:50 +0000955 (void) ResetMagickMemory(&variance,0,sizeof(variance));
956 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
957#if defined(MAGICKCORE_OPENMP_SUPPORT)
958 #pragma omp parallel for schedule(dynamic,4) shared(status)
959#endif
960 for (i=0; i < 4; i++)
961 {
962 register long
963 x;
964
965 for (x=0; x < (long) number_grays; x++)
966 {
cristye0acabf2010-01-30 00:52:38 +0000967 /*
968 Difference variance.
969 */
cristye1897792010-01-29 02:05:50 +0000970 variance.direction[i].red+=sum_average[x].direction[i].red;
971 variance.direction[i].green+=sum_average[x].direction[i].green;
972 variance.direction[i].blue+=sum_average[x].direction[i].blue;
973 if (image->matte != MagickFalse)
974 variance.direction[i].opacity+=sum_average[x].direction[i].opacity;
975 if (image->colorspace == CMYKColorspace)
976 variance.direction[i].index+=sum_average[x].direction[i].index;
977 sum_squares.direction[i].red+=sum_average[x].direction[i].red*
978 sum_average[x].direction[i].red;
979 sum_squares.direction[i].green+=sum_average[x].direction[i].green*
980 sum_average[x].direction[i].green;
981 sum_squares.direction[i].blue+=sum_average[x].direction[i].blue*
982 sum_average[x].direction[i].blue;
983 if (image->matte != MagickFalse)
984 sum_squares.direction[i].opacity+=sum_average[x].direction[i].opacity*
985 sum_average[x].direction[i].opacity;
986 if (image->colorspace == CMYKColorspace)
987 sum_squares.direction[i].index+=sum_average[x].direction[i].index*
988 sum_average[x].direction[i].index;
cristyf6214de2010-01-29 02:47:41 +0000989 /*
990 Difference entropy.
991 */
992 channel_features[RedChannel].difference_entropy[i]-=
993 sum_average[x].direction[i].red*
994 log10(sum_average[x].direction[i].red+MagickEpsilon);
995 channel_features[GreenChannel].difference_entropy[i]-=
996 sum_average[x].direction[i].green*
997 log10(sum_average[x].direction[i].green+MagickEpsilon);
998 channel_features[BlueChannel].difference_entropy[i]-=
999 sum_average[x].direction[i].blue*
1000 log10(sum_average[x].direction[i].blue+MagickEpsilon);
1001 if (image->matte != MagickFalse)
1002 channel_features[OpacityChannel].difference_entropy[i]-=
1003 sum_average[x].direction[i].opacity*
1004 log10(sum_average[x].direction[i].opacity+MagickEpsilon);
1005 if (image->colorspace == CMYKColorspace)
1006 channel_features[IndexChannel].difference_entropy[i]-=
1007 sum_average[x].direction[i].index*
1008 log10(sum_average[x].direction[i].index+MagickEpsilon);
cristye0acabf2010-01-30 00:52:38 +00001009 /*
1010 Information Measures of Correlation.
1011 */
1012 icm_x.direction[i].red-=(mpm_x[x].direction[i].red*
1013 log10(mpm_x[x].direction[i].red+MagickEpsilon));
1014 icm_x.direction[i].green-=(mpm_x[x].direction[i].green*
1015 log10(mpm_x[x].direction[i].green+MagickEpsilon));
1016 icm_x.direction[i].blue-=(mpm_x[x].direction[i].blue*
1017 log10(mpm_x[x].direction[i].blue+MagickEpsilon));
1018 if (image->matte != MagickFalse)
1019 icm_x.direction[i].opacity-=(mpm_x[x].direction[i].opacity*
1020 log10(mpm_x[x].direction[i].opacity+MagickEpsilon));
1021 if (image->colorspace == CMYKColorspace)
1022 icm_x.direction[i].index-=(mpm_x[x].direction[i].index*
1023 log10(mpm_x[x].direction[i].index+MagickEpsilon));
1024 icm_y.direction[i].red-=(mpm_y[y].direction[i].red*
1025 log10(mpm_y[y].direction[i].red+MagickEpsilon));
1026 icm_y.direction[i].green-=(mpm_y[y].direction[i].green*
1027 log10(mpm_y[y].direction[i].green+MagickEpsilon));
1028 icm_y.direction[i].blue-=(mpm_y[y].direction[i].blue*
1029 log10(mpm_y[y].direction[i].blue+MagickEpsilon));
1030 if (image->matte != MagickFalse)
1031 icm_y.direction[i].opacity-=(mpm_y[y].direction[i].opacity*
1032 log10(mpm_y[y].direction[i].opacity+MagickEpsilon));
1033 if (image->colorspace == CMYKColorspace)
1034 icm_y.direction[i].index-=(mpm_y[y].direction[i].index*
1035 log10(mpm_y[y].direction[i].index+MagickEpsilon));
cristye1897792010-01-29 02:05:50 +00001036 }
cristyf6214de2010-01-29 02:47:41 +00001037 /*
1038 Difference variance.
1039 */
cristye1897792010-01-29 02:05:50 +00001040 channel_features[RedChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001041 (((double) number_grays*number_grays*sum_squares.direction[i].red)-
cristye1897792010-01-29 02:05:50 +00001042 (variance.direction[i].red*variance.direction[i].red))/
1043 ((double) number_grays*number_grays*number_grays*number_grays);
1044 channel_features[GreenChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001045 (((double) number_grays*number_grays*sum_squares.direction[i].green)-
cristye1897792010-01-29 02:05:50 +00001046 (variance.direction[i].green*variance.direction[i].green))/
1047 ((double) number_grays*number_grays*number_grays*number_grays);
1048 channel_features[BlueChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001049 (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
cristye1897792010-01-29 02:05:50 +00001050 (variance.direction[i].blue*variance.direction[i].blue))/
1051 ((double) number_grays*number_grays*number_grays*number_grays);
1052 if (image->matte != MagickFalse)
1053 channel_features[OpacityChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001054 (((double) number_grays*number_grays*sum_squares.direction[i].opacity)-
cristye1897792010-01-29 02:05:50 +00001055 (variance.direction[i].opacity*variance.direction[i].opacity))/
1056 ((double) number_grays*number_grays*number_grays*number_grays);
1057 if (image->colorspace == CMYKColorspace)
1058 channel_features[IndexChannel].difference_variance[i]=
cristye0e19dc2010-01-29 02:13:08 +00001059 (((double) number_grays*number_grays*sum_squares.direction[i].index)-
cristye1897792010-01-29 02:05:50 +00001060 (variance.direction[i].index*variance.direction[i].index))/
1061 ((double) number_grays*number_grays*number_grays*number_grays);
cristye0acabf2010-01-30 00:52:38 +00001062 /*
1063 Information Measures of Correlation.
1064 */
1065 channel_features[RedChannel].measure_of_correlation_1[i]=
1066 (icm_xy.direction[i].red-icm_xy1.direction[i].red)/
1067 (icm_x.direction[i].red > icm_y.direction[i].red ?
1068 icm_x.direction[i].red : icm_y.direction[i].red);
1069 channel_features[GreenChannel].measure_of_correlation_1[i]=
1070 (icm_xy.direction[i].green-icm_xy1.direction[i].green)/
1071 (icm_x.direction[i].green > icm_y.direction[i].green ?
1072 icm_x.direction[i].green : icm_y.direction[i].green);
1073 channel_features[BlueChannel].measure_of_correlation_1[i]=
1074 (icm_xy.direction[i].blue-icm_xy1.direction[i].blue)/
1075 (icm_x.direction[i].blue > icm_y.direction[i].blue ?
1076 icm_x.direction[i].blue : icm_y.direction[i].blue);
1077 if (image->matte != MagickFalse)
1078 channel_features[OpacityChannel].measure_of_correlation_1[i]=
1079 (icm_xy.direction[i].opacity-icm_xy1.direction[i].opacity)/
1080 (icm_x.direction[i].opacity > icm_y.direction[i].opacity ?
1081 icm_x.direction[i].opacity : icm_y.direction[i].opacity);
1082 if (image->colorspace == CMYKColorspace)
1083 channel_features[IndexChannel].measure_of_correlation_1[i]=
1084 (icm_xy.direction[i].index-icm_xy1.direction[i].index)/
1085 (icm_x.direction[i].index > icm_y.direction[i].index ?
1086 icm_x.direction[i].index : icm_y.direction[i].index);
1087 channel_features[RedChannel].measure_of_correlation_2[i]=
1088 (sqrt(fabs(1.0-exp(-2.0*(icm_xy2.direction[i].red-
1089 icm_xy.direction[i].red)))));
1090 channel_features[GreenChannel].measure_of_correlation_2[i]=
1091 (sqrt(fabs(1.0-exp(-2.0*(icm_xy2.direction[i].green-
1092 icm_xy.direction[i].green)))));
1093 channel_features[BlueChannel].measure_of_correlation_2[i]=
1094 (sqrt(fabs(1.0-exp(-2.0*(icm_xy2.direction[i].blue-
1095 icm_xy.direction[i].blue)))));
1096 if (image->matte != MagickFalse)
1097 channel_features[OpacityChannel].measure_of_correlation_2[i]=
1098 (sqrt(fabs(1.0-exp(-2.0*(icm_xy2.direction[i].opacity-
1099 icm_xy.direction[i].opacity)))));
1100 if (image->colorspace == CMYKColorspace)
1101 channel_features[IndexChannel].measure_of_correlation_2[i]=
1102 (sqrt(fabs(1.0-exp(-2.0*(icm_xy2.direction[i].index-
1103 icm_xy.direction[i].index)))));
cristye1897792010-01-29 02:05:50 +00001104 }
1105 /*
1106 Compute more texture features.
1107 */
cristy3a82f252010-01-26 20:31:51 +00001108#if defined(MAGICKCORE_OPENMP_SUPPORT)
1109 #pragma omp parallel for schedule(dynamic,4) shared(status)
1110#endif
cristybd822072010-01-27 00:30:00 +00001111 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +00001112 {
cristy7396d882010-01-27 02:37:56 +00001113 for (z=0; z < (long) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +00001114 {
1115 register long
cristybd822072010-01-27 00:30:00 +00001116 y;
cristy3a82f252010-01-26 20:31:51 +00001117
cristy7396d882010-01-27 02:37:56 +00001118 ChannelStatistics
cristybd822072010-01-27 00:30:00 +00001119 pixel;
1120
1121 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy7396d882010-01-27 02:37:56 +00001122 for (y=0; y < (long) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +00001123 {
cristybd822072010-01-27 00:30:00 +00001124 register long
1125 x;
1126
cristy7396d882010-01-27 02:37:56 +00001127 for (x=0; x < (long) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +00001128 {
1129 /*
1130 Contrast: amount of local variations present in an image.
1131 */
1132 if (((y-x) == z) || ((x-y) == z))
1133 {
cristy7396d882010-01-27 02:37:56 +00001134 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
1135 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
1136 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +00001137 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +00001138 pixel.direction[i].opacity+=
1139 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001140 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +00001141 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy3a82f252010-01-26 20:31:51 +00001142 }
1143 }
1144 }
cristy7396d882010-01-27 02:37:56 +00001145 channel_features[RedChannel].contrast[i]+=z*z*pixel.direction[i].red;
1146 channel_features[GreenChannel].contrast[i]+=z*z*pixel.direction[i].green;
1147 channel_features[BlueChannel].contrast[i]+=z*z*pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +00001148 if (image->matte != MagickFalse)
1149 channel_features[OpacityChannel].contrast[i]+=z*z*
cristy7396d882010-01-27 02:37:56 +00001150 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +00001151 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +00001152 channel_features[BlackChannel].contrast[i]+=z*z*
1153 pixel.direction[i].index;
cristy3a82f252010-01-26 20:31:51 +00001154 }
cristye0acabf2010-01-30 00:52:38 +00001155 /*
1156 Maximum Correlation Coefficient.
1157 */
1158 channel_features[RedChannel].maximum_correlation_coefficient[i]=
1159 sqrt((double) -1.0);
1160 channel_features[GreenChannel].maximum_correlation_coefficient[i]=
1161 sqrt((double) -1.0);
1162 channel_features[BlueChannel].maximum_correlation_coefficient[i]=
1163 sqrt((double) -1.0);
1164 if (image->matte != MagickFalse)
1165 channel_features[OpacityChannel].maximum_correlation_coefficient[i]=
1166 sqrt((double) -1.0);
1167 if (image->colorspace == CMYKColorspace)
1168 channel_features[IndexChannel].maximum_correlation_coefficient[i]=
1169 sqrt((double) -1.0);
cristy3a82f252010-01-26 20:31:51 +00001170 }
cristy7e9726d2010-01-26 02:08:40 +00001171 /*
cristyf2bf2c72010-01-25 19:54:15 +00001172 Relinquish resources.
1173 */
cristye0acabf2010-01-30 00:52:38 +00001174 mpm_x=(ChannelStatistics *) RelinquishMagickMemory(mpm_x);
1175 mpm_y=(ChannelStatistics *) RelinquishMagickMemory(mpm_y);
cristye1897792010-01-29 02:05:50 +00001176 sum_average=(ChannelStatistics *) RelinquishMagickMemory(sum_average);
cristy7396d882010-01-27 02:37:56 +00001177 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
1178 for (i=0; i < (long) number_grays; i++)
1179 cooccurrence[i]=(ChannelStatistics *)
1180 RelinquishMagickMemory(cooccurrence[i]);
1181 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
1182 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy3e2860c2010-01-24 01:36:30 +00001183 return(channel_features);
1184}