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 }