diff mbox series

[FFmpeg-devel,v3,5/5] avfilter/vf_libopencv: add opencv drawbox filter

Message ID 1589811766-32338-1-git-send-email-lance.lmwang@gmail.com
State New
Headers show
Series Untitled series #1246
Related show

Commit Message

Limin Wang May 18, 2020, 2:22 p.m. UTC
From: Limin Wang <lance.lmwang@gmail.com>

Signed-off-by: Limin Wang <lance.lmwang@gmail.com>
---
depend on below patchset:
https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=1212

 doc/filters.texi           |  21 ++++++
 libavfilter/vf_libopencv.c | 147 +++++++++++++++++++++++++++++++++++--
 2 files changed, 162 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index e12c667348..cac204fd81 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -14219,6 +14219,27 @@  Display width and height of every faces, face_id is the face index
 which is range from [0, nb_faces-1]
 @end table
 
+@subsection drawbox
+draw all boxes by the detected metadata results of the ocv filter's
+facedetect mode. If no face detection metadata exists, then the filter
+will do nothing.
+
+The filter takes the following parameters:
+@var{color}|@var{thickness}|@var{line_type}|@var{shift}.
+
+@var{color} Specify the color of the box to write. For the general syntax of this option,
+check the @ref{color syntax,,"Color" section in the ffmpeg-utils manual,ffmpeg-utils}.
+
+@var{thickness}
+Set the thickness of the box edge. Default value is @code{1}.
+Negative values, like -1, mean that the function has to draw a filled rectangle.
+
+@var{line_type}
+Set the line type of the box boundary. Default value is @code{8}.
+
+@var{shift}
+Set the number of fractional bits in the point coordinates. Default value is @code{0}.
+
 @section oscilloscope
 
 2D Video Oscilloscope.
diff --git a/libavfilter/vf_libopencv.c b/libavfilter/vf_libopencv.c
index c70c4dc8b9..2e23222cb4 100644
--- a/libavfilter/vf_libopencv.c
+++ b/libavfilter/vf_libopencv.c
@@ -42,6 +42,7 @@ 
 #include "libavutil/common.h"
 #include "libavutil/file.h"
 #include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
 #include "avfilter.h"
 #include "formats.h"
 #include "internal.h"
@@ -90,6 +91,7 @@  typedef struct OCVContext {
     void (*uninit)(AVFilterContext *ctx);
     void (*end_frame_filter)(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg);
     void (*postprocess)(AVFilterContext *ctx, AVFrame *out);
+    void (*preprocess)(AVFilterContext *ctx, AVFrame *in);
     void *priv;
 } OCVContext;
 
@@ -466,6 +468,127 @@  static void facedetect_postprocess(AVFilterContext *ctx, AVFrame *out)
     }
 }
 
