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:
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')