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, 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 }