diff mbox series

[FFmpeg-devel] avfilter/vf_decimate: Improve decimation factor precision

Message ID CAF96VJ5ZWL9KfRT1uL46fOCAuLJLuS95jiNi5Mtat95g+1nVxQ@mail.gmail.com
State New
Headers show
Series [FFmpeg-devel] avfilter/vf_decimate: Improve decimation factor precision | expand

Checks

Context Check Description
yinshiyou/configure_loongarch64 warning Failed to apply patch

Commit Message

Vladimir Petrov Oct. 3, 2023, 12:26 p.m. UTC
Currently, decimate video filter supports dropping of only single frame per
cycle, limiting the range of available framerate decimation factors. Now,
adding a new option 'dropcount' allows increasing of drop count, so more
fractional factors could be chosen. Also added an option 'spread' to set
percentage of dropped frame metrics spreading to adjanced frames. Bigger
value of 'spread' option means that non-duplicate frames will be
distributed more evenly to output. Especially, this is useful in case of
static scenes to avoid skipping of large frame sequences, i.e. to avoid
jumpy motion. Added a new option 'compat' (compatibility with the old
behaviour / 2nd patch). When set to 'true', no metrics recalculation will
be done when marking a new frame to be dropped. In case of scene change
detection, the last one per batch will be prioritized, instead of the best
one. In mixed mode, checking for interlaced frames will be omited.
Compatibility option is intended to keep matching with the original VIVTC
VDecimate filter code. Rules to drop frames are as follow: First priority
drops will be duplicate frames, in ascending order by their 'maxbdiff'
metrics. If these are not enough and have scene change, then will be
dropped first frames of the new scene. If all these are insufficient, then
will drop some frames with less 'maxbdiff' metrics. The logic (with
'dropcount=1') corresponds to the current behaviour.

Examples below (both converts 29.97 fps 3:2 pulldown content mixed with
interlaced content to 23.976 fps video):

1. Deinterlace after decimation.
fieldmatch=order=tff:mode=pcn:field=top:combmatch=full,decimate=mixed=1,bwdif=parity=tff:deint=1,fps=30000/1001

2. Deinterlace before decimation.
fieldmatch=order=tff:mode=pcn:field=top:combmatch=full,bwdif=parity=tff:deint=1,fps=60000/1001,decimate=mixed=0:cycle=10:dropcount=6:compat=0
diff mbox series

Patch

From bdd5688d3c2811923895e31c886dd805e44c2ac6 Mon Sep 17 00:00:00 2001
From: Vladimir Petrov <vppetrovmms@gmail.com>
Date: Sat, 23 Sep 2023 17:41:58 +0300
Subject: [PATCH 2/2] avfilter/vf_decimate: Add compatibility option

Adds compatibility with the old behaviour.
Added a new option 'compat'. When set to 'true', no metrics
recalculation will be done when marking a new frame to be
dropped. In case of scene change detection, the last one
per batch will be prioritized, instead of the best one.
In mixed mode, checking for interlaced frames will be omited.

Signed-off-by: Vladimir Petrov <vppetrovmms@gmail.com>
---
 doc/filters.texi          |  9 +++++++++
 libavfilter/vf_decimate.c | 14 +++++++++-----
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index eef76d71b0..5954299713 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -11441,6 +11441,15 @@  Set whether or not chroma is considered in the metric calculations. Default is
 Set whether or not the input only partially contains content to be decimated.
 Default is @code{false}.
 If enabled video output stream will be in variable frame rate.
+
+@item compat
+Set compatibility with the old behaviour. When set to @code{true}, no metrics
+recalculation will be done when marking a new frame to be dropped. In case of
+scene change detection, the last one per batch will be prioritized, instead of
+the best one. In mixed mode, checking for interlaced frames will be omited.
+In order to get smoother motion and more accurate scene change detection,
+please set this to @code{false}.
+Default is @code{true}.
 @end table
 
 @section deconvolve
diff --git a/libavfilter/vf_decimate.c b/libavfilter/vf_decimate.c
index 67896eaa0d..5ab953f985 100644
--- a/libavfilter/vf_decimate.c
+++ b/libavfilter/vf_decimate.c
@@ -70,6 +70,7 @@  typedef struct DecimateContext {
     int ppsrc;
     int chroma;
     int mixed;
+    int compat;
 } DecimateContext;
 
 #define OFFSET(x) offsetof(DecimateContext, x)
@@ -86,6 +87,7 @@  static const AVOption decimate_options[] = {
     { "ppsrc",     "mark main input as a pre-processed input and activate clean source input stream", OFFSET(ppsrc), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     { "chroma",    "set whether or not chroma is considered in the metric calculations", OFFSET(chroma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
     { "mixed",     "set whether or not the input only partially contains content to be decimated", OFFSET(mixed), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
+    { "compat",    "set compatibility with old behaviour", OFFSET(compat), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
     { NULL }
 };
 
@@ -243,7 +245,7 @@  static int best_sc_idx(const DecimateContext* const dm)
 
     if (0 <= ret) {
         for (i = ret + 1; dm->cycle > i && dm->queue[i].frame; i++) {
-            if (!dm->queue[i].drop && dm->queue[i].totdiff > dm->queue[ret].totdiff)
+            if (!dm->queue[i].drop && dm->queue[i].totdiff > (dm->compat ? dm->scthresh : dm->queue[ret].totdiff))
                 ret = i;
         }
     }
@@ -266,7 +268,7 @@  static void update_frame_metrics(DecimateContext *dm, const int idx_p, const int
     if (!f1) {
         dm->queue[idx_nxt].maxbdiff = INT64_MAX;
         dm->queue[idx_nxt].totdiff  = INT64_MAX;
-    } else if (dm->mixed && ((AV_FRAME_FLAG_INTERLACED & f1->flags) || (f2 && (AV_FRAME_FLAG_INTERLACED & f2->flags)))) {
+    } else if (!dm->compat && dm->mixed && ((AV_FRAME_FLAG_INTERLACED & f1->flags) || (f2 && (AV_FRAME_FLAG_INTERLACED & f2->flags)))) {
         dm->queue[idx_nxt].maxbdiff = INT64_MAX - 1;
         dm->queue[idx_nxt].totdiff  = 0;
     } else if (f2) {
@@ -346,13 +348,14 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
                 } else {
                     idx_prv = prev_frame_idx(dm, idx_drop);
                     idx_nxt = next_frame_idx(dm, idx_drop);
-                    update_frame_metrics(dm, idx_prv, idx_nxt, idx_drop);
+                    if (!dm->compat)
+                        update_frame_metrics(dm, idx_prv, idx_nxt, idx_drop);
                     mark_drop_frame(dm, idx_drop, drop_case);
                 }
             }
         }
         av_frame_free(&dm->last);
-        idx_prv = prev_frame_idx(dm, dm->cycle);
+        idx_prv = dm->compat ? (dm->cycle - 1) : prev_frame_idx(dm, dm->cycle);
         if (0 <= idx_prv && dm->queue[idx_prv].frame)
             dm->last = av_frame_clone(dm->queue[idx_prv].frame);
     } else {
@@ -367,7 +370,8 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
                 } else {
                     idx_prv = prev_frame_idx(dm, idx_drop);
                     idx_nxt = next_frame_idx(dm, idx_drop);
-                    update_frame_metrics(dm, idx_prv, idx_nxt, idx_drop);
+                    if (!dm->compat)
+                        update_frame_metrics(dm, idx_prv, idx_nxt, idx_drop);
                     mark_drop_frame(dm, idx_drop, 4);
                 }
             }
-- 
2.39.2