blind

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

commit 835df7bd1e81852062dd70ce1a054fc9b510e50f
parent 12e20aecd0ef73523b344d4efc4c54d4174c3880
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:
Makefile | 13++++++-------
README | 15+++++++++++++++
TODO | 3---
man/blind-apply-kernel.1 | 1+
man/blind-arithm.1 | 16+++++++++++++++-
man/blind-gauss-blur.1 | 1+
man/blind-kernel.1 | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/blind-make-kernel.1 | 3++-
man/blind-spatial-arithm.1 | 35+++++++++++++++++++++++++++++++++++
man/blind-spatial-mean.1 | 48++++++++++++++++++++++++++++++++++++++++++++++++
man/blind-temporal-arithm.1 | 39+++++++++++++++++++++++++++++++++++++++
man/blind-temporal-mean.1 | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/blind.7 | 15+++++++++++++++
src/blind-arithm.c | 115+++++++++++++++++++++++++++++++++++++++++++------------------------------------
src/blind-kernel.c | 36++++++++++++++----------------------
src/blind-spatial-arithm.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/blind-spatial-mean.c | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/blind-stack.c | 1+
src/blind-temporal-arithm.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/blind-temporal-mean.c | 63+++++++++++++++++++++++++++++++++++----------------------------
src/stream.c | 2+-
src/util.c | 5+++++
22 files changed, 801 insertions(+), 116 deletions(-)

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\ 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 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. 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 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), 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 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 > 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 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 > 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 > 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 > 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 > 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 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; } 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 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; +} 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; +} 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(); 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; +} 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>"); 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) 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')