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:
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;
+}