blob: 4ae625854756cc86a1a688562cfd1c9c57e5a0bd [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,
148 mean,
149 *sum,
150 sum_squares;
151
cristy2070fa52010-01-24 03:17:57 +0000152 LongPixelPacket
cristy7396d882010-01-27 02:37:56 +0000153 gray,
154 *grays;
cristy2070fa52010-01-24 03:17:57 +0000155
cristy3e2860c2010-01-24 01:36:30 +0000156 long
cristy3a82f252010-01-26 20:31:51 +0000157 y,
158 z;
cristy3e2860c2010-01-24 01:36:30 +0000159
cristy2070fa52010-01-24 03:17:57 +0000160 MagickBooleanType
161 status;
162
163 register long
164 i;
165
cristy3e2860c2010-01-24 01:36:30 +0000166 size_t
167 length;
168
cristyf2bf2c72010-01-25 19:54:15 +0000169 unsigned long
cristy7396d882010-01-27 02:37:56 +0000170 number_grays;
cristyf2bf2c72010-01-25 19:54:15 +0000171
cristy3e2860c2010-01-24 01:36:30 +0000172 assert(image != (Image *) NULL);
173 assert(image->signature == MagickSignature);
174 if (image->debug != MagickFalse)
175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000176 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
177 return((ChannelFeatures *) NULL);
cristy3e2860c2010-01-24 01:36:30 +0000178 length=AllChannels+1UL;
179 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
180 sizeof(*channel_features));
181 if (channel_features == (ChannelFeatures *) NULL)
182 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
183 (void) ResetMagickMemory(channel_features,0,length*
184 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000185 /*
cristy7396d882010-01-27 02:37:56 +0000186 Form grays.
cristy2070fa52010-01-24 03:17:57 +0000187 */
cristy7396d882010-01-27 02:37:56 +0000188 grays=(LongPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
189 if (grays == (LongPixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000190 {
191 (void) ThrowMagickException(exception,GetMagickModule(),
192 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
193 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
194 channel_features);
195 return(channel_features);
196 }
197 for (i=0; i <= (long) MaxMap; i++)
198 {
cristy7396d882010-01-27 02:37:56 +0000199 grays[i].red=(~0UL);
200 grays[i].green=(~0UL);
201 grays[i].blue=(~0UL);
202 grays[i].opacity=(~0UL);
203 grays[i].index=(~0UL);
cristy2070fa52010-01-24 03:17:57 +0000204 }
205 status=MagickTrue;
206 image_view=AcquireCacheView(image);
207#if defined(MAGICKCORE_OPENMP_SUPPORT)
208 #pragma omp parallel for schedule(dynamic,4) shared(status)
209#endif
cristy3e2860c2010-01-24 01:36:30 +0000210 for (y=0; y < (long) image->rows; y++)
211 {
212 register const IndexPacket
213 *restrict indexes;
214
215 register const PixelPacket
216 *restrict p;
217
218 register long
219 x;
220
cristy2070fa52010-01-24 03:17:57 +0000221 if (status == MagickFalse)
222 continue;
223 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3e2860c2010-01-24 01:36:30 +0000224 if (p == (const PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000225 {
226 status=MagickFalse;
227 continue;
228 }
229 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3e2860c2010-01-24 01:36:30 +0000230 for (x=0; x < (long) image->columns; x++)
231 {
cristy7396d882010-01-27 02:37:56 +0000232 grays[ScaleQuantumToMap(p->red)].red=ScaleQuantumToMap(p->red);
233 grays[ScaleQuantumToMap(p->green)].green=ScaleQuantumToMap(p->green);
234 grays[ScaleQuantumToMap(p->blue)].blue=ScaleQuantumToMap(p->blue);
cristy2070fa52010-01-24 03:17:57 +0000235 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000236 grays[ScaleQuantumToMap(p->opacity)].opacity=
cristy2070fa52010-01-24 03:17:57 +0000237 ScaleQuantumToMap(p->opacity);
238 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000239 grays[ScaleQuantumToMap(indexes[x])].index=
cristy2070fa52010-01-24 03:17:57 +0000240 ScaleQuantumToMap(indexes[x]);
cristy3e2860c2010-01-24 01:36:30 +0000241 p++;
242 }
243 }
cristy30c510a2010-01-24 03:43:00 +0000244 image_view=DestroyCacheView(image_view);
245 if (status == MagickFalse)
246 {
cristy7396d882010-01-27 02:37:56 +0000247 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy30c510a2010-01-24 03:43:00 +0000248 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
249 channel_features);
250 return(channel_features);
251 }
cristy7396d882010-01-27 02:37:56 +0000252 (void) ResetMagickMemory(&gray,0,sizeof(gray));
cristy2070fa52010-01-24 03:17:57 +0000253 for (i=0; i <= (long) MaxMap; i++)
254 {
cristy7396d882010-01-27 02:37:56 +0000255 if (grays[i].red != ~0UL)
256 grays[gray.red++].red=grays[i].red;
257 if (grays[i].green != ~0UL)
258 grays[gray.green++].green=grays[i].green;
259 if (grays[i].blue != ~0UL)
260 grays[gray.blue++].blue=grays[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000261 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000262 if (grays[i].opacity != ~0UL)
263 grays[gray.opacity++].opacity=grays[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000264 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000265 if (grays[i].index != ~0UL)
266 grays[gray.index++].index=grays[i].index;
cristy2070fa52010-01-24 03:17:57 +0000267 }
cristyf2bf2c72010-01-25 19:54:15 +0000268 /*
269 Allocate spatial dependence matrix.
270 */
cristy7396d882010-01-27 02:37:56 +0000271 number_grays=gray.red;
272 if (gray.green > number_grays)
273 number_grays=gray.green;
274 if (gray.blue > number_grays)
275 number_grays=gray.blue;
cristyf2bf2c72010-01-25 19:54:15 +0000276 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000277 if (gray.opacity > number_grays)
278 number_grays=gray.opacity;
cristyf2bf2c72010-01-25 19:54:15 +0000279 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000280 if (gray.index > number_grays)
281 number_grays=gray.index;
282 cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
283 sizeof(*cooccurrence));
284 if (cooccurrence == (ChannelStatistics **) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000285 {
286 (void) ThrowMagickException(exception,GetMagickModule(),
287 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7396d882010-01-27 02:37:56 +0000288 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000289 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
290 channel_features);
291 return(channel_features);
292 }
cristy7396d882010-01-27 02:37:56 +0000293 for (i=0; i < (long) number_grays; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000294 {
cristy7396d882010-01-27 02:37:56 +0000295 cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
296 sizeof(**cooccurrence));
297 if (cooccurrence[i] == (ChannelStatistics *) NULL)
cristyf2bf2c72010-01-25 19:54:15 +0000298 break;
cristy7396d882010-01-27 02:37:56 +0000299 (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
300 sizeof(*cooccurrence));
cristyf2bf2c72010-01-25 19:54:15 +0000301 }
cristy7396d882010-01-27 02:37:56 +0000302 if (i < (long) number_grays)
cristyf2bf2c72010-01-25 19:54:15 +0000303 {
304 (void) ThrowMagickException(exception,GetMagickModule(),
305 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
306 for (i--; i >= 0; i--)
cristy7396d882010-01-27 02:37:56 +0000307 cooccurrence[i]=(ChannelStatistics *)
308 RelinquishMagickMemory(cooccurrence[i]);
309 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
310 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000311 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
312 channel_features);
313 return(channel_features);
314 }
315 /*
316 Initialize spatial dependence matrix.
317 */
318 status=MagickTrue;
319 image_view=AcquireCacheView(image);
320#if defined(MAGICKCORE_OPENMP_SUPPORT)
321 #pragma omp parallel for schedule(dynamic,4) shared(status)
322#endif
323 for (y=0; y < (long) image->rows; y++)
324 {
325 long
326 u,
327 v;
328
329 register const IndexPacket
330 *restrict indexes;
331
332 register const PixelPacket
333 *restrict p;
334
335 register long
336 x;
337
cristy7e9726d2010-01-26 02:08:40 +0000338 ssize_t
339 offset;
340
cristyf2bf2c72010-01-25 19:54:15 +0000341 if (status == MagickFalse)
342 continue;
cristy7e9726d2010-01-26 02:08:40 +0000343 p=GetCacheViewVirtualPixels(image_view,-(long) distance,y,image->columns+
344 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000345 if (p == (const PixelPacket *) NULL)
346 {
347 status=MagickFalse;
348 continue;
349 }
350 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000351 p+=distance;
352 indexes+=distance;
cristyf2bf2c72010-01-25 19:54:15 +0000353 for (x=0; x < (long) image->columns; x++)
354 {
355 for (i=0; i < 4; i++)
356 {
cristy7e9726d2010-01-26 02:08:40 +0000357 switch (i)
358 {
359 case 0:
cristy549a37e2010-01-26 15:24:15 +0000360 default:
cristy7e9726d2010-01-26 02:08:40 +0000361 {
362 /*
cristy7396d882010-01-27 02:37:56 +0000363 Horizontal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000364 */
365 offset=(ssize_t) distance;
366 break;
367 }
368 case 1:
369 {
370 /*
cristy7396d882010-01-27 02:37:56 +0000371 Vertical adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000372 */
cristy7396d882010-01-27 02:37:56 +0000373 offset=(ssize_t) (image->columns+2*distance);
cristy7e9726d2010-01-26 02:08:40 +0000374 break;
375 }
376 case 2:
377 {
378 /*
cristy7396d882010-01-27 02:37:56 +0000379 Right diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000380 */
cristy7396d882010-01-27 02:37:56 +0000381 offset=(ssize_t) (image->columns+2*distance)-distance;
cristy7e9726d2010-01-26 02:08:40 +0000382 break;
383 }
384 case 3:
385 {
386 /*
cristy7396d882010-01-27 02:37:56 +0000387 Left diagonal adjacency.
cristy7e9726d2010-01-26 02:08:40 +0000388 */
389 offset=(ssize_t) (image->columns+2*distance)+distance;
390 break;
391 }
392 }
393 u=0;
394 v=0;
cristy7396d882010-01-27 02:37:56 +0000395 while (grays[u].red != ScaleQuantumToMap(p->red))
cristy7e9726d2010-01-26 02:08:40 +0000396 u++;
cristy7396d882010-01-27 02:37:56 +0000397 while (grays[v].red != ScaleQuantumToMap((p+offset)->red))
cristy7e9726d2010-01-26 02:08:40 +0000398 v++;
cristy7396d882010-01-27 02:37:56 +0000399 cooccurrence[u][v].direction[i].red++;
400 cooccurrence[v][u].direction[i].red++;
cristy7e9726d2010-01-26 02:08:40 +0000401 u=0;
402 v=0;
cristy7396d882010-01-27 02:37:56 +0000403 while (grays[u].green != ScaleQuantumToMap(p->green))
cristy7e9726d2010-01-26 02:08:40 +0000404 u++;
cristy7396d882010-01-27 02:37:56 +0000405 while (grays[v].green != ScaleQuantumToMap((p+offset)->green))
cristy7e9726d2010-01-26 02:08:40 +0000406 v++;
cristy7396d882010-01-27 02:37:56 +0000407 cooccurrence[u][v].direction[i].green++;
408 cooccurrence[v][u].direction[i].green++;
cristy7e9726d2010-01-26 02:08:40 +0000409 u=0;
410 v=0;
cristy7396d882010-01-27 02:37:56 +0000411 while (grays[u].blue != ScaleQuantumToMap(p->blue))
cristy7e9726d2010-01-26 02:08:40 +0000412 u++;
cristy7396d882010-01-27 02:37:56 +0000413 while (grays[v].blue != ScaleQuantumToMap((p+offset)->blue))
cristy7e9726d2010-01-26 02:08:40 +0000414 v++;
cristy7396d882010-01-27 02:37:56 +0000415 cooccurrence[u][v].direction[i].blue++;
416 cooccurrence[v][u].direction[i].blue++;
cristy7e9726d2010-01-26 02:08:40 +0000417 if (image->matte != MagickFalse)
418 {
419 u=0;
420 v=0;
cristy7396d882010-01-27 02:37:56 +0000421 while (grays[u].opacity != ScaleQuantumToMap(p->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000422 u++;
cristy7396d882010-01-27 02:37:56 +0000423 while (grays[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
cristy7e9726d2010-01-26 02:08:40 +0000424 v++;
cristy7396d882010-01-27 02:37:56 +0000425 cooccurrence[u][v].direction[i].opacity++;
426 cooccurrence[v][u].direction[i].opacity++;
cristy7e9726d2010-01-26 02:08:40 +0000427 }
428 if (image->colorspace == CMYKColorspace)
429 {
430 u=0;
431 v=0;
cristy7396d882010-01-27 02:37:56 +0000432 while (grays[u].index != ScaleQuantumToMap(indexes[x]))
cristy7e9726d2010-01-26 02:08:40 +0000433 u++;
cristy7396d882010-01-27 02:37:56 +0000434 while (grays[v].index != ScaleQuantumToMap(indexes[x+offset]))
cristy7e9726d2010-01-26 02:08:40 +0000435 v++;
cristy7396d882010-01-27 02:37:56 +0000436 cooccurrence[u][v].direction[i].index++;
437 cooccurrence[v][u].direction[i].index++;
cristy7e9726d2010-01-26 02:08:40 +0000438 }
cristyf2bf2c72010-01-25 19:54:15 +0000439 }
cristy7e9726d2010-01-26 02:08:40 +0000440 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000441 }
442 }
443 image_view=DestroyCacheView(image_view);
444 if (status == MagickFalse)
445 {
446 (void) ThrowMagickException(exception,GetMagickModule(),
447 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7396d882010-01-27 02:37:56 +0000448 for (i=0; i < (long) number_grays; i++)
449 cooccurrence[i]=(ChannelStatistics *)
450 RelinquishMagickMemory(cooccurrence[i]);
451 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
452 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristyf2bf2c72010-01-25 19:54:15 +0000453 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
454 channel_features);
455 return(channel_features);
456 }
457 /*
cristy7e9726d2010-01-26 02:08:40 +0000458 Normalize spatial dependence matrix.
459 */
460#if defined(MAGICKCORE_OPENMP_SUPPORT)
461 #pragma omp parallel for schedule(dynamic,4) shared(status)
462#endif
cristybd822072010-01-27 00:30:00 +0000463 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000464 {
cristy549a37e2010-01-26 15:24:15 +0000465 double
466 normalize;
467
cristybd822072010-01-27 00:30:00 +0000468 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000469 {
cristybd822072010-01-27 00:30:00 +0000470 case 0:
471 default:
cristy7e9726d2010-01-26 02:08:40 +0000472 {
cristybd822072010-01-27 00:30:00 +0000473 /*
cristy7396d882010-01-27 02:37:56 +0000474 Horizontal adjacency.
cristybd822072010-01-27 00:30:00 +0000475 */
476 normalize=2.0*image->rows*(image->columns-distance);
477 break;
478 }
479 case 1:
480 {
481 /*
cristy7396d882010-01-27 02:37:56 +0000482 Vertical adjacency.
cristybd822072010-01-27 00:30:00 +0000483 */
cristy7396d882010-01-27 02:37:56 +0000484 normalize=2.0*(image->rows-distance)*image->columns;
cristybd822072010-01-27 00:30:00 +0000485 break;
486 }
487 case 2:
488 {
489 /*
cristy7396d882010-01-27 02:37:56 +0000490 Right diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000491 */
cristy7396d882010-01-27 02:37:56 +0000492 normalize=2.0*(image->rows-distance)*(image->columns-distance);
cristybd822072010-01-27 00:30:00 +0000493 break;
494 }
495 case 3:
496 {
497 /*
cristy7396d882010-01-27 02:37:56 +0000498 Left diagonal adjacency.
cristybd822072010-01-27 00:30:00 +0000499 */
500 normalize=2.0*(image->rows-distance)*(image->columns-distance);
501 break;
502 }
503 }
cristy7396d882010-01-27 02:37:56 +0000504 for (y=0; y < (long) number_grays; y++)
cristybd822072010-01-27 00:30:00 +0000505 {
506 register long
507 x;
508
cristy7396d882010-01-27 02:37:56 +0000509 for (x=0; x < (long) number_grays; x++)
cristybd822072010-01-27 00:30:00 +0000510 {
cristy7396d882010-01-27 02:37:56 +0000511 cooccurrence[x][y].direction[i].red/=normalize;
512 cooccurrence[x][y].direction[i].green/=normalize;
513 cooccurrence[x][y].direction[i].blue/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000514 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000515 cooccurrence[x][y].direction[i].opacity/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000516 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000517 cooccurrence[x][y].direction[i].index/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000518 }
519 }
520 }
521 /*
522 Compute texture features.
523 */
cristy7396d882010-01-27 02:37:56 +0000524 sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
525 if (sum == (ChannelStatistics *) NULL)
526 {
527 (void) ThrowMagickException(exception,GetMagickModule(),
528 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
529 for (i=0; i < (long) number_grays; i++)
530 cooccurrence[i]=(ChannelStatistics *)
531 RelinquishMagickMemory(cooccurrence[i]);
532 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
533 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
534 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
535 channel_features);
536 return(channel_features);
537 }
538 (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
539 (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
540 (void) ResetMagickMemory(&mean,0,sizeof(mean));
541 (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
cristy3a82f252010-01-26 20:31:51 +0000542#if defined(MAGICKCORE_OPENMP_SUPPORT)
543 #pragma omp parallel for schedule(dynamic,4) shared(status)
544#endif
cristybd822072010-01-27 00:30:00 +0000545 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000546 {
547 register long
cristybd822072010-01-27 00:30:00 +0000548 y;
cristy549a37e2010-01-26 15:24:15 +0000549
cristy7396d882010-01-27 02:37:56 +0000550 for (y=0; y < (long) number_grays; y++)
cristy549a37e2010-01-26 15:24:15 +0000551 {
cristybd822072010-01-27 00:30:00 +0000552 register long
553 x;
554
cristy7396d882010-01-27 02:37:56 +0000555 for (x=0; x < (long) number_grays; x++)
cristy549a37e2010-01-26 15:24:15 +0000556 {
557 /*
cristy3a82f252010-01-26 20:31:51 +0000558 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000559 */
560 channel_features[RedChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000561 cooccurrence[x][y].direction[i].red*
562 cooccurrence[x][y].direction[i].red;
cristy549a37e2010-01-26 15:24:15 +0000563 channel_features[GreenChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000564 cooccurrence[x][y].direction[i].green*
565 cooccurrence[x][y].direction[i].green;
cristy549a37e2010-01-26 15:24:15 +0000566 channel_features[BlueChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000567 cooccurrence[x][y].direction[i].blue*
568 cooccurrence[x][y].direction[i].blue;
cristy549a37e2010-01-26 15:24:15 +0000569 if (image->matte != MagickFalse)
570 channel_features[OpacityChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000571 cooccurrence[x][y].direction[i].opacity*
572 cooccurrence[x][y].direction[i].opacity;
cristy549a37e2010-01-26 15:24:15 +0000573 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000574 channel_features[BlackChannel].angular_second_moment[i]+=
cristy7396d882010-01-27 02:37:56 +0000575 cooccurrence[x][y].direction[i].index*
576 cooccurrence[x][y].direction[i].index;
577 /*
578 Correlation: measure of linear-dependencies in the image.
579 */
580 sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000581 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
cristycdf8e1b2010-01-28 01:52:33 +0000582 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
583 if (image->matte != MagickFalse)
584 sum[y].direction[i].opacity+=cooccurrence[x][y].direction[i].opacity;
585 if (image->colorspace == CMYKColorspace)
586 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
587 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
cristy7396d882010-01-27 02:37:56 +0000588 correlation.direction[i].green+=x*y*
589 cooccurrence[x][y].direction[i].green;
cristy7396d882010-01-27 02:37:56 +0000590 correlation.direction[i].blue+=x*y*
591 cooccurrence[x][y].direction[i].blue;
cristy7396d882010-01-27 02:37:56 +0000592 if (image->matte != MagickFalse)
cristycdf8e1b2010-01-28 01:52:33 +0000593 correlation.direction[i].opacity+=x*y*
594 cooccurrence[x][y].direction[i].opacity;
cristy7396d882010-01-27 02:37:56 +0000595 if (image->colorspace == CMYKColorspace)
cristycdf8e1b2010-01-28 01:52:33 +0000596 correlation.direction[i].index+=x*y*
597 cooccurrence[x][y].direction[i].index;
cristy7e9726d2010-01-26 02:08:40 +0000598 }
cristycdf8e1b2010-01-28 01:52:33 +0000599 mean.direction[i].red+=y*sum[y].direction[i].red;
600 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
601 mean.direction[i].green+=y*sum[y].direction[i].green;
602 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
603 mean.direction[i].blue+=y*sum[y].direction[i].blue;
604 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
605 if (image->matte != MagickFalse)
606 {
607 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
608 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
609 }
610 if (image->colorspace == CMYKColorspace)
611 {
612 mean.direction[i].index+=y*sum[y].direction[i].index;
613 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
614 }
cristy7e9726d2010-01-26 02:08:40 +0000615 }
cristycdf8e1b2010-01-28 01:52:33 +0000616 /*
617 Correlation: measure of linear-dependencies in the image.
618 */
619 channel_features[RedChannel].correlation[i]=
620 (correlation.direction[i].red-mean.direction[i].red*
621 mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
622 (mean.direction[i].red*mean.direction[i].red))*sqrt(
623 sum_squares.direction[i].red-(mean.direction[i].red*
624 mean.direction[i].red)));
625 channel_features[GreenChannel].correlation[i]=
626 (correlation.direction[i].green-mean.direction[i].green*
627 mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
628 (mean.direction[i].green*mean.direction[i].green))*sqrt(
629 sum_squares.direction[i].green-(mean.direction[i].green*
630 mean.direction[i].green)));
631 channel_features[BlueChannel].correlation[i]=
632 (correlation.direction[i].blue-mean.direction[i].blue*
633 mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
634 (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
635 sum_squares.direction[i].blue-(mean.direction[i].blue*
636 mean.direction[i].blue)));
637 if (image->matte != MagickFalse)
638 channel_features[OpacityChannel].correlation[i]=
639 (correlation.direction[i].opacity-mean.direction[i].opacity*
640 mean.direction[i].opacity)/(sqrt(sum_squares.direction[i].opacity-
641 (mean.direction[i].opacity*mean.direction[i].opacity))*sqrt(
642 sum_squares.direction[i].opacity-(mean.direction[i].opacity*
643 mean.direction[i].opacity)));
644 if (image->colorspace == CMYKColorspace)
645 channel_features[IndexChannel].correlation[i]=
646 (correlation.direction[i].index-mean.direction[i].index*
647 mean.direction[i].index)/(sqrt(sum_squares.direction[i].index-
648 (mean.direction[i].index*mean.direction[i].index))*sqrt(
649 sum_squares.direction[i].index-(mean.direction[i].index*
650 mean.direction[i].index)));
cristy7e9726d2010-01-26 02:08:40 +0000651 }
cristy3a82f252010-01-26 20:31:51 +0000652#if defined(MAGICKCORE_OPENMP_SUPPORT)
653 #pragma omp parallel for schedule(dynamic,4) shared(status)
654#endif
cristybd822072010-01-27 00:30:00 +0000655 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +0000656 {
cristy7396d882010-01-27 02:37:56 +0000657 for (z=0; z < (long) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +0000658 {
659 register long
cristybd822072010-01-27 00:30:00 +0000660 y;
cristy3a82f252010-01-26 20:31:51 +0000661
cristy7396d882010-01-27 02:37:56 +0000662 ChannelStatistics
cristybd822072010-01-27 00:30:00 +0000663 pixel;
664
665 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy7396d882010-01-27 02:37:56 +0000666 for (y=0; y < (long) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +0000667 {
cristybd822072010-01-27 00:30:00 +0000668 register long
669 x;
670
cristy7396d882010-01-27 02:37:56 +0000671 for (x=0; x < (long) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +0000672 {
673 /*
674 Contrast: amount of local variations present in an image.
675 */
676 if (((y-x) == z) || ((x-y) == z))
677 {
cristy7396d882010-01-27 02:37:56 +0000678 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
679 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
680 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000681 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000682 pixel.direction[i].opacity+=
683 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000684 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000685 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000686 }
687 }
688 }
cristy7396d882010-01-27 02:37:56 +0000689 channel_features[RedChannel].contrast[i]+=z*z*pixel.direction[i].red;
690 channel_features[GreenChannel].contrast[i]+=z*z*pixel.direction[i].green;
691 channel_features[BlueChannel].contrast[i]+=z*z*pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000692 if (image->matte != MagickFalse)
693 channel_features[OpacityChannel].contrast[i]+=z*z*
cristy7396d882010-01-27 02:37:56 +0000694 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000695 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000696 channel_features[BlackChannel].contrast[i]+=z*z*
697 pixel.direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000698 }
699 }
cristy7e9726d2010-01-26 02:08:40 +0000700 /*
cristyf2bf2c72010-01-25 19:54:15 +0000701 Relinquish resources.
702 */
cristy7396d882010-01-27 02:37:56 +0000703 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
704 for (i=0; i < (long) number_grays; i++)
705 cooccurrence[i]=(ChannelStatistics *)
706 RelinquishMagickMemory(cooccurrence[i]);
707 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
708 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy3e2860c2010-01-24 01:36:30 +0000709 return(channel_features);
710}