blind-kernel.c - blind - suckless command-line video editing utility | |
git clone git://git.suckless.org/blind | |
Log | |
Files | |
Refs | |
README | |
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, … | |
8 #define STRCASEEQ2(A, B1, B2) (!strcasecmp(A, B1) || !strcasecmp(A, … | |
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… | |
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, doub… | |
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 … | |
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… | |
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, doub… | |
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-sp… | |
186 return NULL; | |
187 } | |
188 | |
189 static const double * | |
190 kernel_sharpen(int argc, char *argv[], size_t *rows, size_t *cols, doubl… | |
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, doub… | |
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-devi… | |
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_th… | |
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. :… | |
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 } |