Add blind-apply-kernel - 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
       ---
 (DIR) commit ebe2e88b44f46e59bdefef1eb585078d5fa6d4d4
 (DIR) parent 28635cabc72a60674c18c0e160fefdbd4af41734
 (HTM) 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:
         M Makefile                            |       1 +
         M README                              |       6 ++++++
         M TODO                                |       1 -
         A man/blind-apply-kernel.1            |      69 ++++++++++++++++++++++++++++++
         M man/blind-gauss-blur.1              |       4 +++-
         M man/blind.7                         |       6 ++++++
         M src/blind-affine-colour.c           |       2 ++
         A src/blind-apply-kernel.c            |     159 +++++++++++++++++++++++++++++++
       
       8 files changed, 246 insertions(+), 2 deletions(-)
       ---
 (DIR) 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\
 (DIR) 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
        
 (DIR) 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)
 (DIR) 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 >
 (DIR) 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 >
 (DIR) 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
 (DIR) 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
 (DIR) 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