commit d3cd95e055f50df97979bcca7bb26a3edd4b82ee
parent 45c25008cbea5241fb711dc7a9ba21ec631bce64
Author: Mattias Andrée <maandree@kth.se>
Date:   Sun, 14 May 2017 20:13:16 +0200
Add blind-tempral-mean
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
3 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -21,7 +21,6 @@ BIN =\
 	blind-from-video\
 	blind-gauss-blur\
 	blind-invert-luma\
-	blind-kernel\
 	blind-make-kernel\
 	blind-next-frame\
 	blind-read-head\
@@ -44,6 +43,10 @@ BIN =\
 	blind-transpose\
 	blind-write-head
 
+# TODO Not tested yet (and doesn't have any manpages):
+#    blind-kernel
+#    blind-temporal-mean
+
 SCRIPTS =\
 	blind-rotate-90\
 	blind-rotate-180\
diff --git a/TODO b/TODO
@@ -32,6 +32,14 @@ blind-roberts-cross	https://en.wikipedia.org/wiki/Roberts_cross
 ---			https://en.wikipedia.org/wiki/Canny_edge_detector
 ---			https://en.wikipedia.org/wiki/Deriche_edge_detector
 ---			https://en.wikipedia.org/wiki/Edge_detection
+blind-mean		mean of multiple streams
+			means from blind-temporal-mean
+			https://en.wikipedia.org/wiki/Heinz_mean
+			https://en.wikipedia.org/wiki/Heronian_mean
+			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-from-video: add options to:
 	* just run ffmpeg just print the output
@@ -41,6 +49,8 @@ blind-from-video: add options to:
 	  print to stdout (up to user to direct to /dev/null
 	  for discarding)
 
+blind-arithm: add support for multiple streams
+
 Add [-j jobs] to blind-from-video and blind-to-video.
 
 Add -f (framewise) to blind-repeat
diff --git a/src/blind-temporal-mean.c b/src/blind-temporal-mean.c
@@ -0,0 +1,186 @@
+/* See LICENSE file for copyright and license details. */
+#include "stream.h"
+#include "util.h"
+
+#include <math.h>
+#include <string.h>
+
+USAGE("[-g | -h | -l power | -p power]")
+/* TODO add -w weight-stream */
+
+/* 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);
+
+/*
+ * X-parameter 1: method enum value
+ * X-parameter 2: identifier-friendly name
+ * X-parameter 3: images
+ * X-parameter 4: action for first frame
+ * X-parameter 5: pre-process assignments
+ * X-parameter 6: subcell processing
+ * X-parameter 7: pre-finalise assignments
+ * X-parameter 8: subcell finalisation
+ */
+#define LIST_MEANS(TYPE, SUFFIX)\
+	/* [default] arithmetic mean */\
+	X(ARITHMETIC, arithmetic, 1, COPY_FRAME,, *img1 += *buf,\
+	  a = (TYPE)1.0 / (TYPE)frame, *img1 *= a)\
+	/* geometric mean */\
+	X(GEOMETRIC, geometric, 1, COPY_FRAME,, *img1 *= *buf,\
+	  a = (TYPE)1.0 / (TYPE)frame, *img1 = nnpow##SUFFIX(*img1, a))\
+	/* harmonic mean */\
+	X(HARMONIC, harmonic, 1, ZERO_AND_PROCESS_FRAME,, *img1 += (TYPE)1 / *buf,\
+	  a = (TYPE)frame, *img1 = a / *img1)\
+	/* lehmer mean */\
+	X(LEHMER, lehmer, 2, ZERO_AND_PROCESS_FRAME, (a = (TYPE)power, b = a - (TYPE)1),\
+	  (*img1 += nnpow##SUFFIX(*buf, a), *img2 += nnpow##SUFFIX(*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##SUFFIX(*buf, a), (a = (TYPE)1 / (TYPE)frame, b = (TYPE)(1.0 / power)),\
+	  *img1 = a * nnpow##SUFFIX(*img1, b))
+
+enum first_frame_action {
+	COPY_FRAME,
+	PROCESS_FRAME,
+	ZERO_AND_PROCESS_FRAME,
+};
+
+#define X(V, ...) V,
+enum method { LIST_MEANS(,) };
+#undef X
+
+static double power;
+
+static inline double
+nnpow(double a, double b)
+{
+	int neg = a < 0;
+	a = pow(neg ? -a : a, b);
+	return neg ? -a : a;
+}
+
+static inline float
+nnpowf(float a, float b)
+{
+	int neg = a < 0;
+	a = powf(neg ? -a : a, b);
+	return neg ? -a : a;
+}
+
+#define MAKE_PROCESS(PIXFMT, TYPE, SUFFIX,\
+		     _1, NAME, _3, _4, PRE_PROCESS, PROCESS_SUBCELL, PRE_FINALISE, FINALISE_SUBCELL)\
+	static void\
+	process_##PIXFMT##_##NAME(struct stream *stream, void *buffer, void *image, size_t frame)\
+	{\
+		TYPE *buf = buffer, *img1 = image, a, b;\
+		TYPE *img2 = (TYPE *)(((char *)image) + stream->frame_size);\
+		size_t x, y;\
+		if (!stream) {\
+			PRE_FINALISE;\
+			for (y = 0; y < stream->height; y++)\
+				for (x = 0; x < stream->width; x++, img1++, img2++, buf++)\
+					FINALISE_SUBCELL;\
+		} else {\
+			PRE_PROCESS;\
+			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;\
+	}
+#define X(...) MAKE_PROCESS(xyza, double,, __VA_ARGS__)
+LIST_MEANS(double,)
+#undef X
+#define X(...) MAKE_PROCESS(xyzaf, float, f, __VA_ARGS__)
+LIST_MEANS(float, f)
+#undef X
+#undef MAKE_PROCESS
+
+#define X(ID, NAME, ...) [ID] = process_xyza_##NAME,
+static const process_func process_functions_xyza[] = { LIST_MEANS(,) };
+#undef X
+
+#define X(ID, NAME, ...) [ID] = process_xyzaf_##NAME,
+static const process_func process_functions_xyzaf[] = { LIST_MEANS(,) };
+#undef X
+
+int
+main(int argc, char *argv[])
+{
+	struct stream stream;
+	void *buf, *img;
+	process_func process;
+	size_t frames, images;
+	enum method method = ARITHMETIC;
+	enum first_frame_action first_frame_action;
+
+	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;
+	default:
+		usage();
+	} ARGEND;
+
+	if (argc)
+		usage();
+
+#define X(ID, _2, IMAGES, FIRST_FRAME_ACTION, ...)\
+	case ID:\
+		images = IMAGES;\
+		first_frame_action = FIRST_FRAME_ACTION;\
+		break;
+	switch (method) {
+	LIST_MEANS(,)
+	default:
+		abort();
+	}
+#undef X
+
+	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];
+        else
+                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
+
+	stream.frames = 1;
+	echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
+	fprint_stream_head(stdout, &stream);
+	efflush(stdout, "<stdout>");
+	buf = emalloc(stream.frame_size);
+	if (first_frame_action == ZERO_AND_PROCESS_FRAME)
+		img = ecalloc(images, stream.frame_size);
+	else
+		img = emalloc2(images, stream.frame_size);
+
+	frames = 0;
+	if (first_frame_action == COPY_FRAME) {
+		if (!eread_frame(&stream, buf))
+			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");
+	process(&stream, NULL, img, frames);
+
+	ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
+	free(buf);
+	free(img);
+	return 0;
+}