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;
+}