1 /*
2 * Some filters for Potrace in Inkscape
3 *
4 * Authors:
5 * Bob Jamison <rjamison@titan.com>
6 * Stéphane Gimenez <dev@gim.name>
7 *
8 * Copyright (C) 2004-2006 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include <cstdio>
14 #include <stdlib.h>
16 #include "imagemap-gdk.h"
17 #include "filterset.h"
18 #include "quantize.h"
20 /*#########################################################################
21 ### G A U S S I A N (smoothing)
22 #########################################################################*/
24 /**
25 *
26 */
27 static int gaussMatrix[] =
28 {
29 2, 4, 5, 4, 2,
30 4, 9, 12, 9, 4,
31 5, 12, 15, 12, 5,
32 4, 9, 12, 9, 4,
33 2, 4, 5, 4, 2
34 };
37 /**
38 *
39 */
40 GrayMap *grayMapGaussian(GrayMap *me)
41 {
42 int width = me->width;
43 int height = me->height;
44 int firstX = 2;
45 int lastX = width-3;
46 int firstY = 2;
47 int lastY = height-3;
49 GrayMap *newGm = GrayMapCreate(width, height);
50 if (!newGm)
51 return NULL;
53 for (int y = 0 ; y<height ; y++)
54 {
55 for (int x = 0 ; x<width ; x++)
56 {
57 /* image boundaries */
58 if (x<firstX || x>lastX || y<firstY || y>lastY)
59 {
60 newGm->setPixel(newGm, x, y, me->getPixel(me, x, y));
61 continue;
62 }
64 /* all other pixels */
65 int gaussIndex = 0;
66 unsigned long sum = 0;
67 for (int i= y-2 ; i<=y+2 ; i++)
68 {
69 for (int j= x-2; j<=x+2 ; j++)
70 {
71 int weight = gaussMatrix[gaussIndex++];
72 sum += me->getPixel(me, j, i) * weight;
73 }
74 }
75 sum /= 159;
76 newGm->setPixel(newGm, x, y, sum);
77 }
78 }
80 return newGm;
81 }
87 /**
88 *
89 */
90 RgbMap *rgbMapGaussian(RgbMap *me)
91 {
92 int width = me->width;
93 int height = me->height;
94 int firstX = 2;
95 int lastX = width-3;
96 int firstY = 2;
97 int lastY = height-3;
99 RgbMap *newGm = RgbMapCreate(width, height);
100 if (!newGm)
101 return NULL;
103 for (int y = 0 ; y<height ; y++)
104 {
105 for (int x = 0 ; x<width ; x++)
106 {
107 /* image boundaries */
108 if (x<firstX || x>lastX || y<firstY || y>lastY)
109 {
110 newGm->setPixelRGB(newGm, x, y, me->getPixel(me, x, y));
111 continue;
112 }
114 /* all other pixels */
115 int gaussIndex = 0;
116 int sumR = 0;
117 int sumG = 0;
118 int sumB = 0;
119 for (int i= y-2 ; i<=y+2 ; i++)
120 {
121 for (int j= x-2; j<=x+2 ; j++)
122 {
123 int weight = gaussMatrix[gaussIndex++];
124 RGB rgb = me->getPixel(me, j, i);
125 sumR += weight * (int)rgb.r;
126 sumG += weight * (int)rgb.g;
127 sumB += weight * (int)rgb.b;
128 }
129 }
130 RGB rout;
131 rout.r = ( sumR / 159 ) & 0xff;
132 rout.g = ( sumG / 159 ) & 0xff;
133 rout.b = ( sumB / 159 ) & 0xff;
134 newGm->setPixelRGB(newGm, x, y, rout);
135 }
136 }
138 return newGm;
140 }
145 /*#########################################################################
146 ### C A N N Y E D G E D E T E C T I O N
147 #########################################################################*/
150 static int sobelX[] =
151 {
152 -1, 0, 1 ,
153 -2, 0, 2 ,
154 -1, 0, 1
155 };
157 static int sobelY[] =
158 {
159 1, 2, 1 ,
160 0, 0, 0 ,
161 -1, -2, -1
162 };
166 /**
167 * Perform Sobel convolution on a GrayMap
168 */
169 static GrayMap *grayMapSobel(GrayMap *gm,
170 double dLowThreshold, double dHighThreshold)
171 {
172 int width = gm->width;
173 int height = gm->height;
174 int firstX = 1;
175 int lastX = width-2;
176 int firstY = 1;
177 int lastY = height-2;
179 GrayMap *newGm = GrayMapCreate(width, height);
180 if (!newGm)
181 return NULL;
183 for (int y = 0 ; y<height ; y++)
184 {
185 for (int x = 0 ; x<width ; x++)
186 {
187 unsigned long sum = 0;
188 /* image boundaries */
189 if (x<firstX || x>lastX || y<firstY || y>lastY)
190 {
191 sum = 0;
192 }
193 else
194 {
195 /* ### SOBEL FILTERING #### */
196 long sumX = 0;
197 long sumY = 0;
198 int sobelIndex = 0;
199 for (int i= y-1 ; i<=y+1 ; i++)
200 {
201 for (int j= x-1; j<=x+1 ; j++)
202 {
203 sumX += gm->getPixel(gm, j, i) *
204 sobelX[sobelIndex++];
205 }
206 }
208 sobelIndex = 0;
209 for (int i= y-1 ; i<=y+1 ; i++)
210 {
211 for (int j= x-1; j<=x+1 ; j++)
212 {
213 sumY += gm->getPixel(gm, j, i) *
214 sobelY[sobelIndex++];
215 }
216 }
217 /*### GET VALUE ### */
218 sum = abs(sumX) + abs(sumY);
220 if (sum > 765)
221 sum = 765;
223 #if 0
224 /*### GET ORIENTATION (slow, pedantic way) ### */
225 double orient = 0.0;
226 if (sumX==0)
227 {
228 if (sumY==0)
229 orient = 0.0;
230 else if (sumY<0)
231 {
232 sumY = -sumY;
233 orient = 90.0;
234 }
235 else
236 orient = 90.0;
237 }
238 else
239 {
240 orient = 57.295779515 * atan2( ((double)sumY),((double)sumX) );
241 if (orient < 0.0)
242 orient += 180.0;
243 }
245 /*### GET EDGE DIRECTION ### */
246 int edgeDirection = 0;
247 if (orient < 22.5)
248 edgeDirection = 0;
249 else if (orient < 67.5)
250 edgeDirection = 45;
251 else if (orient < 112.5)
252 edgeDirection = 90;
253 else if (orient < 157.5)
254 edgeDirection = 135;
255 #else
256 /*### GET EDGE DIRECTION (fast way) ### */
257 int edgeDirection = 0; /*x,y=0*/
258 if (sumX==0)
259 {
260 if (sumY!=0)
261 edgeDirection = 90;
262 }
263 else
264 {
265 /*long slope = sumY*1024/sumX;*/
266 long slope = (sumY << 10)/sumX;
267 if (slope > 2472 || slope< -2472) /*tan(67.5)*1024*/
268 edgeDirection = 90;
269 else if (slope > 414) /*tan(22.5)*1024*/
270 edgeDirection = 45;
271 else if (slope < -414) /*-tan(22.5)*1024*/
272 edgeDirection = 135;
273 }
275 #endif
276 /* printf("%ld %ld %f %d\n", sumX, sumY, orient, edgeDirection); */
278 /*### Get two adjacent pixels in edge direction ### */
279 unsigned long leftPixel;
280 unsigned long rightPixel;
281 if (edgeDirection == 0)
282 {
283 leftPixel = gm->getPixel(gm, x-1, y);
284 rightPixel = gm->getPixel(gm, x+1, y);
285 }
286 else if (edgeDirection == 45)
287 {
288 leftPixel = gm->getPixel(gm, x-1, y+1);
289 rightPixel = gm->getPixel(gm, x+1, y-1);
290 }
291 else if (edgeDirection == 90)
292 {
293 leftPixel = gm->getPixel(gm, x, y-1);
294 rightPixel = gm->getPixel(gm, x, y+1);
295 }
296 else /*135 */
297 {
298 leftPixel = gm->getPixel(gm, x-1, y-1);
299 rightPixel = gm->getPixel(gm, x+1, y+1);
300 }
302 /*### Compare current value to adjacent pixels ### */
303 /*### if less that either, suppress it ### */
304 if (sum < leftPixel || sum < rightPixel)
305 sum = 0;
306 else
307 {
308 unsigned long highThreshold =
309 (unsigned long)(dHighThreshold * 765.0);
310 unsigned long lowThreshold =
311 (unsigned long)(dLowThreshold * 765.0);
312 if (sum >= highThreshold)
313 sum = 765; /* EDGE. 3*255 this needs to be settable */
314 else if (sum < lowThreshold)
315 sum = 0; /* NONEDGE */
316 else
317 {
318 if ( gm->getPixel(gm, x-1, y-1)> highThreshold ||
319 gm->getPixel(gm, x , y-1)> highThreshold ||
320 gm->getPixel(gm, x+1, y-1)> highThreshold ||
321 gm->getPixel(gm, x-1, y )> highThreshold ||
322 gm->getPixel(gm, x+1, y )> highThreshold ||
323 gm->getPixel(gm, x-1, y+1)> highThreshold ||
324 gm->getPixel(gm, x , y+1)> highThreshold ||
325 gm->getPixel(gm, x+1, y+1)> highThreshold)
326 sum = 765; /* EDGE fix me too */
327 else
328 sum = 0; /* NONEDGE */
329 }
330 }
333 }/* else */
334 if (sum==0) /* invert light & dark */
335 sum = 765;
336 else
337 sum = 0;
338 newGm->setPixel(newGm, x, y, sum);
339 }/* for (x) */
340 }/* for (y) */
342 return newGm;
343 }
349 /**
350 *
351 */
352 GrayMap *
353 grayMapCanny(GrayMap *gm, double lowThreshold, double highThreshold)
354 {
355 if (!gm)
356 return NULL;
358 GrayMap *cannyGm = grayMapSobel(gm, lowThreshold, highThreshold);
359 if (!cannyGm)
360 return NULL;
361 /*cannyGm->writePPM(cannyGm, "canny.ppm");*/
363 return cannyGm;
364 }
372 /**
373 *
374 */
375 GdkPixbuf *
376 gdkCanny(GdkPixbuf *img, double lowThreshold, double highThreshold)
377 {
378 if (!img)
379 return NULL;
382 GrayMap *grayMap = gdkPixbufToGrayMap(img);
383 if (!grayMap)
384 return NULL;
386 /*grayMap->writePPM(grayMap, "gbefore.ppm");*/
388 GrayMap *cannyGm = grayMapCanny(grayMap,lowThreshold, highThreshold);
390 grayMap->destroy(grayMap);
392 if (!cannyGm)
393 return NULL;
395 /*grayMap->writePPM(grayMap, "gafter.ppm");*/
397 GdkPixbuf *newImg = grayMapToGdkPixbuf(cannyGm);
400 return newImg;
401 }
403 /*#########################################################################
404 ### Q U A N T I Z A T I O N
405 #########################################################################*/
407 /**
408 * Experimental. Work on this later
409 */
410 GrayMap *quantizeBand(RgbMap *rgbMap, int nrColors)
411 {
413 RgbMap *gaussMap = rgbMapGaussian(rgbMap);
414 //gaussMap->writePPM(gaussMap, "rgbgauss.ppm");
416 IndexedMap *qMap = rgbMapQuantize(gaussMap, nrColors);
417 //qMap->writePPM(qMap, "rgbquant.ppm");
418 gaussMap->destroy(gaussMap);
420 GrayMap *gm = GrayMapCreate(rgbMap->width, rgbMap->height);
422 // RGB is quantized. There should now be a small set of (R+G+B)
423 for (int y=0 ; y<qMap->height ; y++)
424 {
425 for (int x=0 ; x<qMap->width ; x++)
426 {
427 RGB rgb = qMap->getPixelValue(qMap, x, y);
428 int sum = rgb.r + rgb.g + rgb.b;
429 if (sum & 1)
430 sum = 765;
431 else
432 sum = 0;
433 // printf("%d %d %d : %d\n", rgb.r, rgb.g, rgb.b, index);
434 gm->setPixel(gm, x, y, sum);
435 }
436 }
438 qMap->destroy(qMap);
440 return gm;
441 }
444 /*#########################################################################
445 ### E N D O F F I L E
446 #########################################################################*/