3 #include "sherlock/sherlock.h"
5 #include "lib/fastbuf.h"
6 #include "images/images.h"
8 #include <magick/api.h>
13 * http://www.tecgraf.puc-rio.br/~mgattass/color/ColorIndex.html
17 #define REF_WHITE_X 0.96422
18 #define REF_WHITE_Y 1.
19 #define REF_WHITE_Z 0.82521
23 srgb_to_xyz_slow(double srgb[3], double xyz[3])
26 for (uns i = 0; i < 3; i++)
27 if (srgb[i] > 0.04045)
28 a[i] = pow((srgb[i] + 0.055) * (1 / 1.055), 2.4);
30 a[i] = srgb[i] * (1 / 12.92);
31 xyz[0] = 0.412424 * a[0] + 0.357579 * a[1] + 0.180464 * a[2];
32 xyz[1] = 0.212656 * a[0] + 0.715158 * a[1] + 0.072186 * a[2];
33 xyz[2] = 0.019332 * a[0] + 0.119193 * a[1] + 0.950444 * a[2];
38 xyz_to_luv_slow(double xyz[3], double luv[3])
40 double sum = xyz[0] + 15 * xyz[1] + 3 * xyz[2];
42 luv[0] = luv[1] = luv[2] = 0;
45 double var_u = 4 * xyz[0] / sum;
46 double var_v = 9 * xyz[1] / sum;
47 if (xyz[1] > 0.008856)
48 luv[0] = 116 * pow(xyz[1], 1 / 3.) - 16;
50 luv[0] = (116 * 7.787) * xyz[1];
51 luv[1] = luv[0] * (13 * (var_u - 4 * REF_WHITE_X / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z)));
52 luv[2] = luv[0] * (13 * (var_v - 9 * REF_WHITE_Y / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z)));
53 /* intervals [0..100], [-134..220], [-140..122] */
58 uns l, u, v; /* average Luv coefficients */
59 uns lh, hl, hh; /* energies in Daubechies wavelet bands */
63 compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct image_signature *sig)
65 ASSERT(width >= 4 && height >= 4);
69 DBG("Computing signature for image %dx%d... %dx%d blocks", width, height, w, h);
70 uns blocks_count = w * h;
71 struct block *blocks = xmalloc(blocks_count * sizeof(struct block)), *block = blocks; /* FIXME: use mempool */
73 /* Every 4x4 block (FIXME: deal with smaller blocks near the edges) */
74 PixelPacket *p = pixels;
75 for (uns block_y = 0; block_y < h; block_y++, p += width & 3 + width * 3)
76 for (uns block_x = 0; block_x < w; block_x++, p -= 4 * width - 4, block++)
78 int t[16], s[16], *tp = t;
80 /* Convert pixels to Luv color space and compute average coefficients
82 * - could be MUCH faster with precomputed tables and integer arithmetic...
83 * I will propably use interpolation in 3-dim array */
87 for (uns y = 0; y < 4; y++, p += width - 4)
88 for (uns x = 0; x < 4; x++, p++)
90 double rgb[3], luv[3], xyz[3];
91 rgb[0] = (p->red >> (QuantumDepth - 8)) / 255.;
92 rgb[1] = (p->green >> (QuantumDepth - 8)) / 255.;
93 rgb[2] = (p->blue >> (QuantumDepth - 8)) / 255.;
94 srgb_to_xyz_slow(rgb, xyz);
95 xyz_to_luv_slow(xyz, luv);
96 l_sum += *tp++ = luv[0];
97 u_sum += luv[1] + 150;
98 v_sum += luv[2] + 150;
105 /* Apply Daubechies wavelet transformation
107 * - MMX/SSE instructions or tables could be faster
108 * - maybe it would be better to compute Luv and wavelet separately because of processor cache or MMX/SSE
109 * - eliminate slow square roots
110 * - what about Haar transformation? */
112 #define DAUB_0 31651 /* (1 + sqrt 3) / (4 * sqrt 2) */
113 #define DAUB_1 54822 /* (3 + sqrt 3) / (4 * sqrt 2) */
114 #define DAUB_2 14689 /* (3 - sqrt 3) / (4 * sqrt 2) */
115 #define DAUB_3 -8481 /* (1 - sqrt 3) / (4 * sqrt 2) */
117 /* ... to the rows */
119 for (i = 0; i < 16; i += 4)
121 s[i + 0] = (DAUB_0 * t[i + 2] + DAUB_1 * t[i + 3] + DAUB_2 * t[i + 0] + DAUB_3 * t[i + 1]) / 0x10000;
122 s[i + 1] = (DAUB_0 * t[i + 0] + DAUB_1 * t[i + 1] + DAUB_2 * t[i + 2] + DAUB_3 * t[i + 3]) / 0x10000;
123 s[i + 2] = (DAUB_3 * t[i + 2] - DAUB_2 * t[i + 3] + DAUB_1 * t[i + 0] - DAUB_0 * t[i + 1]) / 0x10000;
124 s[i + 3] = (DAUB_3 * t[i + 0] - DAUB_2 * t[i + 1] + DAUB_1 * t[i + 2] - DAUB_0 * t[i + 3]) / 0x10000;
127 /* ... and to the columns... skip LL band */
128 for (i = 0; i < 2; i++)
130 t[i + 8] = (DAUB_3 * s[i + 8] - DAUB_2 * s[i +12] + DAUB_1 * s[i + 0] - DAUB_0 * s[i + 4]) / 0x1000;
131 t[i +12] = (DAUB_3 * s[i + 0] - DAUB_2 * s[i + 4] + DAUB_1 * s[i + 8] - DAUB_0 * s[i +12]) / 0x1000;
135 t[i + 0] = (DAUB_0 * s[i + 8] + DAUB_1 * s[i +12] + DAUB_2 * s[i + 0] + DAUB_3 * s[i + 4]) / 0x1000;
136 t[i + 4] = (DAUB_0 * s[i + 0] + DAUB_1 * s[i + 4] + DAUB_2 * s[i + 8] + DAUB_3 * s[i +12]) / 0x1000;
137 t[i + 8] = (DAUB_3 * s[i + 8] - DAUB_2 * s[i +12] + DAUB_1 * s[i + 0] - DAUB_0 * s[i + 4]) / 0x1000;
138 t[i +12] = (DAUB_3 * s[i + 0] - DAUB_2 * s[i + 4] + DAUB_1 * s[i + 8] - DAUB_0 * s[i +12]) / 0x1000;
141 /* Extract energies in LH, HL and HH bands */
142 block->lh = sqrt(t[8] * t[8] + t[9] * t[9] + t[12] * t[12] + t[13] * t[13]);
143 block->hl = sqrt(t[2] * t[2] + t[3] * t[3] + t[6] * t[6] + t[7] * t[7]);
144 block->hh = sqrt(t[10] * t[10] + t[11] * t[11] + t[14] * t[14] + t[15] * t[15]);
147 /* FIXME: simple average is for testing pusposes only */
154 for (uns i = 0; i < blocks_count; i++)
156 l_sum += blocks[i].l;
157 u_sum += blocks[i].u;
158 v_sum += blocks[i].v;
159 lh_sum += blocks[i].lh;
160 hl_sum += blocks[i].hl;
161 hh_sum += blocks[i].hh;
164 sig->vec.f[0] = l_sum / blocks_count;
165 sig->vec.f[1] = u_sum / blocks_count;
166 sig->vec.f[2] = v_sum / blocks_count;
167 sig->vec.f[3] = lh_sum / blocks_count;
168 sig->vec.f[4] = hl_sum / blocks_count;
169 sig->vec.f[5] = hh_sum / blocks_count;
175 DBG("Resulting signature is (%s)", stk_print_image_vector(&sig->vec));
178 static ExceptionInfo exception;
179 static QuantizeInfo quantize_info;
180 static ImageInfo *image_info;
183 compute_image_signature_prepare(void)
185 InitializeMagick(NULL);
186 GetExceptionInfo(&exception);
187 image_info = CloneImageInfo(NULL);
188 image_info->subrange = 1;
189 GetQuantizeInfo(&quantize_info);
190 quantize_info.colorspace = RGBColorspace;
194 compute_image_signature_finish(void)
196 DestroyImageInfo(image_info);
197 DestroyExceptionInfo(&exception);
202 compute_image_signature(void *data, uns len, struct image_signature *sig)
204 Image *image = BlobToImage(image_info, data, len, &exception); /* Damn slow... takes most of CPU time :-/ */
206 die("Invalid image format");
207 if (image->columns < 4 || image->rows < 4)
209 DBG("Image too small (%dx%d)", (int)image->columns, (int)image->rows);
213 QuantizeImage(&quantize_info, image); /* Also slow... and propably not necessary... */
214 PixelPacket *pixels = (PixelPacket *) AcquireImagePixels(image, 0, 0, image->columns, image->rows, &exception);
215 compute_image_area_signature(pixels, image->columns, image->rows, sig);