blob: 5ebd1122d5e088d0f3c14c85f9dad0bdffb196b6 [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*/
130MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image,
cristy7e9726d2010-01-26 02:08:40 +0000131 const unsigned long distance,ExceptionInfo *exception)
cristy3e2860c2010-01-24 01:36:30 +0000132{
cristy7396d882010-01-27 02:37:56 +0000133 typedef struct _ChannelStatistics
cristyf2bf2c72010-01-25 19:54:15 +0000134 {
cristy549a37e2010-01-26 15:24:15 +0000135 DoublePixelPacket
cristy7396d882010-01-27 02:37:56 +0000136 direction[4]; /* horizontal, vertical, left and right diagonals */
137 } ChannelStatistics;
cristyf2bf2c72010-01-25 19:54:15 +0000138
cristy2070fa52010-01-24 03:17:57 +0000139 CacheView
140 *image_view;
141
cristy3e2860c2010-01-24 01:36:30 +0000142 ChannelFeatures
143 *channel_features;
144
cristy7396d882010-01-27 02:37:56 +0000145 ChannelStatistics
146 **cooccurrence,
147 correlation,
cristycf5e6492010-01-28 02:45:28 +0000148 inverse_difference_moment,
cristy7396d882010-01-27 02:37:56 +0000149 mean,
150 *sum,
cristycf5e6492010-01-28 02:45:28 +0000151 sum_squares,
152 variance;
cristy7396d882010-01-27 02:37:56 +0000153
cristy2070fa52010-01-24 03:17:57 +0000154 LongPixelPacket
cristy7396d882010-01-27 02:37:56 +0000155 gray,
156 *grays;
cristy2070fa52010-01-24 03:17:57 +0000157
cristy3e2860c2010-01-24 01:36:30 +0000158 long
cristy3a82f252010-01-26 20:31:51 +0000159 y,
160 z;
cristy3e2860c2010-01-24 01:36:30 +0000161
cristy2070fa52010-01-24 03:17:57 +0000162 MagickBooleanType
163 status;
164
165 register long
166 i;
167
cristy3e2860c2010-01-24 01:36:30 +0000168 size_t
169 length;
170
cristyf2bf2c72010-01-25 19:54:15 +0000171 unsigned long
cristy7396d882010-01-27 02:37:56 +0000172 number_grays;
cristyf2bf2c72010-01-25 19:54:15 +0000173
cristy3e2860c2010-01-24 01:36:30 +0000174 assert(image != (Image *) NULL);
175 assert(image->signature == MagickSignature);
176 if (image->debug != MagickFalse)
177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000178 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
179 return((ChannelFeatures *) NULL);
cristy3e2860c2010-01-24 01:36:30 +0000180 length=AllChannels+1UL;
181 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
182 sizeof(*channel_features));
183 if (channel_features == (ChannelFeatures *) NULL)
184 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
185 (void) ResetMagickMemory(channel_features,0,length*
186 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000187 /*
cristy7396d882010-01-27 02:37:56 +0000188 Form grays.
cristy2070fa52010-01-24 03:17:57 +0000189 */
cristy7396d882010-01-27 02:37:56 +0000190 grays=(LongPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
191 if (grays == (LongPixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000192 {
193 (void) ThrowMagickException(exception,GetMagickModule(),
194 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
195 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
196 channel_features);
197 return(channel_features);
198 }
199 for (i=0; i <= (long) MaxMap; i++)
200 {
cristy7396d882010-01-27 02:37:56 +0000201 grays[i].red=(~0UL);
202 grays[i].green=(~0UL);
203 grays[i].blue=(~0UL);
204 grays[i].opacity=(~0UL);
205 grays[i].index=(~0UL);
cristy2070fa52010-01-24 03:17:57 +0000206 }
207 status=MagickTrue;
208 image_view=AcquireCacheView(image);
209#if defined(MAGICKCORE_OPENMP_SUPPORT)
210 #pragma omp parallel for schedule(dynamic,4) shared(status)
211#endif
cristy3e2860c2010-01-24 01:36:30 +0000212 for (y=0; y < (long) image->rows; y++)
213 {
214 register const IndexPacket
215 *restrict indexes;
216
217 register const PixelPacket
218 *restrict p;
219
220 register long
221 x;
222
cristy2070fa52010-01-24 03:17:57 +0000223 if (status == MagickFalse)
224 continue;
225 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3e2860c2010-01-24 01:36:30 +0000226 if (p == (const PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000227 {
228 status=MagickFalse;
229 continue;
230 }
231 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3e2860c2010-01-24 01:36:30 +0000232 for (x=0; x < (long) image->columns; x++)
233 {
cristy7396d882010-01-27 02:37:56 +0000234 grays[ScaleQuantumToMap(p->red)].red=ScaleQuantumToMap(p->red);
235 grays[ScaleQuantumToMap(p->green)].green=ScaleQuantumToMap(p->green);
236 grays[ScaleQuantumToMap(p->blue)].blue=ScaleQuantumToMap(p->blue);
cristy2070fa52010-01-24 03:17:57 +0000237 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000238 grays[ScaleQuantumToMap(p->opacity)].opacity=
cristy2070fa52010-01-24 03:17:57 +0000239 ScaleQuantumToMap(p->opacity);
240 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000241 grays[ScaleQuantumToMap(indexes[x])].index=
cristy2070fa52010-01-24 03:17:57 +0000242 ScaleQuantumToMap(indexes[x]);
cristy3e2860c2010-01-24 01:36:30 +0000243 p++;
244 }
245 }
cristy30c510a2010-01-24 03:43:00 +0000246 image_view=DestroyCacheView(image_view);
247 if (status == MagickFalse)
248 {
cristy7396d882010-01-27 02:37:56 +0000249 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000250 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
251 channel_features);
252 return(channel_features);
253 }
cristy7396d882010-01-27 02:37:56 +0000254 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristy2070fa52010-01-24 03:17:57 +0000255 for (i=0; i <= (long) MaxMap; i++)
256 {
cristy7396d882010-01-27 02:37:56 +0000257 if (grays[i].red != ~0UL)
258 grays[gray.red++].red=grays[i].red;
259 if (grays[i].green != ~0UL)
260 grays[gray.green++].green=grays[i].green;
261 if (grays[i].blue != ~0UL)
262 grays[gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000263 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000264 if (grays[i].opacity != ~0UL)
265 grays[gray.opacity++].opacity=grays[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000266 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000267 if (grays[i].index != ~0UL)
268 grays[gray.index++].index=grays[i].index;
cristy2070fa52010-01-24 03:17:57 +0000269 }
cristyf2bf2c72010-01-25 19:54:15 +0000270 /*
271 Allocate spatial dependence matrix.
272 */
cristy7396d882010-01-27 02:37:56 +0000273 number_grays=gray.red;
274 if (gray.green > number_grays)
275 number_grays=gray.green;
276 if (gray.blue > number_grays)
277 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000278 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000279 if (gray.opacity > number_grays)
280 number_grays=gray.opacity;
cristyf2bf2c72010-01-25 19:54:15 +0000281 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000282 if (gray.index > number_grays)
283 number_grays=gray.index;
284 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
285 sizeof(*cooccurrence));
286 if (cooccurrence == (ChannelStatistics **) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000287 {
288 (void) ThrowMagickException(exception,GetMagickModule(),
289 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7396d882010-01-27 02:37:56 +0000290 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000291 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
292 channel_features);
293 return(channel_features);
294 }
cristy7396d882010-01-27 02:37:56 +0000295 for (i=0; i < (long) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000296 {
cristy7396d882010-01-27 02:37:56 +0000297 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
298 sizeof(**cooccurrence));
299 if (cooccurrence[i] == (ChannelStatistics *) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000300 break;
cristy7396d882010-01-27 02:37:56 +0000301 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
302 sizeof(*cooccurrence));
cristyf2bf2c72010-01-25 19:54:15 +0000303 }
cristy7396d882010-01-27 02:37:56 +0000304 if (i < (long) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000305 {
306 (void) ThrowMagickException(exception,GetMagickModule(),
307 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
308 for (i--; i >= 0; i--)
cristy7396d882010-01-27 02:37:56 +0000309 cooccurrence[i]=(ChannelStatistics *)
310 RelinquishMagickMemory(cooccurrence[i]);
311 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
312 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000313 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
314 channel_features);
315 return(channel_features);
316 }
317 /*
318 Initialize spatial dependence matrix.
319 */
320 status=MagickTrue;
321 image_view=AcquireCacheView(image);
322#if defined(MAGICKCORE_OPENMP_SUPPORT)
323 #pragma omp parallel for schedule(dynamic,4) shared(status)
324#endif
325 for (y=0; y < (long) image->rows; y++)
326 {
327 long
328 u,
329 v;
330
331 register const IndexPacket
332 *restrict indexes;
333
334 register const PixelPacket
335 *restrict p;
336
337 register long
338 x;
339
cristy7e9726d2010-01-26 02:08:40 +0000340 ssize_t
341 offset;
342
cristyf2bf2c72010-01-25 19:54:15 +0000343 if (status == MagickFalse)
344 continue;
cristy7e9726d2010-01-26 02:08:40 +0000345 p=GetCacheViewVirtualPixels(image_view,-(long) distance,y,image->columns+
346 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000347 if (p == (const PixelPacket *) NULL)
348 {
349 status=MagickFalse;
350 continue;
351 }
352 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000353 p+=distance;
354 indexes+=distance;
cristyf2bf2c72010-01-25 19:54:15 +0000355 for (x=0; x < (long) image->columns; x++)
356 {
357 for (i=0; i < 4; i++)
358 {
cristy7e9726d2010-01-26 02:08:40 +0000359 switch (i)
360 {
361 case 0:
cristy549a37e2010-01-26 15:24:15 +0000362 default:
cristy7e9726d2010-01-26 02:08:40 +0000363 {
364 /*
cristy7396d882010-01-27 02:37:56 +0000365 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000366 */
367 offset=(ssize_t) distance;
368 break;
369 }
370 case 1:
371 {
372 /*
cristy7396d882010-01-27 02:37:56 +0000373 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000374 */
cristy7396d882010-01-27 02:37:56 +0000375 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000376 break;
377 }
378 case 2:
379 {
380 /*
cristy7396d882010-01-27 02:37:56 +0000381 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000382 */
cristy7396d882010-01-27 02:37:56 +0000383 offset=(ssize_t) (image->columns+2*distance)-distance;
cristy7e9726d2010-01-26 02:08:40 +0000384 break;
385 }
386 case 3:
387 {
388 /*
cristy7396d882010-01-27 02:37:56 +0000389 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000390 */
391 offset=(ssize_t) (image->columns+2*distance)+distance;
392 break;
393 }
394 }
395 u=0;
396 v=0;
cristy7396d882010-01-27 02:37:56 +0000397 while (grays[u].red != ScaleQuantumToMap(p->red))
cristy7e9726d2010-01-26 02:08:40 +0000398 u++;
cristy7396d882010-01-27 02:37:56 +0000399 while (grays[v].red != ScaleQuantumToMap((p+offset)->red))
cristy7e9726d2010-01-26 02:08:40 +0000400 v++;
cristy7396d882010-01-27 02:37:56 +0000401 cooccurrence[u][v].direction[i].red++;
402 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000403 u=0;
404 v=0;
cristy7396d882010-01-27 02:37:56 +0000405 while (grays[u].green != ScaleQuantumToMap(p->green))
cristy7e9726d2010-01-26 02:08:40 +0000406 u++;
cristy7396d882010-01-27 02:37:56 +0000407 while (grays[v].green != ScaleQuantumToMap((p+offset)->green))
cristy7e9726d2010-01-26 02:08:40 +0000408 v++;
cristy7396d882010-01-27 02:37:56 +0000409 cooccurrence[u][v].direction[i].green++;
410 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000411 u=0;
412 v=0;
cristy7396d882010-01-27 02:37:56 +0000413 while (grays[u].blue != ScaleQuantumToMap(p->blue))
cristy7e9726d2010-01-26 02:08:40 +0000414 u++;
cristy7396d882010-01-27 02:37:56 +0000415 while (grays[v].blue != ScaleQuantumToMap((p+offset)->blue))
cristy7e9726d2010-01-26 02:08:40 +0000416 v++;
cristy7396d882010-01-27 02:37:56 +0000417 cooccurrence[u][v].direction[i].blue++;
418 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000419 if (image->matte != MagickFalse)
420 {
421 u=0;
422 v=0;
cristy7396d882010-01-27 02:37:56 +0000423 while (grays[u].opacity != ScaleQuantumToMap(p->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000424 u++;
cristy7396d882010-01-27 02:37:56 +0000425 while (grays[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000426 v++;
cristy7396d882010-01-27 02:37:56 +0000427 cooccurrence[u][v].direction[i].opacity++;
428 cooccurrence[v][u].direction[i].opacity++;
cristy7e9726d2010-01-26 02:08:40 +0000429 }
430 if (image->colorspace == CMYKColorspace)
431 {
432 u=0;
433 v=0;
cristy7396d882010-01-27 02:37:56 +0000434 while (grays[u].index != ScaleQuantumToMap(indexes[x]))
cristy7e9726d2010-01-26 02:08:40 +0000435 u++;
cristy7396d882010-01-27 02:37:56 +0000436 while (grays[v].index != ScaleQuantumToMap(indexes[x+offset]))
cristy7e9726d2010-01-26 02:08:40 +0000437 v++;
cristy7396d882010-01-27 02:37:56 +0000438 cooccurrence[u][v].direction[i].index++;
439 cooccurrence[v][u].direction[i].index++;
cristy7e9726d2010-01-26 02:08:40 +0000440 }
cristyf2bf2c72010-01-25 19:54:15 +0000441 }
cristy7e9726d2010-01-26 02:08:40 +0000442 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000443 }
444 }
445 image_view=DestroyCacheView(image_view);
446 if (status == MagickFalse)
447 {
448 (void) ThrowMagickException(exception,GetMagickModule(),
449 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7396d882010-01-27 02:37:56 +0000450 for (i=0; i < (long) number_grays; i++)
451 cooccurrence[i]=(ChannelStatistics *)
452 RelinquishMagickMemory(cooccurrence[i]);
453 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
454 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000455 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
456 channel_features);
457 return(channel_features);
458 }
459 /*
cristy7e9726d2010-01-26 02:08:40 +0000460 Normalize spatial dependence matrix.
461 */
462#if defined(MAGICKCORE_OPENMP_SUPPORT)
463 #pragma omp parallel for schedule(dynamic,4) shared(status)
464#endif
cristybd822072010-01-27 00:30:00 +0000465 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000466 {
cristy549a37e2010-01-26 15:24:15 +0000467 double
468 normalize;
469
cristybd822072010-01-27 00:30:00 +0000470 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000471 {
cristybd822072010-01-27 00:30:00 +0000472 case 0:
473 default:
cristy7e9726d2010-01-26 02:08:40 +0000474 {
cristybd822072010-01-27 00:30:00 +0000475 /*
cristy7396d882010-01-27 02:37:56 +0000476 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000477 */
478 normalize=2.0*image->rows*(image->columns-distance);
479 break;
480 }
481 case 1:
482 {
483 /*
cristy7396d882010-01-27 02:37:56 +0000484 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000485 */
cristy7396d882010-01-27 02:37:56 +0000486 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000487 break;
488 }
489 case 2:
490 {
491 /*
cristy7396d882010-01-27 02:37:56 +0000492 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000493 */
cristy7396d882010-01-27 02:37:56 +0000494 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000495 break;
496 }
497 case 3:
498 {
499 /*
cristy7396d882010-01-27 02:37:56 +0000500 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000501 */
502 normalize=2.0*(image->rows-distance)*(image->columns-distance);
503 break;
504 }
505 }
cristy7396d882010-01-27 02:37:56 +0000506 for (y=0; y < (long) number_grays; y++)
cristybd822072010-01-27 00:30:00 +0000507 {
508 register long
509 x;
510
cristy7396d882010-01-27 02:37:56 +0000511 for (x=0; x < (long) number_grays; x++)
cristybd822072010-01-27 00:30:00 +0000512 {
cristy7396d882010-01-27 02:37:56 +0000513 cooccurrence[x][y].direction[i].red/=normalize;
514 cooccurrence[x][y].direction[i].green/=normalize;
515 cooccurrence[x][y].direction[i].blue/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000516 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000517 cooccurrence[x][y].direction[i].opacity/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000518 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000519 cooccurrence[x][y].direction[i].index/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000520 }
521 }
522 }
523 /*
524 Compute texture features.
525 */
cristy7396d882010-01-27 02:37:56 +0000526 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
527 if (sum == (ChannelStatistics *) NULL)
528 {
529 (void) ThrowMagickException(exception,GetMagickModule(),
530 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
531 for (i=0; i < (long) number_grays; i++)
532 cooccurrence[i]=(ChannelStatistics *)
533 RelinquishMagickMemory(cooccurrence[i]);
534 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
535 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
536 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
537 channel_features);
538 return(channel_features);
539 }
540 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
541 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
542 (void) ResetMagickMemory(&mean,0,sizeof(mean));
543 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
cristycf5e6492010-01-28 02:45:28 +0000544 (void) ResetMagickMemory(&inverse_difference_moment,0,
545 sizeof(inverse_difference_moment));
cristy3a82f252010-01-26 20:31:51 +0000546#if defined(MAGICKCORE_OPENMP_SUPPORT)
547 #pragma omp parallel for schedule(dynamic,4) shared(status)
548#endif
cristybd822072010-01-27 00:30:00 +0000549 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000550 {
551 register long
cristybd822072010-01-27 00:30:00 +0000552 y;
cristy549a37e2010-01-26 15:24:15 +0000553
cristy7396d882010-01-27 02:37:56 +0000554 for (y=0; y < (long) number_grays; y++)
cristy549a37e2010-01-26 15:24:15 +0000555 {
cristybd822072010-01-27 00:30:00 +0000556 register long
557 x;
558
cristy7396d882010-01-27 02:37:56 +0000559 for (x=0; x < (long) number_grays; x++)
cristy549a37e2010-01-26 15:24:15 +0000560 {
561 /*
cristy3a82f252010-01-26 20:31:51 +0000562 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000563 */
564 channel_features[RedChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000565 cooccurrence[x][y].direction[i].red*
566 cooccurrence[x][y].direction[i].red;
cristy549a37e2010-01-26 15:24:15 +0000567 channel_features[GreenChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000568 cooccurrence[x][y].direction[i].green*
569 cooccurrence[x][y].direction[i].green;
cristy549a37e2010-01-26 15:24:15 +0000570 channel_features[BlueChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000571 cooccurrence[x][y].direction[i].blue*
572 cooccurrence[x][y].direction[i].blue;
cristy549a37e2010-01-26 15:24:15 +0000573 if (image->matte != MagickFalse)
574 channel_features[OpacityChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000575 cooccurrence[x][y].direction[i].opacity*
576 cooccurrence[x][y].direction[i].opacity;
cristy549a37e2010-01-26 15:24:15 +0000577 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000578 channel_features[BlackChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000579 cooccurrence[x][y].direction[i].index*
580 cooccurrence[x][y].direction[i].index;
581 /*
582 Correlation: measure of linear-dependencies in the image.
583 */
584 sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000585 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
cristycdf8e1b2010-01-28 01:52:33 +0000586 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
587 if (image->matte != MagickFalse)
588 sum[y].direction[i].opacity+=cooccurrence[x][y].direction[i].opacity;
589 if (image->colorspace == CMYKColorspace)
590 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
591 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000592 correlation.direction[i].green+=x*y*
593 cooccurrence[x][y].direction[i].green;
cristy7396d882010-01-27 02:37:56 +0000594 correlation.direction[i].blue+=x*y*
595 cooccurrence[x][y].direction[i].blue;
cristy7396d882010-01-27 02:37:56 +0000596 if (image->matte != MagickFalse)
cristycdf8e1b2010-01-28 01:52:33 +0000597 correlation.direction[i].opacity+=x*y*
598 cooccurrence[x][y].direction[i].opacity;
cristy7396d882010-01-27 02:37:56 +0000599 if (image->colorspace == CMYKColorspace)
cristycdf8e1b2010-01-28 01:52:33 +0000600 correlation.direction[i].index+=x*y*
601 cooccurrence[x][y].direction[i].index;
cristycf5e6492010-01-28 02:45:28 +0000602 /*
603 Inverse Difference Moment.
604 */
605 inverse_difference_moment.direction[i].red+=
606 cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
607 inverse_difference_moment.direction[i].green+=
608 cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
609 inverse_difference_moment.direction[i].blue+=
610 cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
611 if (image->matte != MagickFalse)
612 inverse_difference_moment.direction[i].opacity+=
613 cooccurrence[x][y].direction[i].opacity/((y-x)*(y-x)+1);
614 if (image->colorspace == CMYKColorspace)
615 inverse_difference_moment.direction[i].index+=
616 cooccurrence[x][y].direction[i].index/((y-x)*(y-x)+1);
cristy7e9726d2010-01-26 02:08:40 +0000617 }
cristycdf8e1b2010-01-28 01:52:33 +0000618 mean.direction[i].red+=y*sum[y].direction[i].red;
619 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
620 mean.direction[i].green+=y*sum[y].direction[i].green;
621 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
622 mean.direction[i].blue+=y*sum[y].direction[i].blue;
623 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
624 if (image->matte != MagickFalse)
625 {
626 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
627 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
628 }
629 if (image->colorspace == CMYKColorspace)
630 {
631 mean.direction[i].index+=y*sum[y].direction[i].index;
632 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
633 }
cristy7e9726d2010-01-26 02:08:40 +0000634 }
cristycdf8e1b2010-01-28 01:52:33 +0000635 /*
636 Correlation: measure of linear-dependencies in the image.
637 */
638 channel_features[RedChannel].correlation[i]=
639 (correlation.direction[i].red-mean.direction[i].red*
640 mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
641 (mean.direction[i].red*mean.direction[i].red))*sqrt(
642 sum_squares.direction[i].red-(mean.direction[i].red*
643 mean.direction[i].red)));
644 channel_features[GreenChannel].correlation[i]=
645 (correlation.direction[i].green-mean.direction[i].green*
646 mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
647 (mean.direction[i].green*mean.direction[i].green))*sqrt(
648 sum_squares.direction[i].green-(mean.direction[i].green*
649 mean.direction[i].green)));
650 channel_features[BlueChannel].correlation[i]=
651 (correlation.direction[i].blue-mean.direction[i].blue*
652 mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
653 (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
654 sum_squares.direction[i].blue-(mean.direction[i].blue*
655 mean.direction[i].blue)));
656 if (image->matte != MagickFalse)
657 channel_features[OpacityChannel].correlation[i]=
658 (correlation.direction[i].opacity-mean.direction[i].opacity*
659 mean.direction[i].opacity)/(sqrt(sum_squares.direction[i].opacity-
660 (mean.direction[i].opacity*mean.direction[i].opacity))*sqrt(
661 sum_squares.direction[i].opacity-(mean.direction[i].opacity*
662 mean.direction[i].opacity)));
663 if (image->colorspace == CMYKColorspace)
664 channel_features[IndexChannel].correlation[i]=
665 (correlation.direction[i].index-mean.direction[i].index*
666 mean.direction[i].index)/(sqrt(sum_squares.direction[i].index-
667 (mean.direction[i].index*mean.direction[i].index))*sqrt(
668 sum_squares.direction[i].index-(mean.direction[i].index*
669 mean.direction[i].index)));
cristy7e9726d2010-01-26 02:08:40 +0000670 }
cristycf5e6492010-01-28 02:45:28 +0000671 /*
672 Compute more texture features.
673 */
674 (void) ResetMagickMemory(&variance,0,sizeof(variance));
675#if defined(MAGICKCORE_OPENMP_SUPPORT)
676 #pragma omp parallel for schedule(dynamic,4) shared(status)
677#endif
678 for (i=0; i < 4; i++)
679 {
680 register long
681 y;
682
683 for (y=0; y < (long) number_grays; y++)
684 {
685 register long
686 x;
687
688 for (x=0; x < (long) number_grays; x++)
689 {
690 /*
691 Sum of Squares: Variance
692 */
693 variance.direction[i].red+=(y-mean.direction[i].red+1)*
694 (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
695 variance.direction[i].green+=(y-mean.direction[i].green+1)*
696 (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
697 variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
698 (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
699 if (image->matte != MagickFalse)
700 variance.direction[i].opacity+=(y-mean.direction[i].opacity+1)*
701 (y-mean.direction[i].opacity+1)*
702 cooccurrence[x][y].direction[i].opacity;
703 if (image->colorspace == CMYKColorspace)
704 variance.direction[i].index+=(y-mean.direction[i].index+1)*
705 (y-mean.direction[i].index+1)*cooccurrence[x][y].direction[i].index;
706 }
707 }
708 channel_features[RedChannel].variance_sum_of_squares[i]=
709 variance.direction[i].red;
710 channel_features[GreenChannel].variance_sum_of_squares[i]=
711 variance.direction[i].green;
712 channel_features[BlueChannel].variance_sum_of_squares[i]=
713 variance.direction[i].blue;
714 if (image->matte != MagickFalse)
715 channel_features[RedChannel].variance_sum_of_squares[i]=
716 variance.direction[i].opacity;
717 if (image->colorspace == CMYKColorspace)
718 channel_features[RedChannel].variance_sum_of_squares[i]=
719 variance.direction[i].index;
720 }
721 /*
722 Compute more texture features.
723 */
cristy3a82f252010-01-26 20:31:51 +0000724#if defined(MAGICKCORE_OPENMP_SUPPORT)
725 #pragma omp parallel for schedule(dynamic,4) shared(status)
726#endif
cristybd822072010-01-27 00:30:00 +0000727 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +0000728 {
cristy7396d882010-01-27 02:37:56 +0000729 for (z=0; z < (long) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +0000730 {
731 register long
cristybd822072010-01-27 00:30:00 +0000732 y;
cristy3a82f252010-01-26 20:31:51 +0000733
cristy7396d882010-01-27 02:37:56 +0000734 ChannelStatistics
cristybd822072010-01-27 00:30:00 +0000735 pixel;
736
737 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy7396d882010-01-27 02:37:56 +0000738 for (y=0; y < (long) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +0000739 {
cristybd822072010-01-27 00:30:00 +0000740 register long
741 x;
742
cristy7396d882010-01-27 02:37:56 +0000743 for (x=0; x < (long) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +0000744 {
745 /*
746 Contrast: amount of local variations present in an image.
747 */
748 if (((y-x) == z) || ((x-y) == z))
749 {
cristy7396d882010-01-27 02:37:56 +0000750 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
751 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
752 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000753 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000754 pixel.direction[i].opacity+=
755 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000756 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000757 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000758 }
759 }
760 }
cristy7396d882010-01-27 02:37:56 +0000761 channel_features[RedChannel].contrast[i]+=z*z*pixel.direction[i].red;
762 channel_features[GreenChannel].contrast[i]+=z*z*pixel.direction[i].green;
763 channel_features[BlueChannel].contrast[i]+=z*z*pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000764 if (image->matte != MagickFalse)
765 channel_features[OpacityChannel].contrast[i]+=z*z*
cristy7396d882010-01-27 02:37:56 +0000766 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000767 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000768 channel_features[BlackChannel].contrast[i]+=z*z*
769 pixel.direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000770 }
771 }
cristy7e9726d2010-01-26 02:08:40 +0000772 /*
cristyf2bf2c72010-01-25 19:54:15 +0000773 Relinquish resources.
774 */
cristy7396d882010-01-27 02:37:56 +0000775 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
776 for (i=0; i < (long) number_grays; i++)
777 cooccurrence[i]=(ChannelStatistics *)
778 RelinquishMagickMemory(cooccurrence[i]);
779 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
780 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy3e2860c2010-01-24 01:36:30 +0000781 return(channel_features);
782}