blind

suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log | Files | Refs | README | LICENSE

commit f69ec5134ffc1fc27764ca70d10be92517ec4498
parent 0f189ac84817338ce8ec1572244d1aa680a232f8
Author: Mattias Andrée <maandree@kth.se>
Date:   Sat, 13 May 2017 20:35:26 +0200

Add blind-make-kernel

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
MMakefile | 1+
MREADME | 3+++
Aman/blind-make-kernel.1 | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mman/blind.7 | 3+++
Asrc/blind-make-kernel.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util/emalloc.h | 17+++++++++++++++--
6 files changed, 262 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile @@ -21,6 +21,7 @@ BIN =\ blind-from-video\ blind-gauss-blur\ blind-invert-luma\ + blind-make-kernel\ blind-next-frame\ blind-read-head\ blind-repeat\ diff --git a/README b/README @@ -63,6 +63,9 @@ UTILITIES blind-invert-luma(1) Invert the luminosity of a video + blind-make-kernel(1) + Create a custom convolution matrix + blind-next-frame(1) Extracts the next frame from a video diff --git a/man/blind-make-kernel.1 b/man/blind-make-kernel.1 @@ -0,0 +1,84 @@ +.TH BLIND-MAKE-KERNEL 1 blind +.SH NAME +blind-make-kernel - Create a custom convolution matrix +.SH SYNOPSIS +.B blind-make-kernel +[-d +.IR denominator ]\ ... +[-nxyza] +-- +.IR value \ ...]\ ... +.SH DESCRIPTION +.B blind-make-kernel +creates a convolution matrix that can be applied to +a video using +.BR blind-apply-kernel (1). +.P +The matrix is construct from each +.I value +with +.B -- +delimiting the rows. For example +.P +.nf + blind-make-kernel 1 2 3 -- 4 5 6 +.fi +.P +creates the matrix +.P +.nf + 1 2 3 + 4 5 6 +.fi +.P +If no +.I value +is specified (and at most one +.B -- +is used) +the matrix is read from stdin, <newline> +is used to delimit rows and other whitespace +is used to delimit cells. +.SH OPTIONS +.TP +.B -a +Apply the values to the alpha channel, set the +values for all unselected channels to zero. +.TP +.BR -d \ \fIdenominator\fP +Divide the matrix by +.IR denominator . +This option is applied after +.B -n +and can be used multiple times. The product of +all specified values are used as the denominator. +.TP +.B -n +Normalise the matrix, that is, divide it by the +sum of all its elements. This option is applied +before +.BR -d . +.TP +.B -x +Apply the values to the X channel, set the values +for all unselected channels to zero. +.TP +.B -y +Apply the values to the Y channel, set the values +for all unselected channels to zero. +.TP +.B -z +Apply the values to the Z channel, set the values +for all unselected channels to zero. +.SH NOTES +.B blind-make-kernel +Create a single frame, to that it can be stored to +disk. When applying it to a video, you want to use +.BR blind-repeat (1). +.SH SEE ALSO +.BR blind (7), +.BR blind-apply-kernel (1), +.BR blind-repeat (1) +.SH AUTHORS +Mattias Andrée +.RI < maandree@kth.se > diff --git a/man/blind.7 b/man/blind.7 @@ -76,6 +76,9 @@ Apply Gaussian blur to a video .BR blind-invert-luma (1) Invert the luminosity of a video .TP +.BR blind-make-kernel (1) +Create a custom convolution matrix +.TP .BR blind-next-frame (1) Extracts the next frame from a video .TP diff --git a/src/blind-make-kernel.c b/src/blind-make-kernel.c @@ -0,0 +1,156 @@ +/* See LICENSE file for copyright and license details. */ +#include "stream.h" +#include "util.h" + +#include <ctype.h> +#include <string.h> + +USAGE("[-d denominator] ... [-nxyza] [-- value ...] ...") + +static void +new_row(double **kernel, size_t *col, size_t *rows, size_t *cols) +{ + if (!*col) + return; + if (*rows && *col != *cols) + eprintf("the rows in the matrix do not have the same number of columns\n"); + *kernel = erealloc3(*kernel, 1 + ++*rows, *cols = *col, sizeof(**kernel)); + *col = 0; +} + +static void +new_col(char *arg, double **kernel, size_t *col, size_t *rows, size_t *cols) +{ + if (*rows && *col >= *cols) + eprintf("the rows in the matrix do not have the same number of columns\n"); + if (!*rows) + *kernel = erealloc2(*kernel, *col + 1, sizeof(**kernel)); + if (tolf(arg, &(*kernel)[*rows * *cols + (*col)++])) + eprintf("matrix cell values must be floating-point values\n"); +} + +static void +finalise(double **kernel, size_t col, size_t *rows, size_t *cols) +{ + if (col) + new_row(kernel, &col, rows, cols); + if (!*rows) + eprintf("the matrix cannot be null-sized\n"); +} + +static double * +read_matrix_cmdline(char *args[], size_t *rows, size_t *cols) +{ + size_t col = 0; + double *kernel = NULL; + *rows = *cols = 0; + for (; *args; args++) { + if (!strcmp(*args, "--")) + new_row(&kernel, &col, rows, cols); + else + new_col(*args, &kernel, &col, rows, cols); + } + finalise(&kernel, col, rows, cols); + return kernel; +} + +static double * +read_matrix_stdin(size_t *rows, size_t *cols) +{ + char *line = NULL, *p, *q; + size_t size = 0, col; + double *kernel = NULL; + ssize_t len; + *rows = *cols = 0; + while ((len = getline(&line, &size, stdin)) >= 0) { + col = 0; + for (p = line;; p = q) { + while (*p && isspace(*p)) p++; + if (!*(q = p)) + break; + while (*q && !isspace(*q)) q++; + *q++ = '\0'; + new_col(p, &kernel, &col, rows, cols); + } + new_row(&kernel, &col, rows, cols); + } + free(line); + if (ferror(stdout)) + eprintf("getline:"); + finalise(&kernel, col, rows, cols); + return kernel; +} + +int +main(int argc, char *argv[]) +{ + int normalise = 0; + double denominator = 1; + int null_x = 1, null_y = 1, null_z = 1, null_a = 1; + size_t rows, cols, y, x, n; + double *kernel, *kern, sum = 0, value; + double *buffer, *buf; + + ARGBEGIN { + case 'd': + denominator *= etolf_flag('d', UARGF()); + break; + case 'n': + normalise = 1; + break; + case 'x': + null_x = 0; + break; + case 'y': + null_y = 0; + break; + case 'z': + null_z = 0; + break; + case 'a': + null_a = 0; + break; + default: + usage(); + } ARGEND; + + if (null_x && null_y && null_z && null_a) + null_x = null_y = null_z = null_a = 0; + + if (argc) + kernel = read_matrix_cmdline(argv, &rows, &cols); + else + kernel = read_matrix_stdin(&rows, &cols); + + FPRINTF_HEAD(stdout, (size_t)1, cols, rows, "xyza"); + efflush(stdout, "<stdout>"); + + buffer = emalloc2(cols, 4 * sizeof(double)); + n = cols * 4 * sizeof(double); + + if (normalise) { + kern = kernel; + for (y = 0; y < rows; y++) + for (x = 0; x < cols; x++) + sum += *kern++; + denominator *= sum; + } + + kern = kernel; + for (y = 0; y < rows; y++) { + buf = buffer; + for (x = 0; x < cols; x++) { + value = *kern++ / denominator; + buf[0] = null_x ? 0.0 : value; + buf[1] = null_y ? 0.0 : value; + buf[2] = null_z ? 0.0 : value; + buf[3] = null_a ? 0.0 : value; + buf += 4; + } + ewriteall(STDOUT_FILENO, buffer, n, "<stdout>"); + } + + free(kernel); + free(buffer); + return 0; +} diff --git a/src/util/emalloc.h b/src/util/emalloc.h @@ -7,9 +7,11 @@ #define ecalloc(...) encalloc(1, __VA_ARGS__) #define erealloc(...) enrealloc(1, __VA_ARGS__) #define erealloc2(...) enrealloc2(1, __VA_ARGS__) +#define erealloc3(...) enrealloc3(1, __VA_ARGS__) -#define malloc2(n, m) malloc(n * m); -#define realloc3(p, n, m) realloc(p, n * m); +#define malloc2(n, m) malloc(n * m); +#define realloc2(p, n, m) realloc(p, n * m); +#define realloc3(p, n1, n2, n3) realloc(p, n1 * n2 * n3); static inline void * enmalloc(int status, size_t n) @@ -54,3 +56,14 @@ enrealloc2(int status, void *ptr, size_t n, size_t m) enprintf(status, "realloc: out of memory\n"); return ptr; } + +static inline void * +enrealloc3(int status, void *ptr, size_t n1, size_t n2, size_t n3) +{ + size_t n = n1; + if (n2 > SIZE_MAX / n || + n3 > SIZE_MAX / (n *= n2) || + !(ptr = realloc(ptr, n * n3))) + enprintf(status, "realloc: out of memory\n"); + return ptr; +}