+typedef struct DrawboxContext {
+    CvScalar color;
+    int thickness;
+    int line_type;
+    int shift;
+
+    int nb_faces;
+    CvRect *faces[1024];
+} DrawboxContext;
+
+static av_cold int drawbox_init(AVFilterContext *ctx, const char *args)
+{
+    OCVContext *s = ctx->priv;
+    DrawboxContext *drawbox = s->priv;
+    const char *buf = args;
+    int ret;
+    uint8_t rgba[4] = { 255, 0, 0, 255};
+    char color_str[32] = "Red";
+
+    drawbox->thickness = 1;
+    drawbox->line_type = 8;
+    drawbox->shift = 0;
+    if (args) {
+        sscanf(args, "%32[^|]|%d|%d|%d", color_str, &drawbox->thickness, &drawbox->line_type, &drawbox->shift);
+    }
+
+    if (av_parse_color(rgba, color_str, -1, ctx) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "failed to get color \n");
+        return AVERROR(EINVAL);
+    }
+    drawbox->color = cvScalar(rgba[0], rgba[1], rgba[2], rgba[3]);
+
+    av_log(ctx, AV_LOG_TRACE, "rgba: %d:%d:%d:%d, thickness: %d, line_type: %d, shift: %d \n",
+            rgba[0], rgba[1], rgba[2], rgba[3], drawbox->thickness, drawbox->line_type, drawbox->shift);
+
+    return 0;
+}
+
+static void drawbox_preprocess(AVFilterContext *ctx, AVFrame *in)
+{
+    OCVContext *s = ctx->priv;
+    DrawboxContext *drawbox = s->priv;
+    AVDictionaryEntry *ef, *ex, *ey, *ew, *eh;
+    char key2[128];
+    AVDictionary *metadata = in->metadata;
+    int nb_faces = 0;
+
+    ef = av_dict_get(metadata, "lavfi.facedetect.nb_faces", NULL, AV_DICT_MATCH_CASE);
+    if (ef) {
+        nb_faces = strtol(ef->value, NULL, 10);
+    }
+
+    if (nb_faces > 0) {
+        drawbox->nb_faces = nb_faces;
+        for (int i = 0; i < nb_faces && i < sizeof(drawbox->faces); i++ ) {
+            CvRect *tmp;
+
+            tmp =  av_realloc(drawbox->faces[i], sizeof(*tmp));
+            if (!tmp)
+                return AVERROR(ENOMEM);
+            drawbox->faces[i] = tmp;
+
+            snprintf(key2, sizeof(key2), "lavfi.facedetect.%d.%s", i, "x");
+            ex = av_dict_get(metadata, key2, NULL, AV_DICT_MATCH_CASE);
+
+            snprintf(key2, sizeof(key2), "lavfi.facedetect.%d.%s", i, "y");
+            ey = av_dict_get(metadata, key2, NULL, AV_DICT_MATCH_CASE);
+
+            snprintf(key2, sizeof(key2), "lavfi.facedetect.%d.%s", i, "w");
+            ew = av_dict_get(metadata, key2, NULL, AV_DICT_MATCH_CASE);
+
+            snprintf(key2, sizeof(key2), "lavfi.facedetect.%d.%s", i, "h");
+            eh = av_dict_get(metadata, key2, NULL, AV_DICT_MATCH_CASE);
+
+            if (ex && ey && ew && eh) {
+                tmp->x      = strtol(ex->value, NULL, 10);
+                tmp->y      = strtol(ey->value, NULL, 10);
+                tmp->width  = strtol(ew->value, NULL, 10);
+                tmp->height = strtol(eh->value, NULL, 10);
+            }
+        }
+    }
+}
+
+static av_cold void drawbox_uninit(AVFilterContext *ctx)
+{
+    OCVContext *s = ctx->priv;
+    DrawboxContext *drawbox = s->priv;
+
+    for (int i = 0; i < drawbox->nb_faces; i++ ) {
+        av_freep(&drawbox->faces[i]);
+    }
+}
+
+static void draw_rectangle(AVFilterContext *ctx, IplImage *img, CvPoint pt1, CvPoint pt2) {
+    OCVContext *s = ctx->priv;
+    DrawboxContext *drawbox = s->priv;
+
+    cvRectangle(img, pt1, pt2, drawbox->color, drawbox->thickness, drawbox->line_type, drawbox->shift);
+}
+
+static void drawbox_end_frame_filter(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg)
+{
+    OCVContext *s = ctx->priv;
+    DrawboxContext *drawbox = s->priv;
+
+    for (int i = 0; i < drawbox->nb_faces; i++ ) {
+        CvPoint pt1, pt2;
+        CvRect *face = drawbox->faces[i];
+
+        if (face) {
+            pt1.x = face->x;
+            pt1.y = face->y;
+            pt2.x = face->x + face->width;
+            pt2.y = face->y + face->height;
+
+            draw_rectangle(ctx, inimg, pt1, pt2);
+        }
+    }
+}
+
 typedef struct OCVFilterEntry {
     const char *name;
     size_t priv_size;
@@ -473,13 +596,15 @@  typedef struct OCVFilterEntry {
     void (*uninit)(AVFilterContext *ctx);
     void (*end_frame_filter)(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg);
     void (*postprocess)(AVFilterContext *ctx, AVFrame *out);
+    void (*preprocess)(AVFilterContext *ctx, AVFrame *in);
 } OCVFilterEntry;
 
 static const OCVFilterEntry ocv_filter_entries[] = {
-    { "dilate", sizeof(DilateContext), dilate_init, dilate_uninit, dilate_end_frame_filter, NULL },
-    { "erode",  sizeof(DilateContext), dilate_init, dilate_uninit, erode_end_frame_filter, NULL },
-    { "smooth", sizeof(SmoothContext), smooth_init, NULL, smooth_end_frame_filter, NULL },
-    { "facedetect", sizeof(FaceDetectContext), facedetect_init, facedetect_uninit, facedetect_end_frame_filter, facedetect_postprocess },
+    { "dilate", sizeof(DilateContext), dilate_init, dilate_uninit, dilate_end_frame_filter, NULL, NULL },
+    { "erode",  sizeof(DilateContext), dilate_init, dilate_uninit, erode_end_frame_filter, NULL, NULL},
+    { "smooth", sizeof(SmoothContext), smooth_init, NULL, smooth_end_frame_filter, NULL, NULL},
+    { "facedetect", sizeof(FaceDetectContext), facedetect_init, facedetect_uninit, facedetect_end_frame_filter, NULL, facedetect_postprocess},
+    { "drawbox", sizeof(DrawboxContext), drawbox_init, drawbox_uninit, drawbox_end_frame_filter, drawbox_preprocess, NULL},
 };
 
 static av_cold int init(AVFilterContext *ctx)
@@ -498,6 +623,7 @@  static av_cold int init(AVFilterContext *ctx)
             s->uninit           = entry->uninit;
             s->end_frame_filter = entry->end_frame_filter;
             s->postprocess      = entry->postprocess;
+            s->preprocess       = entry->preprocess;
 
             if (!(s->priv = av_mallocz(entry->priv_size)))
                 return AVERROR(ENOMEM);
@@ -538,12 +664,21 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         out = in;
     }
 
+    if (s->preprocess) {
+        s->preprocess(ctx, in);
+    }
+
     fill_iplimage_from_frame(&inimg , in , inlink->format);
 
     if (strcmp(s->name, "facedetect")) {
         fill_iplimage_from_frame(&outimg, out, inlink->format);
-        s->end_frame_filter(ctx, &inimg, &outimg);
-        fill_frame_from_iplimage(out, &outimg, inlink->format);
+        if (strcmp(s->name, "drawbox")) {
+            s->end_frame_filter(ctx, &inimg, &outimg);
+            fill_frame_from_iplimage(out, &outimg, inlink->format);
+        } else {
+            s->end_frame_filter(ctx, &inimg, NULL);
+            fill_frame_from_iplimage(out, &inimg, inlink->format);
+        }
     } else {
         s->end_frame_filter(ctx, &inimg, NULL);
     }