Add blind-find-rectangle - 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 <[email protected]> | |
Date: Sun, 4 Jun 2017 16:46:14 +0200 | |
Add blind-find-rectangle | |
Signed-off-by: Mattias Andrée <[email protected]> | |
Diffstat: | |
M Makefile | 1 + | |
M README | 3 +++ | |
A man/blind-find-rectangle.1 | 70 +++++++++++++++++++++++++++++… | |
M man/blind.7 | 3 +++ | |
A src/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>, … | |
+ | |
+.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 < [email protected] > | |
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_siz… | |
+ 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… | |
+ } | |
+ | |
+ fshut(stdout, "<stdout>"); | |
+ free(stack); | |
+ free(cache); | |
+ free(buf); | |
+ return 0; | |
+} |