blob: da8260358d2bcaa44385dc873a66ce2a4c5e8f8e [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;
581 correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
582 mean.direction[i].red+=y*sum[y].direction[i].red;
583 sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
584 sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
585 correlation.direction[i].green+=x*y*
586 cooccurrence[x][y].direction[i].green;
587 mean.direction[i].green+=y*sum[y].direction[i].green;
588 sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
589 sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
590 correlation.direction[i].blue+=x*y*
591 cooccurrence[x][y].direction[i].blue;
592 mean.direction[i].blue+=y*sum[y].direction[i].blue;
593 sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
594 if (image->matte != MagickFalse)
595 {
596 sum[y].direction[i].opacity+=
597 cooccurrence[x][y].direction[i].opacity;
598 correlation.direction[i].opacity+=x*y*
599 cooccurrence[x][y].direction[i].opacity;
600 mean.direction[i].opacity+=y*sum[y].direction[i].opacity;
601 sum_squares.direction[i].opacity+=y*y*sum[y].direction[i].opacity;
602 }
603 if (image->colorspace == CMYKColorspace)
604 {
605 sum[y].direction[i].index+=cooccurrence[x][y].direction[i].index;
606 correlation.direction[i].index+=x*y*
607 cooccurrence[x][y].direction[i].index;
608 mean.direction[i].index+=y*sum[y].direction[i].index;
609 sum_squares.direction[i].index+=y*y*sum[y].direction[i].index;
610 }
cristy7e9726d2010-01-26 02:08:40 +0000611 }
612 }
613 }
cristy3a82f252010-01-26 20:31:51 +0000614#if defined(MAGICKCORE_OPENMP_SUPPORT)
615 #pragma omp parallel for schedule(dynamic,4) shared(status)
616#endif
cristybd822072010-01-27 00:30:00 +0000617 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +0000618 {
cristy7396d882010-01-27 02:37:56 +0000619 for (z=0; z < (long) number_grays; z++)
cristy3a82f252010-01-26 20:31:51 +0000620 {
621 register long
cristybd822072010-01-27 00:30:00 +0000622 y;
cristy3a82f252010-01-26 20:31:51 +0000623
cristy7396d882010-01-27 02:37:56 +0000624 ChannelStatistics
cristybd822072010-01-27 00:30:00 +0000625 pixel;
626
627 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy7396d882010-01-27 02:37:56 +0000628 for (y=0; y < (long) number_grays; y++)
cristy3a82f252010-01-26 20:31:51 +0000629 {
cristybd822072010-01-27 00:30:00 +0000630 register long
631 x;
632
cristy7396d882010-01-27 02:37:56 +0000633 for (x=0; x < (long) number_grays; x++)
cristy3a82f252010-01-26 20:31:51 +0000634 {
635 /*
636 Contrast: amount of local variations present in an image.
637 */
638 if (((y-x) == z) || ((x-y) == z))
639 {
cristy7396d882010-01-27 02:37:56 +0000640 pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
641 pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
642 pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000643 if (image->matte != MagickFalse)
cristy7396d882010-01-27 02:37:56 +0000644 pixel.direction[i].opacity+=
645 cooccurrence[x][y].direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000646 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000647 pixel.direction[i].index+=cooccurrence[x][y].direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000648 }
649 }
650 }
cristy7396d882010-01-27 02:37:56 +0000651 channel_features[RedChannel].contrast[i]+=z*z*pixel.direction[i].red;
652 channel_features[GreenChannel].contrast[i]+=z*z*pixel.direction[i].green;
653 channel_features[BlueChannel].contrast[i]+=z*z*pixel.direction[i].blue;
cristy3a82f252010-01-26 20:31:51 +0000654 if (image->matte != MagickFalse)
655 channel_features[OpacityChannel].contrast[i]+=z*z*
cristy7396d882010-01-27 02:37:56 +0000656 pixel.direction[i].opacity;
cristy3a82f252010-01-26 20:31:51 +0000657 if (image->colorspace == CMYKColorspace)
cristy7396d882010-01-27 02:37:56 +0000658 channel_features[BlackChannel].contrast[i]+=z*z*
659 pixel.direction[i].index;
cristy3a82f252010-01-26 20:31:51 +0000660 }
661 }
cristy7e9726d2010-01-26 02:08:40 +0000662 /*
cristyf2bf2c72010-01-25 19:54:15 +0000663 Relinquish resources.
664 */
cristy7396d882010-01-27 02:37:56 +0000665 sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
666 for (i=0; i < (long) number_grays; i++)
667 cooccurrence[i]=(ChannelStatistics *)
668 RelinquishMagickMemory(cooccurrence[i]);
669 cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
670 grays=(LongPixelPacket *) RelinquishMagickMemory(grays);
cristy3e2860c2010-01-24 01:36:30 +0000671 return(channel_features);
672}