[FFmpeg-cvslog] avfilter: Add blockdetect filter

Thilo Borgmann git at videolan.org
Tue May 24 12:22:12 EEST 2022


ffmpeg | branch: master | Thilo Borgmann <thilo.borgmann at mail.de> | Tue May 24 11:20:14 2022 +0200| [9cb9da62a3591f916cb9e673ae29f924e5c7659f] | committer: Thilo Borgmann

avfilter: Add blockdetect filter

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=9cb9da62a3591f916cb9e673ae29f924e5c7659f
---

 Changelog                                    |   2 +-
 doc/filters.texi                             |  29 +++
 libavfilter/Makefile                         |   1 +
 libavfilter/allfilters.c                     |   1 +
 libavfilter/version.h                        |   2 +-
 libavfilter/vf_blockdetect.c                 | 294 +++++++++++++++++++++++++++
 tests/fate/filter-video.mak                  |   3 +
 tests/ref/fate/filter-refcmp-blockdetect-yuv |  10 +
 8 files changed, 340 insertions(+), 2 deletions(-)

diff --git a/Changelog b/Changelog
index 4d42a0f681..7c485d27d6 100644
--- a/Changelog
+++ b/Changelog
@@ -13,9 +13,9 @@ version 5.1:
 - pixelize video filter
 - colormap video filter
 - colorchart video source filter
-- blurdetect filter
 - multiply video filter
 - PGS subtitle frame merge bitstream filter
+- blurdetect filter
 
 
 version 5.0:
diff --git a/doc/filters.texi b/doc/filters.texi
index c243caee20..0e10946cca 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -8131,6 +8131,35 @@ tblend=all_mode=grainextract
 @subsection Commands
 This filter supports same @ref{commands} as options.
 
+ at anchor{blockdetect}
+ at section blockdetect
+
+Determines blockiness of frames without altering the input frames.
+
+Based on Remco Muijs and Ihor Kirenko: "A no-reference blocking artifact measure for adaptive video processing." 2005 13th European signal processing conference.
+
+The filter accepts the following options:
+
+ at table @option
+ at item period_min
+ at item period_max
+Set minimum and maximum values for determining pixel grids (periods).
+Default values are [3,24].
+
+ at item planes
+Set planes to filter. Default is first only.
+ at end table
+
+ at subsection Examples
+
+ at itemize
+ at item
+Determine blockiness for the first plane and search for periods within [8,32]:
+ at example
+blockdetect=period_min=8:period_max=32:planes=1
+ at end example
+ at end itemize
+
 @anchor{blurdetect}
 @section blurdetect
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 78ccfa37d3..e0e4d0de2c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -197,6 +197,7 @@ OBJS-$(CONFIG_BLACKDETECT_FILTER)            += vf_blackdetect.o
 OBJS-$(CONFIG_BLACKFRAME_FILTER)             += vf_blackframe.o
 OBJS-$(CONFIG_BLEND_FILTER)                  += vf_blend.o framesync.o
 OBJS-$(CONFIG_BLEND_VULKAN_FILTER)           += vf_blend_vulkan.o framesync.o vulkan.o vulkan_filter.o
+OBJS-$(CONFIG_BLOCKDETECT_FILTER)            += vf_blockdetect.o
 OBJS-$(CONFIG_BLURDETECT_FILTER)             += vf_blurdetect.o edge_common.o
 OBJS-$(CONFIG_BM3D_FILTER)                   += vf_bm3d.o framesync.o
 OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o boxblur.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 1946e0a51e..2409964e53 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -183,6 +183,7 @@ extern const AVFilter ff_vf_blackdetect;
 extern const AVFilter ff_vf_blackframe;
 extern const AVFilter ff_vf_blend;
 extern const AVFilter ff_vf_blend_vulkan;
+extern const AVFilter ff_vf_blockdetect;
 extern const AVFilter ff_vf_blurdetect;
 extern const AVFilter ff_vf_bm3d;
 extern const AVFilter ff_vf_boxblur;
diff --git a/libavfilter/version.h b/libavfilter/version.h
index c7ef9f25d7..df23710223 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
 
 #include "version_major.h"
 
