commit ae811c3ce7b71902b508c65278050f0358471e98
parent 521dc0c9100cd167be8525ca00ccaed4e0d7fa33
Author: Mattias Andrée <maandree@kth.se>
Date: Thu, 12 Jan 2017 07:53:06 +0100
m + Add vu-gauss-blur
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
12 files changed, 353 insertions(+), 38 deletions(-)
diff --git a/Makefile b/Makefile
@@ -12,6 +12,7 @@ BIN =\
vu-flip\
vu-flop\
vu-from-image\
+ vu-gauss-blur\
vu-invert-luma\
vu-next-frame\
vu-read-head\
diff --git a/TODO b/TODO
@@ -1,10 +1,11 @@
-vu-gauss-blur gaussian blur, -c to only blur chroma
vu-transform transformation by matrix multiplication, -t for tiling, -s for improve quality
on downscaling (pixels' neighbours must not change)
vu-chroma-key replace a chroma with transparency
vu-primary-key replace a primary with transparency, -g for greyscaled images
vu-from-video use ffmpeg to convert from another format
vu-to-video use ffmpeg to convert to another format
+vu-to-text convert each pixel to text
+vu-from-text convert each pixel from text
vu-primaries given three selectable primaries split the video into three side-by-side which
only one primary active
vu-apply-map remap pixels (distortion) using the X and Y values, -t for tiling, -s for
diff --git a/config.mk b/config.mk
@@ -1,3 +1,5 @@
+# You may want to remove -DHAVE_PRCTL from CPPFLAGS if you are not using Linux.
+
CFLAGS = -std=c99 -Wall -pedantic -O2
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -DHAVE_PRCTL
LDFLAGS = -lm -s
diff --git a/src/stream.c b/src/stream.c
@@ -2,6 +2,7 @@
#include "stream.h"
#include "util.h"
+#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <inttypes.h>
@@ -189,27 +190,27 @@ enread_frame(int status, struct stream *stream, void *buf, size_t n)
}
if (!stream->ptr)
return 0;
- stream->ptr = 0;
+ stream->ptr -= n;
return 1;
}
void
-process_each_frame_segmented(struct stream *stream, int output_fd, const char* output_fname,
- void (*process)(struct stream *stream, size_t n, size_t frame))
+nprocess_each_frame_segmented(int status, struct stream *stream, int output_fd, const char* output_fname,
+ void (*process)(struct stream *stream, size_t n, size_t frame))
{
size_t frame_size, frame, r, n;
- echeck_frame_size(stream->width, stream->height, stream->pixel_size, 0, stream->file);
+ encheck_frame_size(status, stream->width, stream->height, stream->pixel_size, 0, stream->file);
frame_size = stream->height * stream->width * stream->pixel_size;
for (frame = 0; frame < stream->frames; frame++) {
for (n = frame_size; n; n -= r) {
- if (!eread_stream(stream, n))
- eprintf("%s: file is shorter than expected\n", stream->file);
+ if (!enread_stream(status, stream, n))
+ enprintf(status, "%s: file is shorter than expected\n", stream->file);
r = stream->ptr - (stream->ptr % stream->pixel_size);
(process)(stream, r, frame);
- ewriteall(output_fd, stream->buf, r, output_fname);
+ enwriteall(status, output_fd, stream->buf, r, output_fname);
memmove(stream->buf, stream->buf + r, stream->ptr -= r);
}
}
@@ -217,20 +218,20 @@ process_each_frame_segmented(struct stream *stream, int output_fd, const char* o
void
-process_two_streams(struct stream *left, struct stream *right, int output_fd, const char* output_fname,
- void (*process)(struct stream *left, struct stream *right, size_t n))
+nprocess_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
+ void (*process)(struct stream *left, struct stream *right, size_t n))
{
size_t n;
- echeck_compat(left, right);
+ encheck_compat(status, left, right);
for (;;) {
- if (left->ptr < sizeof(left->buf) && !eread_stream(left, SIZE_MAX)) {
+ if (left->ptr < sizeof(left->buf) && !enread_stream(status, left, SIZE_MAX)) {
close(left->fd);
left->fd = -1;
break;
}
- if (right->ptr < sizeof(right->buf) && !eread_stream(right, SIZE_MAX)) {
+ if (right->ptr < sizeof(right->buf) && !enread_stream(status, right, SIZE_MAX)) {
close(right->fd);
right->fd = -1;
break;
@@ -243,40 +244,40 @@ process_two_streams(struct stream *left, struct stream *right, int output_fd, co
process(left, right, n);
- ewriteall(output_fd, left->buf, n, output_fname);
+ enwriteall(status, output_fd, left->buf, n, output_fname);
if ((n & 3) || left->ptr != right->ptr) {
- memmove(left->buf, left->buf + n, left->ptr);
- memmove(right->buf, right->buf + n, right->ptr);
+ memmove(left->buf, left->buf + n, left->ptr);
+ memmove(right->buf, right->buf + n, right->ptr);
}
}
if (right->fd >= 0)
close(right->fd);
- ewriteall(output_fd, left->buf, left->ptr, output_fname);
+ enwriteall(status, output_fd, left->buf, left->ptr, output_fname);
if (left->fd >= 0) {
for (;;) {
left->ptr = 0;
- if (!eread_stream(left, SIZE_MAX)) {
+ if (!enread_stream(status, left, SIZE_MAX)) {
close(left->fd);
left->fd = -1;
break;
}
- ewriteall(output_fd, left->buf, left->ptr, output_fname);
+ enwriteall(status, output_fd, left->buf, left->ptr, output_fname);
}
}
}
void
-process_multiple_streams(struct stream *streams, size_t n_streams, int output_fd, const char* output_fname,
- void (*process)(struct stream *streams, size_t n_streams, size_t n))
+nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, int output_fd, const char* output_fname,
+ void (*process)(struct stream *streams, size_t n_streams, size_t n))
{
size_t closed, i, j, n;
for (i = 1; i < n_streams; i++)
- echeck_compat(streams + i, streams);
+ encheck_compat(status, streams + i, streams);
while (n_streams) {
n = SIZE_MAX;
@@ -291,7 +292,7 @@ process_multiple_streams(struct stream *streams, size_t n_streams, int output_fd
n -= n % streams->pixel_size;
process(streams, n_streams, n);
- ewriteall(output_fd, streams->buf, n, output_fname);
+ enwriteall(status, output_fd, streams->buf, n, output_fname);
closed = SIZE_MAX;
for (i = 0; i < n_streams; i++) {
@@ -307,3 +308,56 @@ process_multiple_streams(struct stream *streams, size_t n_streams, int output_fd
}
}
}
+
+
+void
+nprocess_each_frame_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
+ void (*process)(char *restrict output, char *restrict lbuf, char *restrict rbuf,
+ struct stream *left, struct stream *right, size_t ln, size_t rn))
+{
+ size_t lframe_size, rframe_size;
+ char *lbuf, *rbuf, *image;
+
+ encheck_frame_size(status, left->width, left->height, left->pixel_size, 0, left->file);
+ encheck_frame_size(status, right->width, right->height, right->pixel_size, 0, right->file);
+ lframe_size = left->height * left->width * left->pixel_size;
+ rframe_size = right->height * right->width * right->pixel_size;
+
+ if (lframe_size > SIZE_MAX - lframe_size || 2 * lframe_size > SIZE_MAX - rframe_size)
+ enprintf(status, "video frame is too large\n");
+
+ image = mmap(0, 2 * lframe_size + lframe_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (image == MAP_FAILED)
+ enprintf(status, "mmap:");
+ lbuf = image + 1 * lframe_size;
+ rbuf = image + 2 * lframe_size;
+
+ for (;;) {
+ if (!enread_frame(status, left, lbuf, lframe_size)) {
+ close(left->fd);
+ left->fd = -1;
+ break;
+ }
+ if (!enread_frame(status, right, rbuf, rframe_size)) {
+ close(right->fd);
+ right->fd = -1;
+ break;
+ }
+
+ process(image, lbuf, rbuf, left, right, lframe_size, rframe_size);
+ enwriteall(status, output_fd, image, lframe_size, output_fname);
+ }
+
+ if (right->fd >= 0)
+ close(right->fd);
+
+ if (left->fd >= 0) {
+ memcpy(image, lbuf, left->ptr);
+ while (enread_frame(status, left, lbuf, lframe_size))
+ enwriteall(status, output_fd, image, lframe_size, output_fname);
+ }
+
+ free(lbuf);
+ free(rbuf);
+ munmap(image, 2 * lframe_size + rframe_size);
+}
diff --git a/src/stream.h b/src/stream.h
@@ -13,6 +13,11 @@
#define enread_row(...) enread_frame(__VA_ARGS__)
#define eread_row(...) eread_frame(__VA_ARGS__)
+#define process_each_frame_segmented(...) nprocess_each_frame_segmented(1, __VA_ARGS__)
+#define process_two_streams(...) nprocess_two_streams(1, __VA_ARGS__)
+#define process_multiple_streams(...) nprocess_multiple_streams(1, __VA_ARGS__)
+#define process_each_frame_two_streams(...) nprocess_each_frame_two_streams(1, __VA_ARGS__)
+
struct stream
{
size_t frames;
@@ -38,11 +43,15 @@ void encheck_frame_size(int status, size_t width, size_t height, size_t pixel_si
void encheck_compat(int status, const struct stream *a, const struct stream *b);
int enread_frame(int status, struct stream *stream, void *buf, size_t n);
-void process_each_frame_segmented(struct stream *stream, int output_fd, const char* output_fname,
+void nprocess_each_frame_segmented(int status, struct stream *stream, int output_fd, const char* output_fname,
void (*process)(struct stream *stream, size_t n, size_t frame));
-void process_two_streams(struct stream *left, struct stream *right, int output_fd, const char* output_fname,
+void nprocess_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
void (*process)(struct stream *left, struct stream *right, size_t n));
-void process_multiple_streams(struct stream *streams, size_t n_streams, int output_fd, const char* output_fname,
+void nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, int output_fd, const char* output_fname,
void (*process)(struct stream *streams, size_t n_streams, size_t n));
+
+void nprocess_each_frame_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
+ void (*process)(char *restrict output, char *restrict lbuf, char *restrict rbuf,
+ struct stream *left, struct stream *right, size_t ln, size_t rn));
diff --git a/src/util.c b/src/util.c
@@ -1,9 +1,14 @@
/* See LICENSE file for copyright and license details. */
#include "util.h"
+#if defined(HAVE_PRCTL)
+# include <sys/prctl.h>
+#endif
+#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -118,3 +123,48 @@ writeall(int fd, void *buf, size_t n)
}
return 0;
}
+
+
+static inline pid_t
+enfork(int status)
+{
+ pid_t pid = fork();
+ if (pid == -1)
+ enprintf(status, "fork:");
+ return pid;
+}
+
+
+int
+enfork_jobs(int status, size_t *start, size_t *end, size_t jobs)
+{
+ size_t j, s = *start, n = *end - *start;
+ if (jobs < 2)
+ return 1;
+ *end = n / jobs + s;
+ for (j = 1; j < jobs; j++) {
+ if (!enfork(status)) {
+#if defined(HAVE_PRCTL) && defined(PR_SET_PDEATHSIG)
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+#endif
+ *start = n * (j + 0) / jobs + s;
+ *end = n * (j + 1) / jobs + s;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+void
+enjoin_jobs(int status, int is_master)
+{
+ int stat;
+ if (!is_master)
+ exit(0);
+ while (wait(&stat) != -1)
+ if (!stat)
+ exit(status);
+ if (errno != ECHILD)
+ enprintf(status, "wait:");
+}
diff --git a/src/util.h b/src/util.h
@@ -15,3 +15,4 @@
#include "util/to.h"
#include "util/colour.h"
#include "util/io.h"
+#include "util/jobs.h"
diff --git a/src/util/jobs.h b/src/util/jobs.h
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+
+#define efork_jobs(...) enfork_jobs(1, __VA_ARGS__)
+#define ejoin_jobs(...) enjoin_jobs(1, __VA_ARGS__)
+
+int enfork_jobs(int status, size_t *start, size_t *end, size_t jobs);
+void enjoin_jobs(int status, int is_master);
diff --git a/src/vu-concat.c b/src/vu-concat.c
@@ -77,9 +77,11 @@ concat_to_file(int argc, char *argv[], char *output_file)
sprintf(head, "%zu %zu %zu %s\n%cuivf%zn",
stream.frames, stream.width, stream.height, stream.pixfmt, 0, &headlen);
- ewriteall(STDOUT_FILENO, head, (size_t)headlen, "<stdout>");
+ ewriteall(fd, head, (size_t)headlen, output_file);
data = mmap(0, size + (size_t)headlen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (data == MAP_FAILED)
+ eprintf("mmap %s:", output_file);
memmove(data + headlen, data, size);
memcpy(data, head, (size_t)headlen);
munmap(data, size + (size_t)headlen);
diff --git a/src/vu-gauss-blur.c b/src/vu-gauss-blur.c
@@ -0,0 +1,189 @@
+/* See LICENSE file for copyright and license details. */
+#include "stream.h"
+#include "util.h"
+
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+USAGE("[-j jobs] [-ac] sd-stream")
+
+static int chroma = 0;
+static int noalpha = 0;
+static size_t jobs = 1;
+
+static void
+process_xyza(char *restrict output, char *restrict cbuf, char *restrict sbuf,
+ struct stream *colour, struct stream *sigma, size_t n, size_t unused)
+{
+ typedef double pixel_t[4];
+
+ pixel_t *restrict clr = (pixel_t *)cbuf;
+ pixel_t *restrict sig = (pixel_t *)sbuf;
+ pixel_t *img = (pixel_t *)output;
+ pixel_t c, k;
+ double *p;
+ size_t x1, y1, i1, x2, y2, i2;
+ double dy, d, m, dblurred;
+ int i, blurred, blur[3] = {0, 0, 0};
+ size_t start = 0, end = colour->height;
+ int is_master;
+
+ /* premultiply alpha channel */
+ if (!noalpha) {
+ for (y1 = i1 = 0; y1 < colour->height; y1++) {
+ for (x1 = 0; x1 < colour->width; x1++, i1++) {
+ clr[i1][0] *= clr[i1][3];
+ clr[i1][1] *= clr[i1][3];
+ clr[i1][2] *= clr[i1][3];
+ }
+ }
+ }
+
+ is_master = efork_jobs(&start, &end, jobs);
+
+ /* blur */
+ i1 = start * colour->width;
+ for (y1 = i1 = start; y1 < end; y1++) {
+ for (x1 = 0; x1 < colour->width; x1++, i1++) {
+ if (sig[i1][3] == 0)
+ goto no_blur;
+ if (!chroma) {
+ for (i = 0; i < 3; i++) {
+ k[i] = sig[i1][i] * sig[i1][3], c[i] = k[i] *= k[i] * 2, c[i] *= M_PI;
+ k[i] = 1 / k[i], c[i] = -1 / c[i];
+ blur[i] = !sig[i1][1];
+ }
+ } else {
+ k[1] = sig[i1][1] * sig[i1][3], c[1] = k[1] *= k[1] * 2, c[1] *= M_PI;
+ k[1] = 1 / -k[1], c[1] = 1 / c[1];
+ blur[1] = !sig[i1][1];
+ }
+ if (blur[0] + blur[1] + blur[2] == 0)
+ goto no_blur;
+
+ p = img[i1];
+ p[0] = p[1] = p[2] = 0;
+ p[3] = noalpha;
+ if (k[0] == k[1] && k[1] == k[2]) {
+ for (y2 = i2 = 0; y2 < colour->height; y2++) {
+ dy = (ssize_t)y1 - (ssize_t)y2;
+ dy *= dy;
+ for (x2 = 0; x2 < colour->width; x2++, i2++) {
+ d = (ssize_t)x1 - (ssize_t)x2;
+ d = d * d + dy;
+ m = c[i1] * exp(d * k[i1]);
+ for (i = noalpha ? 3 : 4; i--;)
+ p[i] += clr[i2][i] * m;
+ }
+ }
+ } else {
+ blurred = 0;
+ for (i = 0; i < n; i++) {
+ if (!blur[i]) {
+ p[i] = clr[i1][i];
+ continue;
+ }
+ for (y2 = i2 = 0; y2 < colour->height; y2++) {
+ dy = (ssize_t)y1 - (ssize_t)y2;
+ dy *= dy;
+ if (!noalpha) {
+ for (x2 = 0; x2 < colour->width; x2++, i2++) {
+ d = (ssize_t)x1 - (ssize_t)x2;
+ d = d * d + dy;
+ m = c[i1] * exp(d * k[i1]);
+ p[i] += clr[i2][i] * m;
+ p[3] += clr[i2][3] * m;
+ }
+ } else {
+ for (x2 = 0; x2 < colour->width; x2++, i2++) {
+ d = (ssize_t)x1 - (ssize_t)x2;
+ d = d * d + dy;
+ m = c[i1] * exp(d * k[i1]);
+ p[i] += clr[i2][i] * m;
+ }
+ }
+ }
+ blurred += 1;
+ }
+ if (!noalpha) {
+ dblurred = blurred;
+ for (y2 = i2 = 0; y2 < colour->height; y2++)
+ for (x2 = 0; x2 < colour->width; x2++, i2++)
+ p[3] /= dblurred;
+ }
+ }
+
+ continue;
+ no_blur:
+ img[i1][0] = clr[i1][0];
+ img[i1][1] = clr[i1][1];
+ img[i1][2] = clr[i1][2];
+ img[i1][3] = clr[i1][3];
+ }
+ }
+
+ ejoin_jobs(is_master);
+
+ /* unpremultiply alpha channel */
+ if (!noalpha) {
+ for (y1 = i1 = 0; y1 < colour->height; y1++) {
+ for (x1 = 0; x1 < colour->width; x1++, i1++) {
+ if (!img[i1][3])
+ continue;
+ img[i1][0] /= img[i1][3];
+ img[i1][1] /= img[i1][3];
+ img[i1][2] /= img[i1][3];
+ }
+ }
+ }
+
+ (void) unused;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct stream colour, sigma;
+ void (*process)(char *restrict output, char *restrict cbuf, char *restrict sbuf,
+ struct stream *colour, struct stream *sigma, size_t cn, size_t sn);
+
+ ARGBEGIN {
+ case 'a':
+ noalpha = 1;
+ break;
+ case 'c':
+ chroma = 1;
+ break;
+ case 'j':
+ jobs = etozu_flag('j', EARG(), 1, SHRT_MAX);
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc != 1)
+ usage();
+
+ colour.file = "<stdin>";
+ colour.fd = STDIN_FILENO;
+ einit_stream(&colour);
+
+ sigma.file = argv[0];
+ sigma.fd = eopen(sigma.file, O_RDONLY);
+ einit_stream(&sigma);
+
+ if (!strcmp(colour.pixfmt, "xyza"))
+ process = process_xyza;
+ else
+ eprintf("pixel format %s is not supported, try xyza\n", colour.pixfmt);
+
+ echeck_compat(&colour, &sigma);
+
+ process_each_frame_two_streams(&colour, &sigma, STDOUT_FILENO, "<stdout>", process);
+
+ return 0;
+}
diff --git a/src/vu-next-frame.c b/src/vu-next-frame.c
@@ -6,9 +6,6 @@
#include <string.h>
#include <unistd.h>
-#undef eprintf
-#define eprintf(...) enprintf(2, __VA_ARGS__)
-
USAGE("width height pixel-format ...")
int
@@ -26,8 +23,8 @@ main(int argc, char *argv[])
stream.file = "<stdin>";
stream.pixfmt[0] = '\0';
- stream.width = etozu_arg("the width", argv[0], 1, SIZE_MAX);
- stream.height = etozu_arg("the height", argv[1], 1, SIZE_MAX);
+ stream.width = entozu_arg(2, "the width", argv[0], 1, SIZE_MAX);
+ stream.height = entozu_arg(2, "the height", argv[1], 1, SIZE_MAX);
argv += 2, argc -= 2;
n = (size_t)argc - 1;
@@ -41,26 +38,26 @@ main(int argc, char *argv[])
}
}
- eset_pixel_size(&stream);
+ enset_pixel_size(2, &stream);
fprint_stream_head(stdout, &stream);
- efflush(stdout, "<stdout>");
+ enfflush(2, stdout, "<stdout>");
w = stream.width * stream.pixel_size;
while (stream.height) {
stream.height--;
for (n = w; n; n -= stream.ptr) {
stream.ptr = 0;
- if (!eread_stream(&stream, n))
+ if (!enread_stream(2, &stream, n))
goto done;
anything = 1;
- ewriteall(STDOUT_FILENO, stream.buf, stream.ptr, "<stdout>");
+ enwriteall(2, STDOUT_FILENO, stream.buf, stream.ptr, "<stdout>");
}
}
done:
if (stream.height || n)
- eprintf("incomplete frame\n");
+ enprintf(2, "incomplete frame\n");
return !anything;
}
diff --git a/src/vu-rewrite-head.c b/src/vu-rewrite-head.c
@@ -50,6 +50,8 @@ rewrite(struct stream *stream, int frames_auto)
eprintf("ftruncate %s:", stream->file);
data = mmap(0, length + (size_t)headlen, PROT_READ | PROT_WRITE, MAP_PRIVATE, stream->fd, 0);
+ if (data == MAP_FAILED)
+ eprintf("mmap %s:", stream->file);
if (headlen != stream->headlen)
memmove(data + headlen, data + stream->headlen, length);
memcpy(data, head, (size_t)headlen);