blob: bbf8c1b1cc209b34e972f061d26ba09e7fe6e031 [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%
cristy7e9726d2010-01-26 02:08:40 +0000103% GetImageChannelFeatures() returns features for each channel in the image at
104% at 0, 45, 90, and 135 degrees for the specified distance. The features
105% include the angular second momentum, contrast, correlation, sum of squares:
106% variance, inverse difference moment, sum average, sum varience, sum entropy,
107% entropy, difference variance, difference entropy, information measures of
108% correlation 1, information measures of correlation 2, and maximum
109% correlation coefficient. You can access the red channel contrast, for
110% example, like this:
cristy3e2860c2010-01-24 01:36:30 +0000111%
cristy7e9726d2010-01-26 02:08:40 +0000112% channel_features=GetImageChannelFeatures(image,1,excepton);
cristy549a37e2010-01-26 15:24:15 +0000113% contrast=channel_features[RedChannel].contrast[0];
cristy3e2860c2010-01-24 01:36:30 +0000114%
115% Use MagickRelinquishMemory() to free the features buffer.
116%
117% The format of the GetImageChannelFeatures method is:
118%
119% ChannelFeatures *GetImageChannelFeatures(const Image *image,
cristy549a37e2010-01-26 15:24:15 +0000120% const unsigned long distance,ExceptionInfo *exception)
cristy3e2860c2010-01-24 01:36:30 +0000121%
122% A description of each parameter follows:
123%
124% o image: the image.
125%
cristy7e9726d2010-01-26 02:08:40 +0000126% o distance: the distance.
127%
cristy3e2860c2010-01-24 01:36:30 +0000128% o exception: return any errors or warnings in this structure.
129%
130*/
131MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image,
cristy7e9726d2010-01-26 02:08:40 +0000132 const unsigned long distance,ExceptionInfo *exception)
cristy3e2860c2010-01-24 01:36:30 +0000133{
cristyf2bf2c72010-01-25 19:54:15 +0000134 typedef struct _SpatialDependenceMatrix
135 {
cristy549a37e2010-01-26 15:24:15 +0000136 DoublePixelPacket
cristy7e9726d2010-01-26 02:08:40 +0000137 tones[4];
cristyf2bf2c72010-01-25 19:54:15 +0000138 } SpatialDependenceMatrix;
139
cristy2070fa52010-01-24 03:17:57 +0000140 CacheView
141 *image_view;
142
cristy3e2860c2010-01-24 01:36:30 +0000143 ChannelFeatures
144 *channel_features;
145
cristy2070fa52010-01-24 03:17:57 +0000146 LongPixelPacket
cristyf2bf2c72010-01-25 19:54:15 +0000147 tone,
148 *tones;
cristy2070fa52010-01-24 03:17:57 +0000149
cristy3e2860c2010-01-24 01:36:30 +0000150 long
cristy3a82f252010-01-26 20:31:51 +0000151 y,
152 z;
cristy3e2860c2010-01-24 01:36:30 +0000153
cristy2070fa52010-01-24 03:17:57 +0000154 MagickBooleanType
155 status;
156
157 register long
158 i;
159
cristy3e2860c2010-01-24 01:36:30 +0000160 size_t
161 length;
162
cristyf2bf2c72010-01-25 19:54:15 +0000163 SpatialDependenceMatrix
164 **pixels;
165
166 unsigned long
167 number_tones;
168
cristy3e2860c2010-01-24 01:36:30 +0000169 assert(image != (Image *) NULL);
170 assert(image->signature == MagickSignature);
171 if (image->debug != MagickFalse)
172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000173 if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
174 return((ChannelFeatures *) NULL);
cristy3e2860c2010-01-24 01:36:30 +0000175 length=AllChannels+1UL;
176 channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
177 sizeof(*channel_features));
178 if (channel_features == (ChannelFeatures *) NULL)
179 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
180 (void) ResetMagickMemory(channel_features,0,length*
181 sizeof(*channel_features));
cristy2070fa52010-01-24 03:17:57 +0000182 /*
cristyf2bf2c72010-01-25 19:54:15 +0000183 Form tones.
cristy2070fa52010-01-24 03:17:57 +0000184 */
cristyf2bf2c72010-01-25 19:54:15 +0000185 tones=(LongPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*tones));
186 if (tones == (LongPixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000187 {
188 (void) ThrowMagickException(exception,GetMagickModule(),
189 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
190 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
191 channel_features);
192 return(channel_features);
193 }
194 for (i=0; i <= (long) MaxMap; i++)
195 {
cristyf2bf2c72010-01-25 19:54:15 +0000196 tones[i].red=(~0UL);
197 tones[i].green=(~0UL);
198 tones[i].blue=(~0UL);
199 tones[i].opacity=(~0UL);
200 tones[i].index=(~0UL);
cristy2070fa52010-01-24 03:17:57 +0000201 }
202 status=MagickTrue;
203 image_view=AcquireCacheView(image);
204#if defined(MAGICKCORE_OPENMP_SUPPORT)
205 #pragma omp parallel for schedule(dynamic,4) shared(status)
206#endif
cristy3e2860c2010-01-24 01:36:30 +0000207 for (y=0; y < (long) image->rows; y++)
208 {
209 register const IndexPacket
210 *restrict indexes;
211
212 register const PixelPacket
213 *restrict p;
214
215 register long
216 x;
217
cristy2070fa52010-01-24 03:17:57 +0000218 if (status == MagickFalse)
219 continue;
220 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3e2860c2010-01-24 01:36:30 +0000221 if (p == (const PixelPacket *) NULL)
cristy2070fa52010-01-24 03:17:57 +0000222 {
223 status=MagickFalse;
224 continue;
225 }
226 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3e2860c2010-01-24 01:36:30 +0000227 for (x=0; x < (long) image->columns; x++)
228 {
cristyf2bf2c72010-01-25 19:54:15 +0000229 tones[ScaleQuantumToMap(p->red)].red=ScaleQuantumToMap(p->red);
230 tones[ScaleQuantumToMap(p->green)].green=ScaleQuantumToMap(p->green);
231 tones[ScaleQuantumToMap(p->blue)].blue=ScaleQuantumToMap(p->blue);
cristy2070fa52010-01-24 03:17:57 +0000232 if (image->matte != MagickFalse)
cristyf2bf2c72010-01-25 19:54:15 +0000233 tones[ScaleQuantumToMap(p->opacity)].opacity=
cristy2070fa52010-01-24 03:17:57 +0000234 ScaleQuantumToMap(p->opacity);
235 if (image->colorspace == CMYKColorspace)
cristyf2bf2c72010-01-25 19:54:15 +0000236 tones[ScaleQuantumToMap(indexes[x])].index=
cristy2070fa52010-01-24 03:17:57 +0000237 ScaleQuantumToMap(indexes[x]);
cristy3e2860c2010-01-24 01:36:30 +0000238 p++;
239 }
240 }
cristy30c510a2010-01-24 03:43:00 +0000241 image_view=DestroyCacheView(image_view);
242 if (status == MagickFalse)
243 {
cristyf2bf2c72010-01-25 19:54:15 +0000244 tones=(LongPixelPacket *) RelinquishMagickMemory(tones);
cristy30c510a2010-01-24 03:43:00 +0000245 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
246 channel_features);
247 return(channel_features);
248 }
cristyf2bf2c72010-01-25 19:54:15 +0000249 (void) ResetMagickMemory(&tone,0,sizeof(tone));
cristy2070fa52010-01-24 03:17:57 +0000250 for (i=0; i <= (long) MaxMap; i++)
251 {
cristyf2bf2c72010-01-25 19:54:15 +0000252 if (tones[i].red != ~0UL)
253 tones[tone.red++].red=tones[i].red;
254 if (tones[i].green != ~0UL)
255 tones[tone.green++].green=tones[i].green;
256 if (tones[i].blue != ~0UL)
257 tones[tone.blue++].blue=tones[i].blue;
cristy2070fa52010-01-24 03:17:57 +0000258 if (image->matte != MagickFalse)
cristyf2bf2c72010-01-25 19:54:15 +0000259 if (tones[i].opacity != ~0UL)
260 tones[tone.opacity++].opacity=tones[i].opacity;
cristy2070fa52010-01-24 03:17:57 +0000261 if (image->colorspace == CMYKColorspace)
cristyf2bf2c72010-01-25 19:54:15 +0000262 if (tones[i].index != ~0UL)
263 tones[tone.index++].index=tones[i].index;
cristy2070fa52010-01-24 03:17:57 +0000264 }
cristyf2bf2c72010-01-25 19:54:15 +0000265 /*
266 Allocate spatial dependence matrix.
267 */
268 number_tones=tone.red;
269 if (tone.green > number_tones)
270 number_tones=tone.green;
271 if (tone.blue > number_tones)
272 number_tones=tone.blue;
273 if (image->matte != MagickFalse)
274 if (tone.opacity > number_tones)
275 number_tones=tone.opacity;
276 if (image->colorspace == CMYKColorspace)
277 if (tone.index > number_tones)
278 number_tones=tone.index;
279 pixels=(SpatialDependenceMatrix **) AcquireQuantumMemory(number_tones,
cristy7e9726d2010-01-26 02:08:40 +0000280 sizeof(*pixels));
cristyf2bf2c72010-01-25 19:54:15 +0000281 if (pixels == (SpatialDependenceMatrix **) NULL)
282 {
283 (void) ThrowMagickException(exception,GetMagickModule(),
284 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
285 tones=(LongPixelPacket *) RelinquishMagickMemory(tones);
286 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
287 channel_features);
288 return(channel_features);
289 }
cristy7e9726d2010-01-26 02:08:40 +0000290 for (i=0; i < (long) number_tones; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000291 {
292 pixels[i]=(SpatialDependenceMatrix *) AcquireQuantumMemory(number_tones,
cristy7e9726d2010-01-26 02:08:40 +0000293 sizeof(**pixels));
cristyf2bf2c72010-01-25 19:54:15 +0000294 if (pixels[i] == (SpatialDependenceMatrix *) NULL)
295 break;
296 (void) ResetMagickMemory(pixels[i],0,number_tones*sizeof(*pixels));
297 }
cristy7e9726d2010-01-26 02:08:40 +0000298 if (i < (long) number_tones)
cristyf2bf2c72010-01-25 19:54:15 +0000299 {
300 (void) ThrowMagickException(exception,GetMagickModule(),
301 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
302 for (i--; i >= 0; i--)
303 pixels[i]=(SpatialDependenceMatrix *) RelinquishMagickMemory(pixels[i]);
304 pixels=(SpatialDependenceMatrix **) RelinquishMagickMemory(pixels);
305 tones=(LongPixelPacket *) RelinquishMagickMemory(tones);
306 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
307 channel_features);
308 return(channel_features);
309 }
310 /*
311 Initialize spatial dependence matrix.
312 */
313 status=MagickTrue;
314 image_view=AcquireCacheView(image);
315#if defined(MAGICKCORE_OPENMP_SUPPORT)
316 #pragma omp parallel for schedule(dynamic,4) shared(status)
317#endif
318 for (y=0; y < (long) image->rows; y++)
319 {
320 long
321 u,
322 v;
323
324 register const IndexPacket
325 *restrict indexes;
326
327 register const PixelPacket
328 *restrict p;
329
330 register long
331 x;
332
cristy7e9726d2010-01-26 02:08:40 +0000333 ssize_t
334 offset;
335
cristyf2bf2c72010-01-25 19:54:15 +0000336 if (status == MagickFalse)
337 continue;
cristy7e9726d2010-01-26 02:08:40 +0000338 p=GetCacheViewVirtualPixels(image_view,-(long) distance,y,image->columns+
339 2*distance,distance+1,exception);
cristyf2bf2c72010-01-25 19:54:15 +0000340 if (p == (const PixelPacket *) NULL)
341 {
342 status=MagickFalse;
343 continue;
344 }
345 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy7e9726d2010-01-26 02:08:40 +0000346 p+=distance;
347 indexes+=distance;
cristyf2bf2c72010-01-25 19:54:15 +0000348 for (x=0; x < (long) image->columns; x++)
349 {
350 for (i=0; i < 4; i++)
351 {
cristy7e9726d2010-01-26 02:08:40 +0000352 switch (i)
353 {
354 case 0:
cristy549a37e2010-01-26 15:24:15 +0000355 default:
cristy7e9726d2010-01-26 02:08:40 +0000356 {
357 /*
358 0 degrees.
359 */
360 offset=(ssize_t) distance;
361 break;
362 }
363 case 1:
364 {
365 /*
366 45 degrees.
367 */
368 offset=(ssize_t) (image->columns+2*distance)-distance;
369 break;
370 }
371 case 2:
372 {
373 /*
374 90 degrees.
375 */
376 offset=(ssize_t) (image->columns+2*distance);
377 break;
378 }
379 case 3:
380 {
381 /*
382 135 degrees.
383 */
384 offset=(ssize_t) (image->columns+2*distance)+distance;
385 break;
386 }
387 }
388 u=0;
389 v=0;
390 while (tones[u].red != ScaleQuantumToMap(p->red))
391 u++;
392 while (tones[v].red != ScaleQuantumToMap((p+offset)->red))
393 v++;
394 pixels[u][v].tones[i].red++;
395 pixels[v][u].tones[i].red++;
396 u=0;
397 v=0;
398 while (tones[u].green != ScaleQuantumToMap(p->green))
399 u++;
400 while (tones[v].green != ScaleQuantumToMap((p+offset)->green))
401 v++;
402 pixels[u][v].tones[i].green++;
403 pixels[v][u].tones[i].green++;
404 u=0;
405 v=0;
406 while (tones[u].blue != ScaleQuantumToMap(p->blue))
407 u++;
408 while (tones[v].blue != ScaleQuantumToMap((p+offset)->blue))
409 v++;
410 pixels[u][v].tones[i].blue++;
411 pixels[v][u].tones[i].blue++;
412 if (image->matte != MagickFalse)
413 {
414 u=0;
415 v=0;
416 while (tones[u].opacity != ScaleQuantumToMap(p->opacity))
417 u++;
418 while (tones[v].opacity != ScaleQuantumToMap((p+offset)->opacity))
419 v++;
420 pixels[u][v].tones[i].opacity++;
421 pixels[v][u].tones[i].opacity++;
422 }
423 if (image->colorspace == CMYKColorspace)
424 {
425 u=0;
426 v=0;
427 while (tones[u].index != ScaleQuantumToMap(indexes[x]))
428 u++;
429 while (tones[v].index != ScaleQuantumToMap(indexes[x+offset]))
430 v++;
431 pixels[u][v].tones[i].index++;
432 pixels[v][u].tones[i].index++;
433 }
cristyf2bf2c72010-01-25 19:54:15 +0000434 }
cristy7e9726d2010-01-26 02:08:40 +0000435 p++;
cristyf2bf2c72010-01-25 19:54:15 +0000436 }
437 }
438 image_view=DestroyCacheView(image_view);
439 if (status == MagickFalse)
440 {
441 (void) ThrowMagickException(exception,GetMagickModule(),
442 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy7e9726d2010-01-26 02:08:40 +0000443 for (i=0; i < (long) number_tones; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000444 pixels[i]=(SpatialDependenceMatrix *) RelinquishMagickMemory(pixels[i]);
445 pixels=(SpatialDependenceMatrix **) RelinquishMagickMemory(pixels);
446 tones=(LongPixelPacket *) RelinquishMagickMemory(tones);
447 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
448 channel_features);
449 return(channel_features);
450 }
451 /*
cristy7e9726d2010-01-26 02:08:40 +0000452 Normalize spatial dependence matrix.
453 */
454#if defined(MAGICKCORE_OPENMP_SUPPORT)
455 #pragma omp parallel for schedule(dynamic,4) shared(status)
456#endif
cristybd822072010-01-27 00:30:00 +0000457 for (i=0; i < 4; i++)
cristy7e9726d2010-01-26 02:08:40 +0000458 {
cristy549a37e2010-01-26 15:24:15 +0000459 double
460 normalize;
461
cristybd822072010-01-27 00:30:00 +0000462 switch (i)
cristy7e9726d2010-01-26 02:08:40 +0000463 {
cristybd822072010-01-27 00:30:00 +0000464 case 0:
465 default:
cristy7e9726d2010-01-26 02:08:40 +0000466 {
cristybd822072010-01-27 00:30:00 +0000467 /*
468 0 degrees.
469 */
470 normalize=2.0*image->rows*(image->columns-distance);
471 break;
472 }
473 case 1:
474 {
475 /*
476 45 degrees.
477 */
478 normalize=2.0*(image->rows-distance)*(image->columns-distance);
479 break;
480 }
481 case 2:
482 {
483 /*
484 90 degrees.
485 */
486 normalize=2.0*(image->rows-distance)*image->columns;
487 break;
488 }
489 case 3:
490 {
491 /*
492 135 degrees.
493 */
494 normalize=2.0*(image->rows-distance)*(image->columns-distance);
495 break;
496 }
497 }
498 for (y=0; y < (long) number_tones; y++)
499 {
500 register long
501 x;
502
503 for (x=0; x < (long) number_tones; x++)
504 {
cristy7e9726d2010-01-26 02:08:40 +0000505 pixels[x][y].tones[i].red/=normalize;
cristy549a37e2010-01-26 15:24:15 +0000506 pixels[x][y].tones[i].green/=normalize;
507 pixels[x][y].tones[i].blue/=normalize;
508 if (image->matte != MagickFalse)
509 pixels[x][y].tones[i].opacity/=normalize;
510 if (image->colorspace == CMYKColorspace)
511 pixels[x][y].tones[i].index/=normalize;
512 }
513 }
514 }
515 /*
516 Compute texture features.
517 */
cristy3a82f252010-01-26 20:31:51 +0000518#if defined(MAGICKCORE_OPENMP_SUPPORT)
519 #pragma omp parallel for schedule(dynamic,4) shared(status)
520#endif
cristybd822072010-01-27 00:30:00 +0000521 for (i=0; i < 4; i++)
cristy549a37e2010-01-26 15:24:15 +0000522 {
523 register long
cristybd822072010-01-27 00:30:00 +0000524 y;
cristy549a37e2010-01-26 15:24:15 +0000525
cristybd822072010-01-27 00:30:00 +0000526 for (y=0; y < (long) number_tones; y++)
cristy549a37e2010-01-26 15:24:15 +0000527 {
cristybd822072010-01-27 00:30:00 +0000528 register long
529 x;
530
531 for (x=0; x < (long) number_tones; x++)
cristy549a37e2010-01-26 15:24:15 +0000532 {
533 /*
cristy3a82f252010-01-26 20:31:51 +0000534 Angular second moment: measure of homogeneity of the image.
cristy549a37e2010-01-26 15:24:15 +0000535 */
536 channel_features[RedChannel].angular_second_moment[i]+=
537 pixels[x][y].tones[i].red*pixels[x][y].tones[i].red;
538 channel_features[GreenChannel].angular_second_moment[i]+=
539 pixels[x][y].tones[i].green*pixels[x][y].tones[i].green;
540 channel_features[BlueChannel].angular_second_moment[i]+=
541 pixels[x][y].tones[i].blue*pixels[x][y].tones[i].blue;
542 if (image->matte != MagickFalse)
543 channel_features[OpacityChannel].angular_second_moment[i]+=
544 pixels[x][y].tones[i].opacity*pixels[x][y].tones[i].opacity;
545 if (image->colorspace == CMYKColorspace)
cristy3a82f252010-01-26 20:31:51 +0000546 channel_features[BlackChannel].angular_second_moment[i]+=
cristy549a37e2010-01-26 15:24:15 +0000547 pixels[x][y].tones[i].index*pixels[x][y].tones[i].index;
cristy7e9726d2010-01-26 02:08:40 +0000548 }
549 }
550 }
cristy3a82f252010-01-26 20:31:51 +0000551#if defined(MAGICKCORE_OPENMP_SUPPORT)
552 #pragma omp parallel for schedule(dynamic,4) shared(status)
553#endif
cristybd822072010-01-27 00:30:00 +0000554 for (i=0; i < 4; i++)
cristy3a82f252010-01-26 20:31:51 +0000555 {
cristybd822072010-01-27 00:30:00 +0000556 for (z=0; z < (long) number_tones; z++)
cristy3a82f252010-01-26 20:31:51 +0000557 {
558 register long
cristybd822072010-01-27 00:30:00 +0000559 y;
cristy3a82f252010-01-26 20:31:51 +0000560
cristybd822072010-01-27 00:30:00 +0000561 SpatialDependenceMatrix
562 pixel;
563
564 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
565 for (y=0; y < (long) number_tones; y++)
cristy3a82f252010-01-26 20:31:51 +0000566 {
cristybd822072010-01-27 00:30:00 +0000567 register long
568 x;
569
570 for (x=0; x < (long) number_tones; x++)
cristy3a82f252010-01-26 20:31:51 +0000571 {
572 /*
573 Contrast: amount of local variations present in an image.
574 */
575 if (((y-x) == z) || ((x-y) == z))
576 {
577 pixel.tones[i].red+=pixels[x][y].tones[i].red;
578 pixel.tones[i].green+=pixels[x][y].tones[i].green;
579 pixel.tones[i].blue+=pixels[x][y].tones[i].blue;
580 if (image->matte != MagickFalse)
581 pixel.tones[i].opacity+=pixels[x][y].tones[i].opacity;
582 if (image->colorspace == CMYKColorspace)
583 pixel.tones[i].index+=pixels[x][y].tones[i].index;
584 }
585 }
586 }
cristy3a82f252010-01-26 20:31:51 +0000587 channel_features[RedChannel].contrast[i]+=z*z*pixel.tones[i].red;
588 channel_features[GreenChannel].contrast[i]+=z*z*pixel.tones[i].green;
589 channel_features[BlueChannel].contrast[i]+=z*z*pixel.tones[i].blue;
590 if (image->matte != MagickFalse)
591 channel_features[OpacityChannel].contrast[i]+=z*z*
592 pixel.tones[i].opacity;
593 if (image->colorspace == CMYKColorspace)
594 channel_features[BlackChannel].contrast[i]+=z*z*pixel.tones[i].index;
595 }
596 }
cristy7e9726d2010-01-26 02:08:40 +0000597 /*
cristyf2bf2c72010-01-25 19:54:15 +0000598 Relinquish resources.
599 */
cristy7e9726d2010-01-26 02:08:40 +0000600 for (i=0; i < (long) number_tones; i++)
cristyf2bf2c72010-01-25 19:54:15 +0000601 pixels[i]=(SpatialDependenceMatrix *) RelinquishMagickMemory(pixels[i]);
602 pixels=(SpatialDependenceMatrix **) RelinquishMagickMemory(pixels);
603 tones=(LongPixelPacket *) RelinquishMagickMemory(tones);
cristy3e2860c2010-01-24 01:36:30 +0000604 return(channel_features);
605}