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 }