* [FFmpeg-devel] [PATCH] avfilter: add minvar video filter
@ 2022-12-08 17:56 Paul B Mahol
0 siblings, 0 replies; only message in thread
From: Paul B Mahol @ 2022-12-08 17:56 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1: Type: text/plain, Size: 38 bytes --]
Patch attached.
TODO: documentation.
[-- Attachment #2: 0001-avfilter-add-minvar-video-filter.patch --]
[-- Type: text/x-patch, Size: 16644 bytes --]
From 85063fe1d9f1fb33670068f7d3bb9038e22f2f75 Mon Sep 17 00:00:00 2001
From: Paul B Mahol <onemda@gmail.com>
Date: Wed, 7 Dec 2022 22:09:59 +0100
Subject: [PATCH] avfilter: add minvar video filter
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_minvar.c | 330 +++++++++++++++++++++++++++++++++++++++
3 files changed, 332 insertions(+)
create mode 100644 libavfilter/vf_minvar.c
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 9a850d5dc5..321bd9a0c8 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -377,6 +377,7 @@ OBJS-$(CONFIG_MESTIMATE_FILTER) += vf_mestimate.o motion_estimation
OBJS-$(CONFIG_METADATA_FILTER) += f_metadata.o
OBJS-$(CONFIG_MIDEQUALIZER_FILTER) += vf_midequalizer.o framesync.o
OBJS-$(CONFIG_MINTERPOLATE_FILTER) += vf_minterpolate.o motion_estimation.o
+OBJS-$(CONFIG_MINVAR_FILTER) += vf_minvar.o
OBJS-$(CONFIG_MIX_FILTER) += vf_mix.o framesync.o
OBJS-$(CONFIG_MONOCHROME_FILTER) += vf_monochrome.o
OBJS-$(CONFIG_MORPHO_FILTER) += vf_morpho.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 2ece5c15c8..a29dff0bdf 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -355,6 +355,7 @@ extern const AVFilter ff_vf_mestimate;
extern const AVFilter ff_vf_metadata;
extern const AVFilter ff_vf_midequalizer;
extern const AVFilter ff_vf_minterpolate;
+extern const AVFilter ff_vf_minvar;
extern const AVFilter ff_vf_mix;
extern const AVFilter ff_vf_monochrome;
extern const AVFilter ff_vf_morpho;
diff --git a/libavfilter/vf_minvar.c b/libavfilter/vf_minvar.c
new file mode 100644
index 0000000000..55ec98e133
--- /dev/null
+++ b/libavfilter/vf_minvar.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2022 Paul B Mahol
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct MinVarContext {
+ const AVClass *class;
+
+ double variance;
+ int sizew;
+ int sizeh;
+ int planes;
+
+ int depth;
+ int max;
+ int planewidth[4];
+ int planeheight[4];
+
+ uint64_t *ii[4];
+ uint64_t *i2[4];
+ ptrdiff_t i_linesize[4];
+ size_t i_size[4];
+
+ int nb_planes;
+
+ void (*compute_sat)(const uint8_t *ssrc,
+ int linesize,
+ int w, int h,
+ int max,
+ uint64_t *ii,
+ uint64_t *i2,
+ int i_linesize);
+ int (*filter)(AVFilterContext *ctx, void *arg,
+ int jobnr, int nb_jobs);
+} MinVarContext;
+
+#define OFFSET(x) offsetof(MinVarContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
+
+static const AVOption minvar_options[] = {
+ { "var", "set global noise variance", OFFSET(variance), AV_OPT_TYPE_DOUBLE,{.dbl=0}, 0, 16, FLAGS },
+ { "rw", "set horizontal patch size", OFFSET(sizew), AV_OPT_TYPE_INT, {.i64=5}, 1, 16, FLAGS },
+ { "rh", "set vertical patch size", OFFSET(sizeh), AV_OPT_TYPE_INT, {.i64=5}, 1, 16, FLAGS },
+ { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 0xF, FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(minvar);
+
+typedef struct ThreadData {
+ AVFrame *in, *out;
+} ThreadData;
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ MinVarContext *s = ctx->priv;
+
+ for (int p = 0; p < 4; p++) {
+ av_freep(&s->ii[p]);
+ av_freep(&s->i2[p]);
+ }
+}
+
+#define COMPUTE_SAT(type, depth) \
+static void compute_sat##depth(const uint8_t *ssrc, \
+ int linesize, \
+ int w, int h, \
+ int max, \
+ uint64_t *ii, \
+ uint64_t *i2, \
+ int i_linesize) \
+{ \
+ const type *src = (const type *)ssrc; \
+ \
+ linesize /= sizeof(type); \
+ ii += i_linesize; \
+ i2 += i_linesize; \
+ \
+ for (int y = 0; y < h; y++) { \
+ uint64_t sum2 = 0; \
+ uint64_t sum = 0; \
+ \
+ for (int x = 1; x < w; x++) { \
+ const int v = src[x-1]; \
+ sum += v; \
+ sum2 += v*v; \
+ ii[x] = sum + ii[x - i_linesize]; \
+ i2[x] = sum2 + i2[x - i_linesize]; \
+ } \
+ \
+ src += linesize; \
+ ii += i_linesize; \
+ i2 += i_linesize; \
+ } \
+}
+
+COMPUTE_SAT(uint8_t, 8)
+COMPUTE_SAT(uint16_t, 16)
+
+#define SQR(x) ((x) * (x))
+
+#define FILTER(type, ftype, ddepth, LRINT) \
+static int filter##ddepth(AVFilterContext *ctx, \
+ void *arg, \
+ int jobnr, int nb_jobs) \
+{ \
+ MinVarContext *s = ctx->priv; \
+ const int depth = s->depth; \
+ const ftype gvar = SQR(s->variance * ((1 << (depth - 8))) - 1); \
+ ThreadData *td = arg; \
+ AVFrame *out = td->out; \
+ AVFrame *in = td->in; \
+ \
+ for (int plane = 0; plane < s->nb_planes; plane++) { \
+ const ptrdiff_t slinesize = in->linesize[plane] / sizeof(type); \
+ const ptrdiff_t linesize = out->linesize[plane] / sizeof(type); \
+ const int height = s->planeheight[plane]; \
+ const int width = s->planewidth[plane]; \
+ const int slice_start = (height * jobnr) / nb_jobs; \
+ const int slice_end = (height * (jobnr+1)) / nb_jobs; \
+ const int i_linesize = s->i_linesize[plane]; \
+ const int sizeh = s->sizeh; \
+ const int sizew = s->sizew; \
+ const uint64_t *ii = s->ii[plane] + i_linesize; \
+ const uint64_t *i2 = s->i2[plane] + i_linesize; \
+ const type *src = ((const type *)in->data[plane]) + slice_start * slinesize; \
+ type *dst = ((type *)out->data[plane]) + slice_start * linesize; \
+ \
+ if (!(s->planes & (1 << plane))) \
+ continue; \
+ for (int y = slice_start; y < slice_end; y++) { \
+ const int t = FFMIN(sizeh, y); \
+ const int b = FFMIN(sizeh, height - y - 1); \
+ const int tb = t+b; \
+ const int yb = (y+b) * i_linesize; \
+ const int yt = (y-t) * i_linesize; \
+ \
+ for (int x = 0; x < width; x++) { \
+ const int l = FFMIN(sizew, x); \
+ const int r = FFMIN(sizew, width - x - 1); \
+ const int xl = x - l; \
+ const int xr = x + r; \
+ uint64_t tl2 = i2[yt + xl]; \
+ uint64_t tr2 = i2[yt + xr]; \
+ uint64_t bl2 = i2[yb + xl]; \
+ uint64_t br2 = i2[yb + xr]; \
+ uint64_t tl = ii[yt + xl]; \
+ uint64_t tr = ii[yt + xr]; \
+ uint64_t bl = ii[yb + xl]; \
+ uint64_t br = ii[yb + xr]; \
+ const ftype scale = 1.f / ((l + r) * tb); \
+ ftype I2 = br2 - bl2 - tr2 + tl2; \
+ ftype I1 = br - bl - tr + tl; \
+ const ftype mean = I1 * scale; \
+ const ftype lvar = I2 * scale - mean * mean; \
+ \
+ if (lvar > gvar) \
+ dst[x] = av_clip_uintp2_c(LRINT(src[x] + ((gvar / lvar) * (mean - src[x]))), depth); \
+ else \
+ dst[x] = mean; \
+ } \
+ \
+ src += slinesize; \
+ dst += linesize; \
+ } \
+ } \
+ \
+ return 0; \
+}
+
+FILTER(uint8_t, float, 8, lrintf)
+FILTER(uint16_t, double, 16, lrint)
+
+static int config_input(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+ MinVarContext *s = ctx->priv;
+
+ uninit(ctx);
+
+ s->depth = desc->comp[0].depth;
+ s->max = (1 << s->depth) - 1;
+ s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
+ s->planewidth[0] = s->planewidth[3] = inlink->w;
+ s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
+ s->planeheight[0] = s->planeheight[3] = inlink->h;
+
+ s->compute_sat = s->depth <= 8 ? compute_sat8 : compute_sat16;
+ s->filter = s->depth <= 8 ? filter8 : filter16;
+ s->nb_planes = av_pix_fmt_count_planes(inlink->format);
+
+ for (int p = 0; p < s->nb_planes; p++) {
+ s->i_linesize[p] = (s->planewidth[p] + 1);
+ s->i_size[p] = s->i_linesize[p] * (s->planeheight[p] + 1);
+ s->ii[p] = av_calloc(s->i_size[p], sizeof(*s->ii[0]));
+ s->i2[p] = av_calloc(s->i_size[p], sizeof(*s->i2[0]));
+ if (!s->ii[p] || !s->i2[p])
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
+static const enum AVPixelFormat pix_fmts[] = {
+ AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
+ AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
+ AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
+ AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
+ AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
+ AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
+ AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
+ AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
+ AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
+ AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
+ AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
+ AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
+ AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
+ AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
+ AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
+ AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
+ AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
+ AV_PIX_FMT_NONE
+};
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ MinVarContext *s = ctx->priv;
+ AVFilterLink *outlink = ctx->outputs[0];
+ ThreadData td;
+ AVFrame *out;
+
+ if (av_frame_is_writable(in)) {
+ out = in;
+ } else {
+ out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+ if (!out) {
+ av_frame_free(&in);
+ return AVERROR(ENOMEM);
+ }
+ av_frame_copy_props(out, in);
+ }
+
+ for (int plane = 0; plane < s->nb_planes; plane++) {
+ const int height = s->planeheight[plane];
+ const int i_linesize = s->i_linesize[plane];
+ const int width = s->planewidth[plane];
+ uint64_t *ii = s->ii[plane];
+ uint64_t *i2 = s->i2[plane];
+
+ if (!(s->planes & (1 << plane))) {
+ if (out != in)
+ av_image_copy_plane(out->data[plane], out->linesize[plane],
+ in->data[plane], in->linesize[plane],
+ width * ((s->depth + 7) / 8), height);
+ continue;
+ }
+
+ s->compute_sat(in->data[plane], in->linesize[plane],
+ width, height, s->max,
+ ii, i2, i_linesize);
+ }
+
+ td.in = in;
+ td.out = out;
+
+ ff_filter_execute(ctx, s->filter, &td, NULL,
+ FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
+
+ if (out != in)
+ av_frame_free(&in);
+ return ff_filter_frame(outlink, out);
+}
+
+static const AVFilterPad minvar_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_input,
+ .filter_frame = filter_frame,
+ },
+};
+
+static const AVFilterPad minvar_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ },
+};
+
+const AVFilter ff_vf_minvar = {
+ .name = "minvar",
+ .description = NULL_IF_CONFIG_SMALL("Apply Minimum Variance filter."),
+ .priv_size = sizeof(MinVarContext),
+ .priv_class = &minvar_class,
+ .uninit = uninit,
+ FILTER_INPUTS(minvar_inputs),
+ FILTER_OUTPUTS(minvar_outputs),
+ FILTER_PIXFMTS_ARRAY(pix_fmts),
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
+ AVFILTER_FLAG_SLICE_THREADS,
+ .process_command = ff_filter_process_command,
+};
--
2.37.2
[-- Attachment #3: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-12-08 17:56 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-08 17:56 [FFmpeg-devel] [PATCH] avfilter: add minvar video filter Paul B Mahol
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git