blind-kernel.c - blind - suckless command-line video editing utility
(HTM) git clone git://git.suckless.org/blind
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
blind-kernel.c (9972B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "common.h"
3
4 USAGE("[-xyza] kernel [parameter] ...")
5
6 #define SUBUSAGE(FORMAT) "usage: %s [-xyza] " FORMAT "\n", argv0
7 #define STRCASEEQ3(A, B1, B2, B3) (!strcasecmp(A, B1) || !strcasecmp(A, B2) || !strcasecmp(A, B3))
8 #define STRCASEEQ2(A, B1, B2) (!strcasecmp(A, B1) || !strcasecmp(A, B2))
9
10 #define LIST_KERNELS\
11 X(kernel_kirsch, "kirsch")\
12 X(kernel_gradient, "gradient")\
13 X(kernel_sobel, "sobel")\
14 X(kernel_emboss, "emboss")\
15 X(kernel_box_blur, "box blur")\
16 X(kernel_sharpen, "sharpen")\
17 X(kernel_gaussian, "gaussian")
18
19 static const double *
20 kernel_kirsch(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
21 {
22 static const double matrices[][9] = {
23 { 5, 5, 5, -3, 0, -3, -3, -3, -3},
24 { 5, 5, -3, 5, 0, -3, -3, -3, -3},
25 { 5, -3, -3, 5, 0, -3, 5, -3, -3},
26 {-3, -3, -3, 5, 0, -3, 5, 5, -3},
27 {-3, -3, -3, -3, 0, -3, 5, 5, 5},
28 {-3, -3, -3, -3, 0, 5, -3, 5, 5},
29 {-3, -3, 5, -3, 0, 5, -3, -3, 5},
30 {-3, 5, 5, -3, 0, 5, -3, -3, -3},
31 };
32 *free_this = NULL;
33 *rows = *cols = 3;
34 if (argc != 1)
35 eprintf(SUBUSAGE("'kirsch' direction"));
36 if (STRCASEEQ3(argv[0], "1", "N", "N")) return matrices[0];
37 if (STRCASEEQ3(argv[0], "2", "NW", "WN")) return matrices[1];
38 if (STRCASEEQ3(argv[0], "3", "W", "W")) return matrices[2];
39 if (STRCASEEQ3(argv[0], "4", "SW", "WS")) return matrices[3];
40 if (STRCASEEQ3(argv[0], "5", "S", "S")) return matrices[4];
41 if (STRCASEEQ3(argv[0], "6", "SE", "ES")) return matrices[5];
42 if (STRCASEEQ3(argv[0], "7", "E", "E")) return matrices[6];
43 if (STRCASEEQ3(argv[0], "8", "NE", "EN")) return matrices[7];
44 eprintf("unrecognised direction: %s\n", argv[0]);
45 return NULL;
46 }
47
48 static const double *
49 kernel_gradient(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
50 {
51 static const double matrices[][9] = {
52 { 1, 1, 1, 0, 0, 0, -1, -1, -1},
53 { 1, 1, 0, 1, 0, -1, 0, -1, -1},
54 { 1, 0, -1, 1, 0, -1, 1, 0, -1},
55 { 0, -1, -1, 1, 0, -1, 1, 1, 0},
56 {-1, -1, -1, 0, 0, 0, 1, 1, 1},
57 {-1, -1, 0, -1, 0, 1, 0, 1, 1},
58 {-1, 0, 1, -1, 0, 1, -1, 0, 1},
59 { 0, 1, 1, -1, 0, 1, -1, -1, 0},
60 };
61 *free_this = NULL;
62 *rows = *cols = 3;
63 if (argc != 1)
64 eprintf(SUBUSAGE("'gradient' direction"));
65 if (STRCASEEQ2(argv[0], "N", "N")) return matrices[0];
66 if (STRCASEEQ2(argv[0], "NW", "WN")) return matrices[1];
67 if (STRCASEEQ2(argv[0], "W", "W")) return matrices[2];
68 if (STRCASEEQ2(argv[0], "SW", "WS")) return matrices[3];
69 if (STRCASEEQ2(argv[0], "S", "H")) return matrices[4];
70 if (STRCASEEQ2(argv[0], "SE", "ES")) return matrices[5];
71 if (STRCASEEQ2(argv[0], "E", "V")) return matrices[6];
72 if (STRCASEEQ2(argv[0], "NE", "EN")) return matrices[7];
73 eprintf("unrecognised direction: %s\n", argv[0]);
74 return NULL;
75 }
76
77 static const double *
78 kernel_sobel(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
79 {
80 static const double matrices[][9] = {
81 { 1, 2, 1, 0, 0, 0, -1, -2, -1},
82 { 2, 1, 0, 1, 0, -1, 0, -1, -2},
83 { 1, 0, -1, 2, 0, -2, 1, 0, -1},
84 { 0, -1, -2, 1, 0, -1, 2, 1, 0},
85 {-1, -2, -1, 0, 0, 0, 1, 2, 1},
86 {-2, -1, 0, -1, 0, 1, 0, 1, 2},
87 {-1, 0, 1, -2, 0, 2, -1, 0, 1},
88 { 0, 1, 2, -1, 0, 1, -2, -1, 0},
89 };
90 *free_this = NULL;
91 *rows = *cols = 3;
92 if (argc != 1)
93 eprintf(SUBUSAGE("'sobel' direction"));
94 if (STRCASEEQ2(argv[0], "N", "H")) return matrices[0];
95 if (STRCASEEQ2(argv[0], "NW", "WN")) return matrices[1];
96 if (STRCASEEQ2(argv[0], "W", "V")) return matrices[2];
97 if (STRCASEEQ2(argv[0], "SW", "WS")) return matrices[3];
98 if (STRCASEEQ2(argv[0], "S", "S")) return matrices[4];
99 if (STRCASEEQ2(argv[0], "SE", "ES")) return matrices[5];
100 if (STRCASEEQ2(argv[0], "E", "E")) return matrices[6];
101 if (STRCASEEQ2(argv[0], "NE", "EN")) return matrices[7];
102 eprintf("unrecognised direction: %s\n", argv[0]);
103 return NULL;
104 }
105
106 static const double *
107 kernel_emboss(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
108 {
109 static const double matrices[][9] = {
110 { 1, 2, 1, 0, 1, 0, -1, -2, -1},
111 { 2, 1, 0, 1, 1, -1, 0, -1, -2},
112 { 1, 0, -1, 2, 1, -2, 1, 0, -1},
113 { 0, -1, -2, 1, 1, -1, 2, 1, 0},
114 {-1, -2, -1, 0, 1, 0, 1, 2, 1},
115 {-2, -1, 0, -1, 1, 1, 0, 1, 2},
116 {-1, 0, 1, -2, 1, 2, -1, 0, 1},
117 { 0, 1, 2, -1, 1, 1, -2, -1, 0},
118 };
119 *free_this = NULL;
120 *rows = *cols = 3;
121 if (argc > 1)
122 eprintf(SUBUSAGE("'emboss' [direction]"));
123 if (!argc)
124 return matrices[5];
125 if (STRCASEEQ2(argv[0], "N", "N")) return matrices[0];
126 if (STRCASEEQ2(argv[0], "NW", "WN")) return matrices[1];
127 if (STRCASEEQ2(argv[0], "W", "W")) return matrices[2];
128 if (STRCASEEQ2(argv[0], "SW", "WS")) return matrices[3];
129 if (STRCASEEQ2(argv[0], "S", "S")) return matrices[4];
130 if (STRCASEEQ2(argv[0], "SE", "ES")) return matrices[5];
131 if (STRCASEEQ2(argv[0], "E", "E")) return matrices[6];
132 if (STRCASEEQ2(argv[0], "NE", "EN")) return matrices[7];
133 eprintf("unrecognised direction: %s\n", argv[0]);
134 return NULL;
135 }
136
137 static const double *
138 kernel_box_blur(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
139 {
140 size_t sx = 1, sy = 1, i, n;
141 double *cells, value, weight = 0;
142 int have_weight = 0;
143 char *arg;
144 *free_this = NULL;
145 *rows = *cols = 3;
146
147 #define argv0 arg
148 argc++, argv--;
149 ARGBEGIN {
150 case 'w':
151 if (!(arg = ARGF()))
152 goto usage;
153 weight = etolf_flag('w', arg);
154 have_weight = 1;
155 break;
156 default:
157 goto usage;
158 } ARGEND;
159 #undef argv0
160
161 if (argc == 1) {
162 sx = sy = etozu_arg("spread", argv[0], 0, SIZE_MAX / 2);
163 } else if (argc == 2) {
164 sx = etozu_arg("x-spread", argv[0], 0, SIZE_MAX / 2);
165 sy = etozu_arg("y-spread", argv[1], 0, SIZE_MAX / 2);
166 } else if (argc) {
167 goto usage;
168 }
169
170 *rows = 2 * sy + 1;
171 *cols = 2 * sx + 1;
172 *free_this = cells = emalloc3(*rows, *cols, sizeof(double));
173
174 n = (2 * sy + 1) * (2 * sx + 1);
175 value = 1 / (double)n;
176 if (have_weight)
177 value = (1.0 - weight) / (double)(n - 1);
178 for (i = 0; i < n; i++)
179 cells[i] = value;
180 if (have_weight)
181 cells[sy * *cols + sx] = weight;
182 return cells;
183
184 usage:
185 eprintf(SUBUSAGE("'box blur' [-w weight] [spread | x-spread y-spread]"));
186 return NULL;
187 }
188
189 static const double *
190 kernel_sharpen(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
191 {
192 static const double matrices[][9] = {
193 { 0, -1, 0, -1, 5, -1, 0, -1, 0},
194 {-1, -1, -1, -1, 9, -1, -1, -1, -1}
195 };
196 char *arg;
197 int intensified = 0;
198 *free_this = NULL;
199 *rows = *cols = 3;
200
201 #define argv0 arg
202 (void) arg;
203 argc++, argv--;
204 ARGBEGIN {
205 case 'i':
206 intensified = 1;
207 break;
208 default:
209 goto usage;
210 } ARGEND;
211 #undef argv0
212 if (argc)
213 goto usage;
214
215 return matrices[intensified];
216 usage:
217 eprintf(SUBUSAGE("'sharpen' [-i]"));
218 return NULL;
219 }
220
221 static const double *
222 kernel_gaussian(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
223 {
224 size_t spread = 0, y, x;
225 ssize_t xx, yy;
226 int unsharpen = 0, glow = 0;
227 double sigma, value, c, k;
228 char *arg;
229
230 #define argv0 arg
231 argc++, argv--;
232 ARGBEGIN {
233 case 'g':
234 glow = 1;
235 break;
236 case 's':
237 if (!(arg = ARGF()))
238 goto usage;
239 spread = etozu_flag('s', arg, 1, SIZE_MAX / 2);
240 break;
241 case 'u':
242 unsharpen = 1;
243 break;
244 default:
245 goto usage;
246 } ARGEND;
247 #undef argv0
248
249 if (argc != 1 || (unsharpen && glow))
250 goto usage;
251
252 sigma = etolf_arg("standard-deviation", argv[0]);
253
254 if (!spread)
255 spread = (size_t)(sigma * 3.0 + 0.5);
256 *rows = *cols = spread * 2 + 1;
257
258 *free_this = emalloc3(*rows, *cols, sizeof(double));
259
260 k = sigma * sigma * 2.;
261 c = M_PI * k;
262 c = 1.0 / c;
263 k = 1.0 / -k;
264 for (y = 0; y < 2 * spread + 1; y++) {
265 yy = (ssize_t)spread - (ssize_t)y, yy *= yy;
266 for (x = 0; x < 2 * spread + 1; x++) {
267 xx = (ssize_t)spread - (ssize_t)x, xx *= xx;
268 value = (double)(xx + yy) * k;
269 value = exp(value) * c;
270 (*free_this)[y * *cols + x] = value;
271 }
272 }
273
274 if (unsharpen)
275 (*free_this)[spread * *cols + spread] -= 2.0;
276 if (glow)
277 (*free_this)[spread * *cols + spread] += 1;
278
279 return *free_this;
280
281 usage:
282 eprintf(SUBUSAGE("'gaussian' [-s spread] [-g | -u] standard-deviation"));
283 return NULL;
284 }
285
286 /* TODO more kernels:
287 Edge detection: MATRIX( 1, 0, -1, 0, 0, 0, -1, 0, 1)
288 Edge detection: MATRIX( 0, 1, 0, 1, -4, 1, 0, 1, 0)
289 Edge detection: MATRIX(-1, -1, -1, -1, 8, -1, -1, -1, -1)
290 Edge detection: MATRIX( 0, 0, 0, -1, 2, -1, 0, 0, 0) [H]
291 Edge detection: MATRIX( 0, -1, 0, 0, 2, 0, 0, -1, 0) [V]
292 Edge enhance: MATRIX( 0, 0, 0, -1, 1, 0, 0, 0, 0)
293 */
294
295 int
296 main(int argc, char *argv[])
297 {
298 int id_x = 1, id_y = 1, id_z = 1, id_a = 1;
299 size_t rows, cols, y, x, n;
300 const double *kernel, *kern;
301 double *buffer, *buf, *free_this, id_val;
302
303 ARGBEGIN {
304 case 'x':
305 id_x = 0;
306 break;
307 case 'y':
308 id_y = 0;
309 break;
310 case 'z':
311 id_z = 0;
312 break;
313 case 'a':
314 id_a = 0;
315 break;
316 default:
317 usage();
318 } ARGEND;
319
320 if (!argc)
321 usage();
322
323 if (id_x && id_y && id_z && id_a)
324 id_x = id_y = id_z = id_a = 0;
325
326 if (0);
327 #define X(FUNC, NAME)\
328 else if (!strcmp(argv[0], NAME))\
329 kernel = FUNC(argc - 1, argv + 1, &rows, &cols, &free_this);
330 LIST_KERNELS
331 #undef X
332 else
333 eprintf("unrecognised kernel: %s\n", argv[0]);
334
335 FPRINTF_HEAD(stdout, (size_t)1, cols, rows, "xyza");
336 efflush(stdout, "<stdout>");
337
338 buffer = emalloc2(cols, 4 * sizeof(double));
339 n = cols * 4 * sizeof(double);
340
341 kern = kernel;
342 for (y = 0; y < rows; y++) {
343 buf = buffer;
344 for (x = 0; x < cols; x++) {
345 id_val = (x == cols / 2 && y == rows / 2) ? 1. : 0.;
346 buf[0] = id_x ? id_val : *kern;
347 buf[1] = id_y ? id_val : *kern;
348 buf[2] = id_z ? id_val : *kern;
349 buf[3] = id_a ? id_val : *kern;
350 buf += 4;
351 kern++;
352 }
353 ewriteall(STDOUT_FILENO, buffer, n, "<stdout>");
354 }
355
356 free(buffer);
357 free(free_this);
358 return 0;
359 }