URI: 
       Fix blind-kernel and blind-temporal-mean,d add blind-{spatial,temporal}-arithm and blind-spatial-mean, and add support for multiple streams in blind-arithm - blind - suckless command-line video editing utility
  HTML git clone git://git.suckless.org/blind
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 835df7bd1e81852062dd70ce1a054fc9b510e50f
   DIR parent 12e20aecd0ef73523b344d4efc4c54d4174c3880
  HTML Author: Mattias Andrée <maandree@kth.se>
       Date:   Tue, 25 Jul 2017 22:12:23 +0200
       
       Fix blind-kernel and blind-temporal-mean,d add blind-{spatial,temporal}-arithm and blind-spatial-mean, and add support for multiple streams in blind-arithm
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |      13 ++++++-------
         M README                              |      15 +++++++++++++++
         M TODO                                |       3 ---
         M man/blind-apply-kernel.1            |       1 +
         M man/blind-arithm.1                  |      16 +++++++++++++++-
         M man/blind-gauss-blur.1              |       1 +
         A man/blind-kernel.1                  |     116 ++++++++++++++++++++++++++++++
         M man/blind-make-kernel.1             |       3 ++-
         A man/blind-spatial-arithm.1          |      35 +++++++++++++++++++++++++++++++
         A man/blind-spatial-mean.1            |      48 +++++++++++++++++++++++++++++++
         A man/blind-temporal-arithm.1         |      39 +++++++++++++++++++++++++++++++
         A man/blind-temporal-mean.1           |      60 +++++++++++++++++++++++++++++++
         M man/blind.7                         |      15 +++++++++++++++
         M src/blind-arithm.c                  |     115 +++++++++++++++++--------------
         M src/blind-kernel.c                  |      36 ++++++++++++-------------------
         A src/blind-spatial-arithm.c          |      94 +++++++++++++++++++++++++++++++
         A src/blind-spatial-mean.c            |     142 +++++++++++++++++++++++++++++++
         M src/blind-stack.c                   |       1 +
         A src/blind-temporal-arithm.c         |      94 +++++++++++++++++++++++++++++++
         M src/blind-temporal-mean.c           |      63 +++++++++++++++++--------------
         M src/stream.c                        |       2 +-
         M src/util.c                          |       5 +++++
       
       22 files changed, 801 insertions(+), 116 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -43,6 +43,7 @@ BIN =\
                blind-interleave\
                blind-invert-luma\
                blind-invert-matrix\
       +        blind-kernel\
                blind-linear-gradient\
                blind-make-kernel\
                blind-matrix-orthoproject\
       @@ -76,6 +77,8 @@ BIN =\
                blind-sinc-wave\
                blind-sine-wave\
                blind-skip-pattern\
       +        blind-spatial-arithm\
       +        blind-spatial-mean\
                blind-spectrum\
                blind-spiral-gradient\
                blind-split\
       @@ -85,6 +88,8 @@ BIN =\
                blind-square-gradient\
                blind-stack\
                blind-tee\
       +        blind-temporal-arithm\
       +        blind-temporal-mean\
                blind-time-blur\
                blind-triangular-wave\
                blind-to-image\
       @@ -97,13 +102,7 @@ BIN =\
                blind-triangle-tessellation\
                blind-unpremultiply\
                blind-vector-projection\
       -        blind-write-head\
       -        blind-kernel\
       -        blind-temporal-mean
       -
       -# TODO Not tested yet (and doesn't have any manpages):
       -#    blind-kernel
       -#    blind-temporal-mean
       +        blind-write-head
        
        SH_SCRIPTS =\
                blind-rotate-90\
   DIR diff --git a/README b/README
       @@ -126,6 +126,9 @@ UTILITIES
               blind-invert-matrix(1)
                      Invert matrix-video
        
       +       blind-kernel(1)
       +              Create a convolution matrix
       +
               blind-linear-gradient(1)
                      Generate a video with a linear gradient
        
       @@ -240,6 +243,12 @@ UTILITIES
               blind-skip-pattern(1)
                      Skips frames in a video according to pattern
        
       +       blind-spatial-arithm(1)
       +              Perform simple arithmetic over all pixels for each frame in a video
       +
       +       blind-spatial-mean(1)
       +              Calculate the mean over all pixel for each frame in a video
       +
               blind-spectrum(1)
                      Transform a gradient into a spectrum
        
       @@ -267,6 +276,12 @@ UTILITIES
               blind-tee(1)
                      /dev/fd/ aware tee(1) implementation
        
       +       blind-temporal-arithm(1)
       +              Perform simple arithmetic over all frames in a video for each pixel
       +
       +       blind-temporal-mean(1)
       +              Calculate the mean over all frames in a video for each pixel
       +
               blind-time-blur(1)
                      Draw new frames on top of old frames with partial alpha
        
   DIR diff --git a/TODO b/TODO
       @@ -31,7 +31,6 @@ blind-mean                mean of multiple streams
                                https://en.wikipedia.org/wiki/Identric_mean
                                https://en.wikipedia.org/wiki/Logarithmic_mean
                                https://en.wikipedia.org/wiki/Stolarsky_mean
       -blind-temporal-arithm        blind-arithm but over all frames in a video instead of over all streams
        blind-apply-icc                apply ICC profile to video
        blind-convex-gradient        create a gradient in the shape of a convex lens
        blind-concave-gradient        create a gradient in the shape of a concave lens
       @@ -51,8 +50,6 @@ blind-from-video: add options to:
        
        blind-cone-gradient: add ability to make gradient superelliptic
        
       -blind-arithm: add support for multiple streams
       -
        Add [-j jobs] to blind-from-video and blind-to-video.
        
        long double is slightly faster than long.
   DIR diff --git a/man/blind-apply-kernel.1 b/man/blind-apply-kernel.1
       @@ -63,6 +63,7 @@ A frame or row requires 32 bytes per pixel it contains.
        .SH SEE ALSO
        .BR blind (7),
        .BR blind-make-kernel (1),
       +.BR blind-kernel (1),
        .BR blind-gauss-blur (1)
        .SH AUTHORS
        Mattias Andrée
   DIR diff --git a/man/blind-arithm.1 b/man/blind-arithm.1
       @@ -5,7 +5,7 @@ blind-arithm - Perform simple arithmetic on a video
        .B blind-arithm
        [-axyz]
        .I operation
       -.I right-hand-stream
       +.IR right-hand-stream \ ...
        .SH DESCRIPTION
        .B blind-arithm
        reads left-hand operands from stdin, and right-hand
       @@ -27,6 +27,16 @@ If stdin is shorter than
        the remainder of
        .I right-hand-stream
        is ignored but may be partially read.
       +.P
       +IF multiple
       +.I right-hand-stream
       +are specified, they are applied from left to right,
       +with the exception for if        
       +.I operation
       +is
       +.BR exp ,
       +in which case they are applied from right to left with
       +stdin applied last.
        .SH OPERATIONS
        .TP
        .B add
       @@ -86,6 +96,10 @@ Do not modify the Z channel (the third channel).
        .BR blind-cross-product (1),
        .BR blind-quaternion-product (1),
        .BR blind-vector-projection (1),
       +.BR blind-spatial-mean (1),
       +.BR blind-spatial-arithm (1),
       +.BR blind-temporal-mean (1),
       +.BR blind-temporal-arithm (1),
        .BR blind-single-colour (1),
        .BR blind-set-alpha (1),
        .BR blind-set-luma (1),
   DIR diff --git a/man/blind-gauss-blur.1 b/man/blind-gauss-blur.1
       @@ -81,6 +81,7 @@ memory. A frame requires 32 bytes per pixel it contains.
        .BR blind-single-colour (1),
        .BR blind-time-blur (1),
        .BR blind-make-kernel (1),
       +.BR blind-kernel (1),
        .BR blind-apply-kernel (1)
        .SH AUTHORS
        Mattias Andrée
   DIR diff --git a/man/blind-kernel.1 b/man/blind-kernel.1
       @@ -0,0 +1,116 @@
       +.TH BLIND-KERNEL 1 blind
       +.SH NAME
       +blind-kernel - Create a convolution matrix
       +.SH SYNOPSIS
       +.B blind-kernel
       +[-xyza]
       +.I kernel
       +.RI [ parameter ]\ ...
       +.SH DESCRIPTION
       +.B blind-kernel
       +creates a convolution matrix that can be applied to
       +a video using
       +.BR blind-apply-kernel (1).
       +The convolution matrix is created from a set
       +of standard formulae. The formula is selected
       +using the
       +.I kernel
       +argument and is tuned with
       +.IR kernel -specific
       +.IR parameter s.
       +.SH KERNELS
       +.TP
       +.BI kirsch\  direction
       +Create a Kirsch kernel with the specified
       +.IR direction .
       +The
       +.I direction
       +must be
       +.B 1
       +or
       +.BR N ;
       +.BR 2 ,
       +.BR NW ,
       +or
       +.BR WN ;
       +.BR 3
       +or
       +.BR W ;
       +.BR 4 ,
       +.BR SW ,
       +or
       +.BR WS ;
       +.BR 5
       +or
       +.BR S ;
       +.BR 6 ,
       +.BR SE ,
       +or
       +.BR ES ;
       +.BR 7
       +or
       +.BR E ;
       +or
       +.BR 8 ,
       +.BR NE ,
       +or
       +.BR EN .
       +.TP
       +.RI ' \fBbox\ blur\fP '\ [-w\  weight ]\ [ spread \ |\  x-spread \  y-spread ]
       +Creates a box blur kernel. Unless
       +.B -w
       +is used, the kernel is unweighted, otherwise it has the specified
       +.IR weight .
       +The kernel will have the spread 1, the specified
       +.IR spread ,
       +or
       +.I x-spread
       +as the horizontal spread and
       +.I y-spread
       +as the vertical spread.
       +.TP
       +.BR sharpen \ [-i]
       +Creates a sharpen kernel. If
       +.B -i
       +is used, an intensified sharpen kernel is created.
       +.TP
       +.RI \fBgaussian\fP\ [-s\  spread ]\ [-u]\  standard-deviation
       +Creates a Gaussian blur kernel with the standard deviation
       +.IR standard-deviation .
       +If
       +.B -u
       +is used, the a Gaussian unsharpen kernel is created. If
       +.B -s
       +is specified, the specified
       +.I spread
       +will be used, otherwise the spread will be selected automatically.
       +.SH OPTIONS
       +.TP
       +.B -a
       +Apply the values to the alpha channel, set the
       +values for all unselected channels to zero.
       +.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
       +disc. 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-kernel (1),
       +.BR blind-repeat (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
   DIR diff --git a/man/blind-make-kernel.1 b/man/blind-make-kernel.1
       @@ -73,11 +73,12 @@ 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
       +disc. 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-kernel (1),
        .BR blind-repeat (1)
        .SH AUTHORS
        Mattias Andrée
   DIR diff --git a/man/blind-spatial-arithm.1 b/man/blind-spatial-arithm.1
       @@ -0,0 +1,35 @@
       +.TH BLIND-SPATIAL-ARITHM 1 blind
       +.SH NAME
       +blind-spatial-arithm - Perform simple arithmetic over all pixels for each frame in a video
       +.SH SYNOPSIS
       +.B blind-spatial-arithm
       +.I operation
       +.SH DESCRIPTION
       +.B blind-spatial-arithm
       +reads a video from stdin and applies an arithmetic
       +operation over all pixels, for each frame in the
       +video, and prints the resulting video, with one
       +pixel in each frame, to stdout.
       +.SH OPERATIONS
       +.TP
       +.B add
       +Calculate the sum of the operands.
       +.TP
       +.B mul
       +Calculate the product of the operands.
       +.TP
       +.B min
       +Select the lowest operand.
       +.TP
       +.B max
       +Select the highest operand.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-temporal-arithm (1),
       +.BR blind-arithm (1),
       +.BR blind-spatial-mean (1),
       +.BR blind-temporal-mean (1),
       +.BR blind-rewrite-head (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
   DIR diff --git a/man/blind-spatial-mean.1 b/man/blind-spatial-mean.1
       @@ -0,0 +1,48 @@
       +.TH BLIND-SPATIAL-MEAN 1 blind
       +.SH NAME
       +blind-spatial-mean - Calculate the mean over all pixel for each frame in a video
       +.SH SYNOPSIS
       +.B blind-spatial-mean
       +[-g | -h | -l
       +.I power
       +| -p
       +.I power
       +| -v]
       +.SH DESCRIPTION
       +.B blind-spatial-mean
       +reads a video from stdin and calculates the mean
       +over all pixels for frames, and outputs a
       +video with one pixel in each frame, to stdout with
       +the mean for each frame.
       +.P
       +Unless otherwise specified, the arithmetic mean
       +is calculated.
       +.SH OPTIONS
       +.TP
       +.B -g
       +Calculate the geometric mean.
       +.TP
       +.B -h
       +Calculate the harmonic mean.
       +.TP
       +.BR -l \ \fIpower\fP
       +Calculate the Lehmer mean with the specified
       +.IR power .
       +.TP
       +.BR -p \ \fIpower\fP
       +Calculate the power mean (Hölder mean) with
       +the specified
       +.IR power .
       +.TP
       +.B -v
       +Calculate the variance.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-temporal-arithm (1),
       +.BR blind-spatial-arithm (1),
       +.BR blind-arithm (1),
       +.BR blind-temporal-mean (1),
       +.BR blind-rewrite-head (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
   DIR diff --git a/man/blind-temporal-arithm.1 b/man/blind-temporal-arithm.1
       @@ -0,0 +1,39 @@
       +.TH BLIND-TEMPORAL-ARITHM 1 blind
       +.SH NAME
       +blind-temporal-arithm - Perform simple arithmetic over all frames in a video for each pixel
       +.SH SYNOPSIS
       +.B blind-temporal-arithm
       +.I operation
       +.SH DESCRIPTION
       +.B blind-temporal-arithm
       +reads a video from stdin and applies an arithmetic
       +operation over all frames in the video, for each
       +pixel, and prints the resulting single-frame video
       +to stdout.
       +.SH OPERATIONS
       +.TP
       +.B add
       +Calculate the sum of the operands.
       +.TP
       +.B mul
       +Calculate the product of the operands.
       +.TP
       +.B min
       +Select the lowest operand.
       +.TP
       +.B max
       +Select the highest operand.
       +.SH REQUIREMENTS
       +.B blind-temporal-arithm
       +requires enough free memory to load one full frames memory.
       +A frame requires 32 bytes per pixel it contains.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-temporal-mean (1),
       +.BR blind-spatial-arithm (1),
       +.BR blind-spatial-mean (1),
       +.BR blind-arithm (1),
       +.BR blind-rewrite-head (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
   DIR diff --git a/man/blind-temporal-mean.1 b/man/blind-temporal-mean.1
       @@ -0,0 +1,60 @@
       +.TH BLIND-TEMPORAL-MEAN 1 blind
       +.SH NAME
       +blind-temporal-mean - Calculate the mean over all frames in a video for each pixel
       +.SH SYNOPSIS
       +.B blind-temporal-mean
       +[-g | -h | -l
       +.I power
       +| -p
       +.I power
       +| -v]
       +.SH DESCRIPTION
       +.B blind-temporal-mean
       +reads a video from stdin and calculates the mean
       +over all frames for each pixel, and outputs a
       +single frame video to stdout with the mean for
       +each pixel.
       +.P
       +Unless otherwise specified, the arithmetic mean
       +is calculated.
       +.SH OPTIONS
       +.TP
       +.B -g
       +Calculate the geometric mean.
       +.TP
       +.B -h
       +Calculate the harmonic mean.
       +.TP
       +.BR -l \ \fIpower\fP
       +Calculate the Lehmer mean with the specified
       +.IR power .
       +.TP
       +.BR -p \ \fIpower\fP
       +Calculate the power mean (Hölder mean) with
       +the specified
       +.IR power .
       +.TP
       +.B -v
       +Calculate the variance.
       +.SH REQUIREMENTS
       +.B blind-temporal-mean
       +requires enough free memory to load two full frames memory.
       +A frame requires 32 bytes per pixel it contains. If
       +.B -l
       +or
       +.B -v
       +is used, enough free memory to load three full frames
       +memory is required.
       +.P
       +.B blind-temporal-mean
       +is optimised for simplicity rather than memory usage.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-temporal-arithm (1),
       +.BR blind-spatial-arithm (1),
       +.BR blind-arithm (1),
       +.BR blind-spatial-mean (1),
       +.BR blind-rewrite-head (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
   DIR diff --git a/man/blind.7 b/man/blind.7
       @@ -144,6 +144,9 @@ Invert the luminosity of a video
        .BR blind-invert-matrix (1)
        Invert matrix-video
        .TP
       +.BR blind-kernel (1)
       +Create a convolution matrix
       +.TP
        .BR blind-linear-gradient (1)
        Generate a video with a linear gradient
        .TP
       @@ -255,6 +258,12 @@ Apply sine-wave repetition to gradient
        .BR blind-skip-pattern (1)
        Skips frames in a video according to pattern
        .TP
       +.BR blind-spatial-arithm (1)
       +Perform simple arithmetic over all pixels for each frame in a video
       +.TP
       +.BR blind-spatial-mean (1)
       +Calculate the mean over all pixel for each frame in a video
       +.TP
        .BR blind-spectrum (1)
        Transform a gradient into a spectrum
        .TP
       @@ -284,6 +293,12 @@ Overlay videos
        .BR tee (1)
        implementation
        .TP
       +.BR blind-temporal-arithm (1)
       +Perform simple arithmetic over all frames in a video for each pixel
       +.TP
       +.BR blind-temporal-mean (1)
       +Calculate the mean over all frames in a video for each pixel
       +.TP
        .BR blind-time-blur (1)
        Draw new frames on top of old frames with partial alpha
        .TP
   DIR diff --git a/src/blind-arithm.c b/src/blind-arithm.c
       @@ -1,67 +1,71 @@
        /* See LICENSE file for copyright and license details. */
        #include "common.h"
        
       -USAGE("[-axyz] operation right-hand-stream")
       +USAGE("[-axyz] operation right-hand-stream ...")
        
       -static int skip_a = 0;
       -static int skip_x = 0;
       -static int skip_y = 0;
       -static int skip_z = 0;
       +static int skip_ch[4] = {0, 0, 0, 0};
        
        /* Because the syntax for a function returning a function pointer is disgusting. */
       -typedef void (*process_func)(struct stream *left, struct stream *right, size_t n);
       +typedef void (*process_func)(struct stream *streams, size_t n_streams, size_t n);
        
        #define LIST_OPERATORS(PIXFMT, TYPE)\
       -        X(add, *lh += rh,                  PIXFMT, TYPE)\
       -        X(sub, *lh -= rh,                  PIXFMT, TYPE)\
       -        X(mul, *lh *= rh,                  PIXFMT, TYPE)\
       -        X(div, *lh /= rh,                  PIXFMT, TYPE)\
       -        X(mod, *lh = posmod(*lh, rh),      PIXFMT, TYPE)\
       -        X(exp, *lh = pow(*lh, rh),         PIXFMT, TYPE)\
       -        X(log, *lh = log2(*lh) / log2(rh), PIXFMT, TYPE)\
       -        X(min, *lh = MIN(*lh, rh),         PIXFMT, TYPE)\
       -        X(max, *lh = MAX(*lh, rh),         PIXFMT, TYPE)\
       -        X(abs, *lh = abs(*lh - rh) + rh,   PIXFMT, TYPE)
       +        X(add, 0, *lh += rh,                  PIXFMT, TYPE)\
       +        X(sub, 0, *lh -= rh,                  PIXFMT, TYPE)\
       +        X(mul, 0, *lh *= rh,                  PIXFMT, TYPE)\
       +        X(div, 0, *lh /= rh,                  PIXFMT, TYPE)\
       +        X(mod, 0, *lh = posmod(*lh, rh),      PIXFMT, TYPE)\
       +        X(exp, 1, *lh = pow(*lh, rh),         PIXFMT, TYPE)\
       +        X(log, 0, *lh = log2(*lh) / log2(rh), PIXFMT, TYPE)\
       +        X(min, 0, *lh = MIN(*lh, rh),         PIXFMT, TYPE)\
       +        X(max, 0, *lh = MAX(*lh, rh),         PIXFMT, TYPE)\
       +        X(abs, 0, *lh = abs(*lh - rh) + rh,   PIXFMT, TYPE)
        
       -#define C(CH, CHI, ALGO, TYPE)\
       -        (!skip_##CH ? ((lh = ((TYPE *)(left->buf + i)) + (CHI),\
       -                        rh = ((TYPE *)(right->buf + i))[CHI],\
       -                        (ALGO)), 0) : 0)
       +#define P(L, R, ALGO, TYPE)\
       +        (lh = (TYPE *)(streams[L].buf + k),\
       +         rh = *((TYPE *)(streams[R].buf + k)),\
       +         (ALGO))
        
       -#define X(NAME, ALGO, PIXFMT, TYPE)\
       +#define X(NAME, RTL, ALGO, PIXFMT, TYPE)\
                static void\
       -        process_##PIXFMT##_##NAME(struct stream *left, struct stream *right, size_t n)\
       +        process_##PIXFMT##_##NAME(struct stream *streams, size_t n_streams, size_t n)\
                {\
       -                size_t i;\
       +                size_t i, j, k;\
                        TYPE *lh, rh;\
       -                for (i = 0; i < n; i += 4 * sizeof(TYPE)) {\
       -                        C(x, 0, ALGO, TYPE);\
       -                        C(y, 1, ALGO, TYPE);\
       -                        C(z, 2, ALGO, TYPE);\
       -                        C(a, 3, ALGO, TYPE);\
       +                if (RTL) {\
       +                        for (i = 0; i < streams->n_chan; i++)\
       +                                if (!skip_ch[i])\
       +                                        for (j = n_streams; --j;)\
       +                                                for (k = i * sizeof(TYPE); k < n; k += 4 * sizeof(TYPE))\
       +                                                        P(j - 1, j, ALGO, TYPE);\
       +                } else {\
       +                        for (i = 0; i < streams->n_chan; i++)\
       +                                if (!skip_ch[i])\
       +                                        for (j = 1; j < n_streams; j++)\
       +                                                for (k = i * sizeof(TYPE); k < n; k += 4 * sizeof(TYPE))\
       +                                                        P(0, j, ALGO, TYPE);\
                        }\
                }
       -LIST_OPERATORS(xyza, double)
       -LIST_OPERATORS(xyzaf, float)
       +LIST_OPERATORS(lf, double)
       +LIST_OPERATORS(f, float)
        #undef X
        
        static process_func
       -get_process_xyza(const char *operation)
       +get_process_lf(const char *operation)
        {
       -#define X(NAME, ALGO, PIXFMT, TYPE)\
       +#define X(NAME, _RTL, _ALGO, PIXFMT, _TYPE)\
                if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       -        LIST_OPERATORS(xyza, double)
       +        LIST_OPERATORS(lf, double)
        #undef X
                eprintf("algorithm not recognised: %s\n", operation);
                return NULL;
        }
        
        static process_func
       -get_process_xyzaf(const char *operation)
       +get_process_f(const char *operation)
        {
       -#define X(NAME, ALGO, PIXFMT, TYPE)\
       +#define X(NAME, _RTL, _ALGO, PIXFMT, _TYPE)\
                if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       -        LIST_OPERATORS(xyzaf, float)
       +        LIST_OPERATORS(f, float)
        #undef X
                eprintf("algorithm not recognised: %s\n", operation);
                return NULL;
       @@ -70,41 +74,46 @@ get_process_xyzaf(const char *operation)
        int
        main(int argc, char *argv[])
        {
       -        struct stream left, right;
       +        struct stream *streams;
                process_func process;
       +        const char *operation;
       +        size_t frames = SIZE_MAX, tmp;
       +        int i;
        
                ARGBEGIN {
                case 'a':
       -                skip_a = 1;
       +                skip_ch[3] = 1;
                        break;
                case 'x':
       -                skip_x = 1;
       -                break;
                case 'y':
       -                skip_y = 1;
       -                break;
                case 'z':
       -                skip_z = 1;
       +                skip_ch[ARGC() - 'x'] = 1;
                        break;
                default:
                        usage();
                } ARGEND;
        
       -        if (argc != 2)
       +        if (argc < 2)
                        usage();
        
       -        eopen_stream(&left, NULL);
       -        eopen_stream(&right, argv[1]);
       +        operation = *argv;
       +        streams = alloca((size_t)argc * sizeof(*streams));
       +        *argv = NULL;
       +        for (i = 0; i < argc; i++) {
       +                eopen_stream(streams + i, argv[i]);
       +                if (streams[i].frames && streams[i].frames < frames)
       +                        frames = streams[i].frames;
       +        }
        
       -        if (!strcmp(left.pixfmt, "xyza"))
       -                process = get_process_xyza(argv[0]);
       -        else if (!strcmp(left.pixfmt, "xyza f"))
       -                process = get_process_xyzaf(argv[0]);
       +        if (streams->encoding == DOUBLE)
       +                process = get_process_lf(operation);
                else
       -                eprintf("pixel format %s is not supported, try xyza\n", left.pixfmt);
       +                process = get_process_f(operation);
        
       -        fprint_stream_head(stdout, &left);
       +        tmp = streams->frames, streams->frames = frames;
       +        fprint_stream_head(stdout, streams);
                efflush(stdout, "<stdout>");
       -        process_two_streams(&left, &right, STDOUT_FILENO, "<stdout>", process);
       +        streams->frames = tmp;
       +        process_multiple_streams(streams, (size_t)argc, STDOUT_FILENO, "<stdout>", 1, process);
                return 0;
        }
   DIR diff --git a/src/blind-kernel.c b/src/blind-kernel.c
       @@ -3,7 +3,7 @@
        
        USAGE("[-xyza] kernel [parameter] ...")
        
       -#define SUBUSAGE(FORMAT)          "usage: %s [-xyza] " FORMAT, argv0
       +#define SUBUSAGE(FORMAT)          "usage: %s [-xyza] " FORMAT "\n", argv0
        #define STRCASEEQ3(A, B1, B2, B3) (!strcasecmp(A, B1) || !strcasecmp(A, B2) || !strcasecmp(A, B3))
        
        #define LIST_KERNELS\
       @@ -37,7 +37,7 @@ kernel_kirsch(int argc, char *argv[], size_t *rows, size_t *cols, double **free_
                if (STRCASEEQ3(argv[0], "6", "SE", "ES")) return matrices[5];
                if (STRCASEEQ3(argv[0], "7", "E",  "E"))  return matrices[6];
                if (STRCASEEQ3(argv[0], "8", "NE", "EN")) return matrices[7];
       -        eprintf("Unrecognised direction: %s\n", argv[0]);
       +        eprintf("unrecognised direction: %s\n", argv[0]);
                return NULL;
        }
        
       @@ -129,6 +129,7 @@ static const double *
        kernel_gaussian(int argc, char *argv[], size_t *rows, size_t *cols, double **free_this)
        {
                size_t spread = 0, y, x;
       +        ssize_t xx, yy;
                int unsharpen = 0;
                double sigma, value, c, k;
                char *arg;
       @@ -160,29 +161,17 @@ kernel_gaussian(int argc, char *argv[], size_t *rows, size_t *cols, double **fre
        
                *free_this = emalloc3(*rows, *cols, sizeof(double));
        
       -        k = sigma * sigma * 2;
       +        k = sigma * sigma * 2.;
                c = M_PI * k;
       -        c = sqrt(c);
                c = 1.0 / c;
                k = 1.0 / -k;
       -
       -        for (x = 0; x <= spread; x++) {
       -                value = (double)(spread - x);
       -                value *= value * k;
       -                value = exp(value) * c;
       -                for (y = 0; y < *rows; y++) {
       +        for (y = 0; y < 2 * spread + 1; y++) {
       +                yy = (ssize_t)spread - (ssize_t)y, yy *= yy;
       +                for (x = 0; x < 2 * spread + 1; x++) {
       +                        xx = (ssize_t)spread - (ssize_t)x, xx *= xx;
       +                        value = (double)(xx + yy) * k;
       +                        value = exp(value) * c;
                                (*free_this)[y * *cols + x] = value;
       -                        (*free_this)[y + 1 * *cols + *cols - 1 - x] = value;
       -                }
       -        }
       -
       -        for (y = 0; y <= spread; y++) {
       -                value = (double)(spread - y);
       -                value *= value * k;
       -                value = exp(value) * c;
       -                for (x = 0; x < *cols; x++) {
       -                        (*free_this)[y * *cols + x] *= value;
       -                        (*free_this)[y + 1 * *cols + *cols - 1 - x] *= value;
                        }
                }
        
       @@ -235,13 +224,16 @@ main(int argc, char *argv[])
                        usage();
                } ARGEND;
        
       +        if (!argc)
       +                usage();
       +
                if (null_x && null_y && null_z && null_a)
                        null_x = null_y = null_z = null_a = 0;
        
                if (0);
        #define X(FUNC, NAME)\
                else if (!strcmp(argv[0], NAME))\
       -                kernel = FUNC(argc, argv + 1, &rows, &cols, &free_this);
       +                kernel = FUNC(argc - 1, argv + 1, &rows, &cols, &free_this);
                LIST_KERNELS
        #undef X
                else
   DIR diff --git a/src/blind-spatial-arithm.c b/src/blind-spatial-arithm.c
       @@ -0,0 +1,94 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("operation")
       +
       +/* Because the syntax for a function returning a function pointer is disgusting. */
       +typedef void (*process_func)(struct stream *stream);
       +
       +#define LIST_OPERATORS(PIXFMT, TYPE)\
       +        X(add, img[j & 3] + *buf,     PIXFMT, TYPE)\
       +        X(mul, img[j & 3] * *buf,     PIXFMT, TYPE)\
       +        X(min, MIN(img[j & 3], *buf), PIXFMT, TYPE)\
       +        X(max, MAX(img[j & 3], *buf), PIXFMT, TYPE)
       +
       +#define X(NAME, ALGO, PIXFMT, TYPE)\
       +        static void\
       +        process_##PIXFMT##_##NAME(struct stream *stream)\
       +        {\
       +                TYPE img[4], *buf;\
       +                size_t i, n, j = 0, m = stream->frame_size / sizeof(*img);\
       +                int first = 1;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size * stream->n_chan;\
       +                        buf = (TYPE *)(stream->buf);\
       +                        for (i = 0; i < n; i++, buf++, j++, j %= m) {\
       +                                if (!j) {\
       +                                        if (!first)\
       +                                                ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
       +                                        first = 0;\
       +                                        img[0] = *buf++;\
       +                                        img[1] = *buf++;\
       +                                        img[2] = *buf++;\
       +                                        img[3] = *buf;\
       +                                        i += 3;\
       +                                        j = 3;\
       +                                } else {\
       +                                        img[j & 3] = ALGO;\
       +                                }\
       +                        }\
       +                        n *= sizeof(TYPE);\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (!first)\
       +                        ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
       +        }
       +LIST_OPERATORS(lf, double)
       +LIST_OPERATORS(f, float)
       +#undef X
       +
       +static process_func
       +get_process_lf(const char *operation)
       +{
       +#define X(NAME, _ALGO, PIXFMT, TYPE)\
       +        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       +        LIST_OPERATORS(lf, double)
       +#undef X
       +        eprintf("algorithm not recognised: %s\n", operation);
       +        return NULL;
       +}
       +
       +static process_func
       +get_process_f(const char *operation)
       +{
       +#define X(NAME, _ALGO, PIXFMT, TYPE)\
       +        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       +        LIST_OPERATORS(f, float)
       +#undef X
       +        eprintf("algorithm not recognised: %s\n", operation);
       +        return NULL;
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        process_func process;
       +
       +        UNOFLAGS(argc != 1);
       +
       +        eopen_stream(&stream, NULL);
       +        echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
       +
       +        if (stream.encoding == DOUBLE)
       +                process = get_process_lf(argv[0]);
       +        else
       +                process = get_process_f(argv[0]);
       +
       +        if (DPRINTF_HEAD(STDOUT_FILENO, stream.frames, 1, 1, stream.pixfmt) < 0)
       +                eprintf("dprintf:");
       +        process(&stream);
       +        if (stream.ptr)
       +                eprintf("%s: incomplete frame\n", stream.file);
       +        return 0;
       +}
   DIR diff --git a/src/blind-spatial-mean.c b/src/blind-spatial-mean.c
       @@ -0,0 +1,142 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-g | -h | -l power | -p power | -v]")
       +/* TODO add [-w weight-stream] for -l */
       +
       +/* Because the syntax for a function returning a function pointer is disgusting. */
       +typedef void (*process_func)(struct stream *stream);
       +
       +/*
       + * X-parameter 1: method enum value
       + * X-parameter 2: identifier-friendly name
       + * X-parameter 3: initial assignments
       + * X-parameter 4: initial value
       + * X-parameter 5: subcell processing
       + * X-parameter 6: subcell finalisation
       + */
       +#define LIST_MEANS(TYPE)\
       +        /* [default] arithmetic mean */\
       +        X(ARITHMETIC, arithmetic,, 0, img[j & 3] += *buf, img[j & 3] /= pixels)\
       +        /* geometric mean */\
       +        X(GEOMETRIC, geometric,, 1, img[j & 3] *= *buf, img[j & 3] = nnpow(img[j & 3], 1 / pixels))\
       +        /* harmonic mean */\
       +        X(HARMONIC, harmonic,, 0, img[j & 3] += (TYPE)1 / *buf, img[j & 3] = pixels / img[j & 3])\
       +        /* Lehmer mean */\
       +        X(LEHMER, lehmer, (a = (TYPE)power, b = a - (TYPE)1), 0,\
       +          (img[j & 3] += nnpow(*buf, a), aux[j & 3] += nnpow(*buf, b)), img[j & 3] /= aux[j & 3])\
       +        /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for cubic mean) */\
       +        X(POWER, power, a = (TYPE)power, 0, img[j & 3] += nnpow(*buf, a),\
       +          img[j & 3] = nnpow(img[j & 3], (TYPE)(1. / power)) / pixels)\
       +        /* variance */\
       +        X(VARIANCE, variance,, 0, (img[j & 3] += *buf * *buf, aux[j & 3] += *buf),\
       +          img[j & 3] = (img[j & 3] - aux[j & 3] * aux[j & 3] / pixels) / pixels)
       +
       +#define X(V, ...) V,
       +enum method { LIST_MEANS() };
       +#undef X
       +
       +static double power;
       +
       +#define MAKE_PROCESS(PIXFMT, TYPE,\
       +                     _1, NAME, INIT, INITIAL, PROCESS_SUBCELL, FINALISE_SUBCELL)\
       +        static void\
       +        process_##PIXFMT##_##NAME(struct stream *stream)\
       +        {\
       +                TYPE img[4], aux[4], *buf, a, b;\
       +                TYPE pixels = (TYPE)(stream->frame_size / sizeof(img));\
       +                size_t i, n, j = 0, m = stream->frame_size / sizeof(*img);\
       +                int first = 1;\
       +                INIT;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size * stream->n_chan;\
       +                        buf = (TYPE *)(stream->buf);\
       +                        for (i = 0; i < n; i++, buf++, j++, j %= m) {\
       +                                if (!j) {\
       +                                        if (!first) {\
       +                                                for (j = 0; j < ELEMENTSOF(img); j++)\
       +                                                        FINALISE_SUBCELL;\
       +                                                j = 0;\
       +                                                ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
       +                                        }\
       +                                        first = 0;\
       +                                        img[0] = aux[0] = INITIAL;\
       +                                        img[1] = aux[1] = INITIAL;\
       +                                        img[2] = aux[2] = INITIAL;\
       +                                        img[3] = aux[3] = INITIAL;\
       +                                }\
       +                                PROCESS_SUBCELL;\
       +                        }\
       +                        n *= sizeof(TYPE);\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (!first) {\
       +                        for (j = 0; j < ELEMENTSOF(img); j++)\
       +                                FINALISE_SUBCELL;\
       +                        ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
       +                }\
       +                (void) aux, (void) a, (void) b, (void) pixels;\
       +        }
       +#define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
       +LIST_MEANS(double)
       +#undef X
       +#define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
       +LIST_MEANS(float)
       +#undef X
       +#undef MAKE_PROCESS
       +
       +#define X(ID, NAME, ...) [ID] = process_lf_##NAME,
       +static const process_func process_functions_lf[] = { LIST_MEANS() };
       +#undef X
       +
       +#define X(ID, NAME, ...) [ID] = process_f_##NAME,
       +static const process_func process_functions_f[] = { LIST_MEANS() };
       +#undef X
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        process_func process;
       +        enum method method = ARITHMETIC;
       +
       +        ARGBEGIN {
       +        case 'g':
       +                method = GEOMETRIC;
       +                break;
       +        case 'h':
       +                method = HARMONIC;
       +                break;
       +        case 'l':
       +                method = LEHMER;
       +                power = etolf_flag('l', UARGF());
       +                break;
       +        case 'p':
       +                method = POWER;
       +                power = etolf_flag('p', UARGF());
       +                break;
       +        case 'v':
       +                method = VARIANCE;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.encoding == DOUBLE)
       +                process = process_functions_lf[method];
       +        else
       +                process = process_functions_f[method];
       +
       +
       +        if (DPRINTF_HEAD(STDOUT_FILENO, stream.frames, 1, 1, stream.pixfmt) < 0)
       +                eprintf("dprintf:");
       +        process(&stream);
       +        if (stream.ptr)
       +                eprintf("%s: incomplete frame\n", stream.file);
       +        return 0;
       +}
   DIR diff --git a/src/blind-stack.c b/src/blind-stack.c
       @@ -52,6 +52,7 @@ main(int argc, char *argv[])
                        break;
                case 's':
                        shortest = 1;
       +                frames = SIZE_MAX;
                        break;
                default:
                        usage();
   DIR diff --git a/src/blind-temporal-arithm.c b/src/blind-temporal-arithm.c
       @@ -0,0 +1,94 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("operation")
       +
       +/* Because the syntax for a function returning a function pointer is disgusting. */
       +typedef void (*process_func)(struct stream *stream, void *image);
       +
       +#define LIST_OPERATORS(PIXFMT, TYPE)\
       +        X(add, *img + *buf,     PIXFMT, TYPE)\
       +        X(mul, *img * *buf,     PIXFMT, TYPE)\
       +        X(min, MIN(*img, *buf), PIXFMT, TYPE)\
       +        X(max, MAX(*img, *buf), PIXFMT, TYPE)
       +
       +#define X(NAME, ALGO, PIXFMT, TYPE)\
       +        static void\
       +        process_##PIXFMT##_##NAME(struct stream *stream, void *image)\
       +        {\
       +                TYPE *buf, *img = image;\
       +                size_t i, n, j = 0, m = stream->frame_size / sizeof(TYPE);\
       +                do {\
       +                        n = stream->ptr / sizeof(TYPE);\
       +                        buf = (TYPE *)(stream->buf);\
       +                        for (i = 0; i < n; i++, buf++) {\
       +                                *img = ALGO;\
       +                                if (++j == m) {\
       +                                        j = 0;\
       +                                        img = image;\
       +                                } else {\
       +                                        img++;\
       +                                }\
       +                        }\
       +                        n *= sizeof(TYPE);\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +        }
       +LIST_OPERATORS(lf, double)
       +LIST_OPERATORS(f, float)
       +#undef X
       +
       +static process_func
       +get_process_lf(const char *operation)
       +{
       +#define X(NAME, _ALGO, PIXFMT, TYPE)\
       +        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       +        LIST_OPERATORS(lf, double)
       +#undef X
       +        eprintf("algorithm not recognised: %s\n", operation);
       +        return NULL;
       +}
       +
       +static process_func
       +get_process_f(const char *operation)
       +{
       +#define X(NAME, _ALGO, PIXFMT, TYPE)\
       +        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
       +        LIST_OPERATORS(f, float)
       +#undef X
       +        eprintf("algorithm not recognised: %s\n", operation);
       +        return NULL;
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        process_func process;
       +        char *img;
       +
       +        UNOFLAGS(argc != 1);
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.encoding == DOUBLE)
       +                process = get_process_lf(argv[0]);
       +        else
       +                process = get_process_f(argv[0]);
       +
       +        echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
       +        img = emalloc(stream.frame_size);
       +        if (!eread_frame(&stream, img))
       +                eprintf("video has no frames\n");
       +
       +        process(&stream, img);
       +        if (stream.ptr)
       +                eprintf("%s: incomplete frame\n", stream.file);
       +
       +        stream.frames = 1;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
       +        free(img);
       +        return 0;
       +}
   DIR diff --git a/src/blind-temporal-mean.c b/src/blind-temporal-mean.c
       @@ -1,8 +1,8 @@
        /* See LICENSE file for copyright and license details. */
        #include "common.h"
        
       -USAGE("[-g | -h | -l power | -p power]")
       -/* TODO add -w weight-stream */
       +USAGE("[-g | -h | -l power | -p power | -v]")
       +/* TODO add [-w weight-stream] for -l */
        
        /* Because the syntax for a function returning a function pointer is disgusting. */
        typedef void (*process_func)(struct stream *stream, void *buffer, void *image, size_t frame);
       @@ -20,20 +20,23 @@ typedef void (*process_func)(struct stream *stream, void *buffer, void *image, s
        #define LIST_MEANS(TYPE)\
                /* [default] arithmetic mean */\
                X(ARITHMETIC, arithmetic, 1, COPY_FRAME,, *img1 += *buf,\
       -          a = (TYPE)1.0 / (TYPE)frame, *img1 *= a)\
       +          a = (TYPE)1 / (TYPE)frame, *img1 *= a)\
                /* geometric mean */\
                X(GEOMETRIC, geometric, 1, COPY_FRAME,, *img1 *= *buf,\
       -          a = (TYPE)1.0 / (TYPE)frame, *img1 = nnpow(*img1, a))\
       +          a = (TYPE)1 / (TYPE)frame, *img1 = nnpow(*img1, a))\
                /* harmonic mean */\
                X(HARMONIC, harmonic, 1, ZERO_AND_PROCESS_FRAME,, *img1 += (TYPE)1 / *buf,\
                  a = (TYPE)frame, *img1 = a / *img1)\
       -        /* lehmer mean */\
       +        /* Lehmer mean */\
                X(LEHMER, lehmer, 2, ZERO_AND_PROCESS_FRAME, (a = (TYPE)power, b = a - (TYPE)1),\
                  (*img1 += nnpow(*buf, a), *img2 += nnpow(*buf, b)),, *img1 /= *img2)\
                /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for cubic mean) */\
                X(POWER, power, 1, ZERO_AND_PROCESS_FRAME, a = (TYPE)power,\
       -          *img1 += nnpow(*buf, a), (a = (TYPE)1 / (TYPE)frame, b = (TYPE)(1.0 / power)),\
       -          *img1 = a * nnpow(*img1, b))
       +          *img1 += nnpow(*buf, a), (a = (TYPE)1 / (TYPE)frame, b = (TYPE)(1. / power)), \
       +          *img1 = a * nnpow(*img1, b))\
       +        /* variance */\
       +        X(VARIANCE, variance, 2, ZERO_AND_PROCESS_FRAME,, (*img1 += *buf * *buf, *img2 += *buf),\
       +          a = (TYPE)1 / (TYPE)frame, *img1 = (*img1 - *img2 * *img2 * a) * a)
        
        enum first_frame_action {
                COPY_FRAME,
       @@ -54,34 +57,37 @@ static double power;
                {\
                        TYPE *buf = buffer, *img1 = image, a, b;\
                        TYPE *img2 = (TYPE *)(((char *)image) + stream->frame_size);\
       -                size_t x, y;\
       -                if (!stream) {\
       +                size_t x, y, z;\
       +                if (!buf) {\
                                PRE_FINALISE;\
       -                        for (y = 0; y < stream->height; y++)\
       -                                for (x = 0; x < stream->width; x++, img1++, img2++, buf++)\
       -                                        FINALISE_SUBCELL;\
       +                        for (z = 0; z < stream->n_chan; z++)\
       +                                for (y = 0; y < stream->height; y++)\
       +                                        for (x = 0; x < stream->width; x++, img1++, img2++)\
       +                                                FINALISE_SUBCELL;\
                        } else {\
                                PRE_PROCESS;\
       -                        for (y = 0; y < stream->height; y++)\
       -                                for (x = 0; x < stream->width; x++, img1++, img2++, buf++)\
       -                                        PROCESS_SUBCELL;\
       +                        for (z = 0; z < stream->n_chan; z++)\
       +                                for (y = 0; y < stream->height; y++)\
       +                                        for (x = 0; x < stream->width; x++, img1++, img2++, buf++) {\
       +                                                PROCESS_SUBCELL;\
       +                                        }\
                        }\
                        (void) img2, (void) a, (void) b, (void) frame;\
                }
       -#define X(...) MAKE_PROCESS(xyza, double, __VA_ARGS__)
       +#define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
        LIST_MEANS(double)
        #undef X
       -#define X(...) MAKE_PROCESS(xyzaf, float, __VA_ARGS__)
       +#define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
        LIST_MEANS(float)
        #undef X
        #undef MAKE_PROCESS
        
       -#define X(ID, NAME, ...) [ID] = process_xyza_##NAME,
       -static const process_func process_functions_xyza[] = { LIST_MEANS() };
       +#define X(ID, NAME, ...) [ID] = process_lf_##NAME,
       +static const process_func process_functions_lf[] = { LIST_MEANS() };
        #undef X
        
       -#define X(ID, NAME, ...) [ID] = process_xyzaf_##NAME,
       -static const process_func process_functions_xyzaf[] = { LIST_MEANS() };
       +#define X(ID, NAME, ...) [ID] = process_f_##NAME,
       +static const process_func process_functions_f[] = { LIST_MEANS() };
        #undef X
        
        int
       @@ -109,6 +115,9 @@ main(int argc, char *argv[])
                        method = POWER;
                        power = etolf_flag('p', UARGF());
                        break;
       +        case 'v':
       +                method = VARIANCE;
       +                break;
                default:
                        usage();
                } ARGEND;
       @@ -130,12 +139,10 @@ main(int argc, char *argv[])
        
                eopen_stream(&stream, NULL);
        
       -        if (!strcmp(stream.pixfmt, "xyza"))
       -                process = process_functions_xyza[method];
       -        else if (!strcmp(stream.pixfmt, "xyza f"))
       -                process = process_functions_xyzaf[method];
       +        if (stream.encoding == DOUBLE)
       +                process = process_functions_lf[method];
                else
       -                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +                process = process_functions_f[method];
        
                stream.frames = 1;
                echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
       @@ -149,14 +156,14 @@ main(int argc, char *argv[])
        
                frames = 0;
                if (first_frame_action == COPY_FRAME) {
       -                if (!eread_frame(&stream, buf))
       +                if (!eread_frame(&stream, img))
                                eprintf("video is no frames\n");
                        frames++;
                }
                for (; eread_frame(&stream, buf); frames++)
                        process(&stream, buf, img, frames);
                if (!frames)
       -                eprintf("video is no frames\n");
       +                eprintf("video has no frames\n");
                process(&stream, NULL, img, frames);
        
                ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
   DIR diff --git a/src/stream.c b/src/stream.c
       @@ -487,7 +487,7 @@ nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, 
                while (n_streams) {
                        n = SIZE_MAX;
                        for (i = 0; i < n_streams; i++) {
       -                        if (streams[i].ptr < sizeof(streams->buf) && !enread_stream(status, streams + i, SIZE_MAX)) {
       +                        if (streams[i].ptr < streams->pixel_size && !enread_stream(status, streams + i, SIZE_MAX)) {
                                        close(streams[i].fd);
                                        streams[i].fd = -1;
                                        if (shortest)
   DIR diff --git a/src/util.c b/src/util.c
       @@ -32,6 +32,11 @@ tollu(const char *s, unsigned long long int min, unsigned long long int max, uns
                        errno = ERANGE;
                        return -1;
                }
       +        if (!isdigit(s[*s == 'x' || *s == 'X' || *s == '#']) ||
       +            (*s == '0' && !isdigit(s[1 + (*s == 'x' || *s == 'o' || *s == 'b')]))) {
       +                errno = EINVAL;
       +                return -1;
       +        }
                if (tolower(*s) == 'x' || *s == '#')
                        *out = strtoull(s + 1, &end, 16);
                else if (*s == '0' && tolower(s[1]) == 'x')