blind

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

commit ebe2e88b44f46e59bdefef1eb585078d5fa6d4d4
parent 28635cabc72a60674c18c0e160fefdbd4af41734
Author: Mattias Andrée <maandree@kth.se>
Date:   Sat, 22 Jul 2017 19:33:54 +0200

Add blind-apply-kernel

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

Diffstat:
Makefile | 1+
README | 6++++++
TODO | 1-
man/blind-apply-kernel.1 | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/blind-gauss-blur.1 | 4+++-
man/blind.7 | 6++++++
src/blind-affine-colour.c | 2++
src/blind-apply-kernel.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 246 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,6 +4,7 @@ include $(CONFIGFILE) BIN =\ blind-affine-colour\ + blind-apply-kernel\ blind-apply-palette\ blind-arithm\ blind-cat-cols\ diff --git a/README b/README @@ -15,6 +15,12 @@ UTILITIES blind-affine-colour(1) Apply an affine transformation to the colours in a video + blind-apply-kernel(1) + Apply a convolution matrix to a video + + blind-apply-palette(1) + Apply a colour palette to a video + blind-arithm(1) Perform simple arithmetic on a video diff --git a/TODO b/TODO @@ -4,7 +4,6 @@ blind-primary-key replace a primary with transparency, -g for greyscaled images blind-colour-matrix create colour space conversion matrix blind-apply-map remap pixels (distortion) using the X and Y values, -[xy] for tiling, -s for improve quality on downscaling (pixels' neighbours must not change) -blind-apply-kernel apply a convolution matrix. blind-find-frame a graphical tool for locating frames, should highlight key frames, and play audio. Should support both regular videos files and uivf finding key frames: ffprobe -show_frames (lists all frames) diff --git a/man/blind-apply-kernel.1 b/man/blind-apply-kernel.1 @@ -0,0 +1,69 @@ +.TH BLIND-APPLY-KERNEL 1 blind +.SH NAME +blind-apply-kernel - Apply a convolution matrix to a video +.SH SYNOPSIS +.B blind-apply-kernel +[-apPxy] +.I kernel-stream +.SH DESCRIPTION +.B blind-apply-kernel +reads a video from stdin and a convolution matrix video +from +.I kernel-stream +and apply the convolution matrix in each frame the +same frame in stdin, and prints the resulting video +to stdout. +.SH OPTIONS +.TP +.B -a +Used to optimise performance if it is known that +the video is opaque, and to ensure that the output +video is opaque. +.TP +.B -p +Each frame in +.I kernel-stream +shall contain one matrix per pixel in a frame in +stdin. The width of +.I kernel-stream +shall be a multiple of the width of stdin, the width +shall be the width of stdin multiplied by the width +of the convolution matrix. The height of +.I kernel-stream +shall be a multiple of the height of stdin, the +height shall be the height of stdin multiplied by the +height of the convolution matrix. +.TP +.B -P +Apply the convolution with first premultiplying +the alpha channel. +.TP +.B -x +When encountering the left or right edge of the video, +wrap around to the opposite edge. +.TP +.B -y +When encountering the upper or lower edge of the video, +wrap around to the opposite edge. +.SH REQUIREMENTS +.B blind-apply-kernel +requires enough free memory to load two full frames +from stdin and one full frame from +.I kernel-stream +into memory. However, if +.I -p +is used, the height of +.I kernel-stream +divide by the height of stdin number of rows from +.I kernel-stream +rather than a full frame from +.I kernel-stream +is loaded into memory. +A frame or row requires 32 bytes per pixel it contains. +.SH SEE ALSO +.BR blind (7), +.BR blind-make-kernel (1), +.BR blind-gauss-blur (1) +.SH AUTHORS +Mattias Andrée +.RI < maandree@kth.se > diff --git a/man/blind-gauss-blur.1 b/man/blind-gauss-blur.1 @@ -79,7 +79,9 @@ memory. A frame requires 32 bytes per pixel it contains. .SH SEE ALSO .BR blind (7), .BR blind-single-colour (1), -.BR blind-time-blur (1) +.BR blind-time-blur (1), +.BR blind-make-kernel (1), +.BR blind-apply-kernel (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind.7 b/man/blind.7 @@ -22,6 +22,12 @@ first convert it with .BR blind-affine-colour (1) Apply an affine transformation to the colours in a video .TP +.BR blind-apply-kernel (1) +Apply a convolution matrix to a video +.TP +.BR blind-apply-palette (1) +Apply a colour palette to a video +.TP .BR blind-arithm (1) Perform simple arithmetic on a video .TP diff --git a/src/blind-affine-colour.c b/src/blind-affine-colour.c @@ -113,6 +113,8 @@ PROCESS(struct stream *colour, struct stream *matrix) } while (eread_stream(colour, SIZE_MAX)); if (colour->ptr) eprintf("%s: incomplete frame\n", colour->file); + + free(mbuf); } #endif diff --git a/src/blind-apply-kernel.c b/src/blind-apply-kernel.c @@ -0,0 +1,159 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TYPE +#include "common.h" + +USAGE("[-apPxy] kernel-stream") + +static int no_alpha = 0; +static int dont_premultiply = 0; +static int per_pixel = 0; +static int wrap_x = 0; +static int wrap_y = 0; +static size_t kern_w; +static size_t kern_h; + +#define FILE "blind-apply-kernel.c" +#include "define-functions.h" + +int +main(int argc, char *argv[]) +{ + struct stream colour, kernel; + void (*process)(struct stream *colour, struct stream *kernel); + size_t tmp; + + ARGBEGIN { + case 'a': + no_alpha = 1; + break; + case 'p': + per_pixel = 1; + break; + case 'P': + dont_premultiply = 1; + break; + case 'x': + wrap_x = 1; + break; + case 'y': + wrap_y = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 1) + usage(); + + eopen_stream(&colour, NULL); + eopen_stream(&kernel, argv[0]); + + SELECT_PROCESS_FUNCTION(&colour); + if (colour.encoding != kernel.encoding || colour.n_chan != kernel.n_chan) + eprintf("videos use incompatible pixel formats"); + if (per_pixel && !(kernel.width % colour.width || kernel.height % colour.height)) + eprintf("-p is specified but the dimensions of kernel-stream " + "are not multiples of the dimensions of stdin."); + + kern_w = per_pixel ? kernel.width / colour.width : kernel.width; + kern_h = per_pixel ? kernel.height / colour.height : kernel.height; + + tmp = kernel.height, kernel.height = kern_h; + echeck_dimensions(&colour, WIDTH | HEIGHT, NULL); + echeck_dimensions(&kernel, WIDTH | HEIGHT, NULL); + kernel.height = tmp; + + fprint_stream_head(stdout, &colour); + efflush(stdout, "<stdout>"); + process(&colour, &kernel); + return 0; +} + +#else + +static void +PROCESS(struct stream *colour, struct stream *kernel) +{ + TYPE *out, *clr, *krn, *kern, *pix; + size_t i, x, y, n, x2, y2; + ssize_t cx, cy, xoff, yoff; + + out = emalloc(colour->frame_size); + clr = emalloc(colour->frame_size); + krn = emalloc2(kern_h, kernel->row_size); + + xoff = (ssize_t)(kern_w / 2); + yoff = (ssize_t)(kern_h / 2); + + n = colour->width * colour->height * colour->n_chan; + while (eread_frame(colour, clr)) { + /* premultiply */ + if (!no_alpha && !dont_premultiply) { + for (i = 0; i < n; i += 4) { + clr[i + 0] *= clr[i + 3]; + clr[i + 1] *= clr[i + 3]; + clr[i + 2] *= clr[i + 3]; + } + } + + /* apply kernel */ + memset(out, 0, colour->frame_size); + pix = out; + for (y = 0; y < colour->height; y++) { + if ((!y || per_pixel) && !eread_segment(kernel, krn, kern_h * kernel->row_size)) + goto done; + for (x = 0; x < colour->width; x++, pix += colour->n_chan) { + kern = per_pixel ? (krn + x * kern_w * kernel->n_chan) : krn; + for (y2 = 0; y2 < kern_h; y2++, kern += kernel->width * kernel->n_chan) { + cy = (ssize_t)(y + y2) - yoff; + if (cy < 0 || (size_t)cy >= colour->height) { + if (!wrap_y) + continue; + cy %= (ssize_t)(colour->height); + if (cy < 0) + cy += (ssize_t)(colour->height); + } + for (x2 = 0; x2 < kern_w; x2++) { + cx = (ssize_t)(x + x2) - xoff; + if (cx < 0 || (size_t)cx >= colour->width) { + if (!wrap_x) + continue; + cx %= (ssize_t)(colour->width); + if (cx < 0) + cx += (ssize_t)(colour->width); + } + for (i = 0; i < colour->n_chan; i++) + pix[i] += kern[x2 * kernel->n_chan + i] * + clr[((size_t)cy * colour->width + (size_t)cx) * colour->n_chan + i]; + } + } + } + } + + /* unpremultiply */ + if (!dont_premultiply) { + for (i = 0; i < n; i += 4) { + if (out[i + 3]) { + out[i + 0] /= out[i + 3]; + out[i + 1] /= out[i + 3]; + out[i + 2] /= out[i + 3]; + } + } + } + + /* ensure video is opaque if -a was used */ + if (no_alpha) + for (i = 0; i < n; i += 4) + out[i + 3] = 1; + + /* output video */ + ewriteall(STDOUT_FILENO, out, colour->frame_size, "<stdout>"); + } +done: + + free(out); + free(clr); + free(krn); +} + +#endif