commit b87d6efb6fdca579c23020a93212f8f35375e1fe
Author:     Mattias Andrée <[email protected]>
AuthorDate: Sun Jun 4 16:46:14 2017 +0200
Commit:     Mattias Andrée <[email protected]>
CommitDate: Sun Jun 4 16:46:14 2017 +0200

    Add blind-find-rectangle
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index be882d8..21e4e1f 100644
--- 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
index eb2d574..8b0b49a 100644
--- 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
new file mode 100644
index 0000000..beb16fa
--- /dev/null
+++ 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 < [email protected] >
diff --git a/man/blind.7 b/man/blind.7
index b811440..ff3f479 100644
--- 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
new file mode 100644
index 0000000..87d2734
--- /dev/null
+++ 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;
+}

Reply via email to