commit f69ec5134ffc1fc27764ca70d10be92517ec4498
Author:     Mattias Andrée <[email protected]>
AuthorDate: Sat May 13 20:35:26 2017 +0200
Commit:     Mattias Andrée <[email protected]>
CommitDate: Sat May 13 20:35:26 2017 +0200

    Add blind-make-kernel
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index 0d9e5bd..5ef88d3 100644
--- 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
index 8e767dc..0204564 100644
--- 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
new file mode 100644
index 0000000..903f489
--- /dev/null
+++ 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 < [email protected] >
diff --git a/man/blind.7 b/man/blind.7
index 1d9b579..2f8d872 100644
--- 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
new file mode 100644
index 0000000..67f8691
--- /dev/null
+++ 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
index f4a7f12..214a773 100644
--- 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;
+}

Reply via email to