Add blind-find-rectangle - blind - suckless command-line video editing utility
 (HTM) git clone git://git.suckless.org/blind
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit b87d6efb6fdca579c23020a93212f8f35375e1fe
 (DIR) parent 533819598030141394a67cb8b80047622c478f44
 (HTM) 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:
         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(-)
       ---
 (DIR) 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\
 (DIR) 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
        
 (DIR) 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 >
 (DIR) 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
 (DIR) 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;
       +}