-#define LIBAVFILTER_VERSION_MINOR  38
+#define LIBAVFILTER_VERSION_MINOR  39
 #define LIBAVFILTER_VERSION_MICRO 100
 
 
diff --git a/libavfilter/vf_blockdetect.c b/libavfilter/vf_blockdetect.c
new file mode 100644
index 0000000000..624dcfdeda
--- /dev/null
+++ b/libavfilter/vf_blockdetect.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2021 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * No-reference blockdetect filter
+ *
+ * Implementing:
+ * Remco Muijs and Ihor Kirenko: "A no-reference blocking artifact measure for adaptive video processing." 2005 13th European signal processing conference. IEEE, 2005.
+ * http://www.eurasip.org/Proceedings/Eusipco/Eusipco2005/defevent/papers/cr1042.pdf
+ *
+ * @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+
+typedef struct BLKContext {
+    const AVClass *class;
+
+    int hsub, vsub;
+    int nb_planes;
+
+    int period_min;    // minimum period to search for
+    int period_max;    // maximum period to search for
+    int planes;        // number of planes to filter
+
+    double block_total;
+    uint64_t nb_frames;
+
+    float *gradients;
+} BLKContext;
+
+#define OFFSET(x) offsetof(BLKContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+static const AVOption blockdetect_options[] = {
+    { "period_min", "Minimum period to search for", OFFSET(period_min), AV_OPT_TYPE_INT, {.i64=3}, 2, 32, FLAGS},
+    { "period_max", "Maximum period to search for", OFFSET(period_max), AV_OPT_TYPE_INT, {.i64=24}, 2, 64, FLAGS},
+    { "planes",        "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1}, 0, 15, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(blockdetect);
+
+static av_cold int blockdetect_init(AVFilterContext *ctx)
+{
+    return 0;
+}
+
+static int blockdetect_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    BLKContext      *s   = ctx->priv;
+    const int bufsize    = inlink->w * inlink->h;
+    const AVPixFmtDescriptor *pix_desc;
+
+    pix_desc = av_pix_fmt_desc_get(inlink->format);
+    s->hsub = pix_desc->log2_chroma_w;
+    s->vsub = pix_desc->log2_chroma_h;
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
+
+    s->gradients = av_calloc(bufsize, sizeof(*s->gradients));
+
+    if (!s->gradients)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static float calculate_blockiness(BLKContext *s, int w, int h,
+                                  float *grad, int grad_linesize,
+                                  uint8_t* src, int src_linesize)
+{
+    float block = 0.0f;
+    float nonblock = 0.0f;
+    int block_count = 0;
+    int nonblock_count = 0;
+    float ret = 0;
+
+    // Calculate BS in horizontal and vertical directions according to (1)(2)(3).
+    // Also try to find integer pixel periods (grids) even for scaled images.
+    // In case of fractional periods, FFMAX of current and neighbor pixels
+    // can help improve the correlation with MQS.
+    // Skip linear correction term (4)(5), as it appears only valid for their own test samples.
+
+    // horizontal blockiness (fixed width)
+    for (int j = 1; j < h; j++) {
+        for (int i = 3; i < w - 4; i++) {
+            float temp = 0.0f;
+            grad[j * grad_linesize + i] =
+                    abs(src[j * src_linesize + i + 0] - src[j * src_linesize + i + 1]);
+            temp += abs(src[j * src_linesize + i + 1] - src[j * src_linesize + i + 2]);
+            temp += abs(src[j * src_linesize + i + 2] - src[j * src_linesize + i + 3]);
+            temp += abs(src[j * src_linesize + i + 3] - src[j * src_linesize + i + 4]);
+            temp += abs(src[j * src_linesize + i - 0] - src[j * src_linesize + i - 1]);
+            temp += abs(src[j * src_linesize + i - 1] - src[j * src_linesize + i - 2]);
+            temp += abs(src[j * src_linesize + i - 2] - src[j * src_linesize + i - 3]);
+            temp = FFMAX(1, temp);
+            grad[j * grad_linesize + i] /= temp;
+
+            // use first row to store acculated results
+            grad[i] += grad[j * grad_linesize + i];
+        }
+    }
+
+    // find horizontal period
+    for (int period = s->period_min; period < s->period_max + 1; period++) {
+        float temp;
+        block = 0;
+        nonblock = 0;
+        block_count = 0;
+        nonblock_count = 0;
+        for (int i = 3; i < w - 4; i++) {
+            if ((i % period) == (period - 1)) {
+                block += FFMAX(FFMAX(grad[i + 0], grad[i + 1]), grad[i - 1]);
+                block_count++;
+            } else {
+                nonblock += grad[i];
+                nonblock_count++;
+            }
+        }
+        temp = (block / block_count) / (nonblock / nonblock_count);
+        ret = FFMAX(ret, temp);
+    }
+
+    // vertical blockiness (fixed height)
+    block_count = 0;
+    for (int j = 3; j < h - 4; j++) {
+        for (int i = 1; i < w; i++) {
+            float temp = 0.0f;
+            grad[j * grad_linesize + i] =
+                    abs(src[(j + 0) * src_linesize + i] - src[(j + 1) * src_linesize + i]);
+            temp += abs(src[(j + 1) * src_linesize + i] - src[(j + 2) * src_linesize + i]);
+            temp += abs(src[(j + 2) * src_linesize + i] - src[(j + 3) * src_linesize + i]);
+            temp += abs(src[(j + 3) * src_linesize + i] - src[(j + 4) * src_linesize + i]);
+            temp += abs(src[(j - 0) * src_linesize + i] - src[(j - 1) * src_linesize + i]);
+            temp += abs(src[(j - 1) * src_linesize + i] - src[(j - 2) * src_linesize + i]);
+            temp += abs(src[(j - 2) * src_linesize + i] - src[(j - 3) * src_linesize + i]);
+            temp = FFMAX(1, temp);
+            grad[j * grad_linesize + i] /= temp;
+
+            // use first column to store accumulated results
+            grad[j * grad_linesize] += grad[j * grad_linesize + i];
+        }
+    }
+
+    // find vertical period
+    for (int period = s->period_min; period < s->period_max + 1; period++) {
+        float temp;
+        block = 0;
+        nonblock = 0;
+        block_count = 0;
+        nonblock_count = 0;
+        for (int j = 3; j < h - 4; j++) {
+            if ((j % period) == (period - 1)) {
+                block += FFMAX(FFMAX(grad[(j + 0) * grad_linesize],
+                                     grad[(j + 1) * grad_linesize]),
+                                     grad[(j - 1) * grad_linesize]);
+                block_count++;
+            } else {
+                nonblock += grad[j * grad_linesize];
+                nonblock_count++;
+            }
+        }
+        temp = (block / block_count) / (nonblock / nonblock_count);
+        ret = FFMAX(ret, temp);
+    }
+
+    // return highest value of horz||vert
+    return ret;
+}
+
+static void set_meta(AVDictionary **metadata, const char *key, float d)
+{
+    char value[128];
+    snprintf(value, sizeof(value), "%f", d);
+    av_dict_set(metadata, key, value, 0);
+}
+
+static int blockdetect_filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    BLKContext *s         = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+
+    const int inw = inlink->w;
+    const int inh = inlink->h;
+
+    float *gradients   = s->gradients;
+
+    float block = 0.0f;
+    int nplanes = 0;
+    AVDictionary **metadata;
+    metadata = &in->metadata;
+
+    for (int plane = 0; plane < s->nb_planes; plane++) {
+        int hsub = plane == 1 || plane == 2 ? s->hsub : 0;
+        int vsub = plane == 1 || plane == 2 ? s->vsub : 0;
+        int w = AV_CEIL_RSHIFT(inw, hsub);
+        int h = AV_CEIL_RSHIFT(inh, vsub);
+
+        if (!((1 << plane) & s->planes))
+            continue;
+
+        nplanes++;
+
+        block += calculate_blockiness(s, w, h, gradients, w, in->data[plane], in->linesize[plane]);
+    }
+
+    if (nplanes)
+        block /= nplanes;
+
+    s->block_total += block;
+
+    // write stats
+    av_log(ctx, AV_LOG_VERBOSE, "block: %.7f\n", block);
+
+    set_meta(metadata, "lavfi.block", block);
+
+    s->nb_frames = inlink->frame_count_in;
+
+    return ff_filter_frame(outlink, in);
+}
+
+static av_cold void blockdetect_uninit(AVFilterContext *ctx)
+{
+    BLKContext *s = ctx->priv;
+
+    if (s->nb_frames > 0) {
+        av_log(ctx, AV_LOG_INFO, "block mean: %.7f\n",
+               s->block_total / s->nb_frames);
+    }
+
+    av_freep(&s->gradients);
+}
+
+static const enum AVPixelFormat pix_fmts[] = {
+    AV_PIX_FMT_GRAY8,
+    AV_PIX_FMT_GBRP,     AV_PIX_FMT_GBRAP,
+    AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
+    AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV440P,
+    AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,
+    AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P,
+    AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,
+    AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
+    AV_PIX_FMT_NONE
+};
+
+static const AVFilterPad blockdetect_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = blockdetect_config_input,
+        .filter_frame = blockdetect_filter_frame,
+    },
+};
+
+static const AVFilterPad blockdetect_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+};
+
+const AVFilter ff_vf_blockdetect = {
+    .name          = "blockdetect",
+    .description   = NULL_IF_CONFIG_SMALL("Blockdetect filter."),
+    .priv_size     = sizeof(BLKContext),
+    .init          = blockdetect_init,
+    .uninit        = blockdetect_uninit,
+    FILTER_PIXFMTS_ARRAY(pix_fmts),
+    FILTER_INPUTS(blockdetect_inputs),
+    FILTER_OUTPUTS(blockdetect_outputs),
+    .priv_class    = &blockdetect_class,
+    .flags         = AVFILTER_FLAG_METADATA_ONLY,
+};
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 97831bdf39..faed832cd4 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -687,6 +687,9 @@ fate-filter-metadata-avf-aphase-meter-out-of-phase: CMD = run $(FILTER_METADATA_
 FATE_FILTER_SAMPLES-$(call TRANSCODE, RAWVIDEO H264, MOV, ARESAMPLE_FILTER  AAC_FIXED_DECODER) += fate-filter-meta-4560-rotate0
 fate-filter-meta-4560-rotate0: CMD = transcode mov $(TARGET_SAMPLES)/filter/sample-in-issue-505.mov mov "-c copy -metadata:s:v:0 rotate=0" "-af aresample" "" "" "-flags +bitexact -c:a aac_fixed"
 
+FATE_FILTER_CMP_METADATA-$(CONFIG_BLOCKDETECT_FILTER) += fate-filter-refcmp-blockdetect-yuv
+fate-filter-refcmp-blockdetect-yuv: CMD = cmp_metadata blockdetect yuv420p 0.015
+
 FATE_FILTER_CMP_METADATA-$(CONFIG_BLURDETECT_FILTER) += fate-filter-refcmp-blurdetect-yuv
 fate-filter-refcmp-blurdetect-yuv: CMD = cmp_metadata blurdetect yuv420p 0.015
 
diff --git a/tests/ref/fate/filter-refcmp-blockdetect-yuv b/tests/ref/fate/filter-refcmp-blockdetect-yuv
new file mode 100644
index 0000000000..c3b3a3644f
--- /dev/null
+++ b/tests/ref/fate/filter-refcmp-blockdetect-yuv
@@ -0,0 +1,10 @@
+frame:0    pts:0       pts_time:0
+lavfi.block=46.592525
+frame:1    pts:1       pts_time:1
+lavfi.block=40.478703
+frame:2    pts:2       pts_time:2
+lavfi.block=40.858681
+frame:3    pts:3       pts_time:3
+lavfi.block=39.519077
+frame:4    pts:4       pts_time:4
+lavfi.block=38.713215



More information about the ffmpeg-cvslog mailing list