blind

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

commit b87d6efb6fdca579c23020a93212f8f35375e1fe
parent 533819598030141394a67cb8b80047622c478f44
Author: Mattias Andrée <maandree@kth.se>
Date:   Sun,  4 Jun 2017 16:46:14 +0200

Add blind-find-rectangle

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
MMakefile | 1+
MREADME | 3+++
Aman/blind-find-rectangle.1 | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mman/blind.7 | 3+++
Asrc/blind-find-rectangle.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 212 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -16,6 +16,7 @@ BIN =\ blind-disperse\ blind-dissolve\ blind-extend\ + blind-find-rectangle\ blind-flip\ blind-flop\ blind-from-image\ diff --git a/README b/README @@ -48,6 +48,9 @@ UTILITIES blind-extend(1) Add margins to a video + blind-find-rectangle(1) + Locate a coloured rectangle + blind-flip(1) Mirror a video vertically diff --git a/man/blind-find-rectangle.1 b/man/blind-find-rectangle.1 @@ -0,0 +1,70 @@ +.TH BLIND-FIND-RECTANGLE 1 blind +.SH NAME +blind-find-rectangle - Locate a coloured rectangle +.SH SYNOPSIS +.B blind-find-rectangle +[-a +.IR min-area ] +[-h +.IR min-height ] +[-w +.IR min-width ] +.I X +.I Y +.I Z +.RI [ alpha ] +.SH DESCRIPTION +.B blind-find-rectangle +reads a video from stdin, and locates the largest +rectangle of a specified colour. If there are two +or more maximal rectangles, one is choosen arbitrarily. +The specified by the arguments +.IR X , +.IR Y , +and +.IR Z , +and the selected +.I alpha +value. The colour is specified in CIE XYZ. If +.I X +and +.I Z +are not specified, the colour will be CIE Standard Illuminant +D65-grey with the luminosity +.IR Y . +If +.I alpha +is not specified, 1, which means fully opaque, will be used. +.SH STDOUT +The location and dimensions of the rectangle is printed stdout. +Exactly one line is printed per frame. Each line has the format +.nf + + \fB"%zu %zu %zu %zu\\n"\fP, <\fIleft\fP>, <\fItop\fP>, <\fIwidth\fP>, <\fIheight\fP> + +.fi +where +.I left +is position on the X-axis (measured from the left) on the +left-most pixels in the rectangle, +.I top +is position on the Y-axis (measured from the top) on the +top-most pixels in the rectangle, +.I width +is the width of the rectangle, and +.I height +is the width of the rectangle. +.SH NOTES +.B blind-find-rectangle +may be changed in the future to use some other colour model, +therefore, it is recommended to also use +.BR blind-colour-ciexyz (1) +if you are specifying the colour in CIE XYZ. If however +your values are colour space-agnostic, you should not. +.SH SEE ALSO +.BR blind (7), +.BR blind-colour-ciexyz (1), +.BR blind-colour-srgb (1) +.SH AUTHORS +Mattias Andrée +.RI < maandree@kth.se > diff --git a/man/blind.7 b/man/blind.7 @@ -61,6 +61,9 @@ Framewise split a video into multiple videos .BR blind-extend (1) Add margins to a video .TP +.BR blind-find-rectangle (1) +Locate a coloured rectangle +.TP .BR blind-flip (1) Mirror a video vertically .TP diff --git a/src/blind-find-rectangle.c b/src/blind-find-rectangle.c @@ -0,0 +1,135 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("[-a min-area] [-h min-height] [-w min-width] X Y Z [alpha]") + +struct pair { + size_t x; + size_t w; +}; + +static struct stream stream; +static double X, Y, Z, alpha = 1; +static size_t min_width = 1; +static size_t min_height = 1; +static size_t min_area = 1; +static struct pair *stack = NULL; +static size_t *cache = NULL; +static char *buf = NULL; + +static void +process(const void *colour) +{ + size_t y, x, x0, w, w0, h, top, area; + size_t best_area, x1, x2, y1, y2; + for (;;) { + top = x1 = x2 = y1 = y2 = best_area = 0; + memset(cache, 0, (stream.width + 1) * sizeof(*cache)); + for (y = 0; eread_row(&stream, buf); y++) { + w = 0; + for (x = 0; x <= stream.width; x++) { + if (x != stream.width) { + if (!memcmp(buf + x * stream.pixel_size, colour, stream.pixel_size)) + cache[x] += 1; + else + cache[x] = 0; + } + if (cache[x] > w) { + stack[top].x = x; + stack[top++].w = w; + w = cache[x]; + } else if (cache[x] < w) { + do { + x0 = stack[--top].x; + w0 = stack[top].w; + area = w * (x - x0); + if (area > best_area) { + best_area = area; + x1 = x0; + x2 = x - 1; + y1 = y - w + 1; + y2 = y; + } + w = w0; + } while (cache[x] < w); + if ((w = cache[x])) { + stack[top].x = x0; + stack[top++].w = w0; + } + } + } + fprintf(stderr, "%zu\n", y); + } + if (!y) + break; + w = x2 - x1 + 1; + h = y2 - y1 + 1; + if (best_area < min_area || w < min_width || h < min_height) + printf("0 0 0 0\n"); + else + printf("%zu %zu %zu %zu\n", x1, y1, w, h); + } +} + +int +main(int argc, char *argv[]) +{ + double colour_lf[4]; + double colour_f[4]; + + ARGBEGIN { + case 'a': + min_area = etozu_flag('a', UARGF(), 1, SIZE_MAX); + break; + case 'h': + min_height = etozu_flag('h', UARGF(), 1, SIZE_MAX); + break; + case 'w': + min_width = etozu_flag('w', UARGF(), 1, SIZE_MAX); + break; + default: + usage(); + } ARGEND; + + if (argc != 3 && argc != 4) + usage(); + + X = etolf_arg("the X value", argv[0]); + Y = etolf_arg("the Y value", argv[1]); + Z = etolf_arg("the Z value", argv[2]); + if (argc > 3) + alpha = etolf_arg("the alpha value", argv[3]); + + eopen_stream(&stream, NULL); + echeck_dimensions(&stream, WIDTH, NULL); + if (stream.width == SIZE_MAX) + eprintf("video is too wide\n"); + if (stream.width > SIZE_MAX / stream.height) + eprintf("video is too large\n"); + + stack = emalloc2(stream.width + 1, sizeof(*stack)); + cache = emalloc2(stream.width + 1, sizeof(*cache)); + buf = emalloc(stream.row_size); + + if (!strcmp(stream.pixfmt, "xyza")) { + colour_lf[0] = X; + colour_lf[1] = Y; + colour_lf[2] = Z; + colour_lf[3] = alpha; + process(colour_lf); + } else if (!strcmp(stream.pixfmt, "xyza f")) { + colour_f[0] = (float)X; + colour_f[1] = (float)Y; + colour_f[2] = (float)Z; + colour_f[3] = (float)alpha; + process(colour_f); + } else { + eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt); + } + + fshut(stdout, "<stdout>"); + free(stack); + free(cache); + free(buf); + return 0; +}