From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <ffmpeg-devel-bounces@ffmpeg.org>
Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100])
	by master.gitmailbox.com (Postfix) with ESMTP id B5D174B28D
	for <ffmpegdev@gitmailbox.com>; Sun,  2 Jun 2024 20:17:41 +0000 (UTC)
Received: from [127.0.1.1] (localhost [127.0.0.1])
	by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2CCD868D627;
	Sun,  2 Jun 2024 23:17:39 +0300 (EEST)
Received: from mail-lj1-f180.google.com (mail-lj1-f180.google.com
 [209.85.208.180])
 by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A4D9C68D3EB
 for <ffmpeg-devel@ffmpeg.org>; Sun,  2 Jun 2024 23:17:32 +0300 (EEST)
Received: by mail-lj1-f180.google.com with SMTP id
 38308e7fff4ca-2eaaa3be0c5so803711fa.0
 for <ffmpeg-devel@ffmpeg.org>; Sun, 02 Jun 2024 13:17:32 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=gmail.com; s=20230601; t=1717359452; x=1717964252; darn=ffmpeg.org;
 h=thread-index:content-language:mime-version:message-id:date:subject
 :in-reply-to:references:to:from:from:to:cc:subject:date:message-id
 :reply-to; bh=W9Jj2PFFYiNwjtkZyRJ6D4AZQAblkNzjswX2MADZjvM=;
 b=G35eerd53j2Vnr6RLE8ZyaWOnYF+bP0kI12v496Lm7SI2h6/Y374TDdfIIUTqzcqHR
 nCZomxHUpEVaWz+pMbE+8orLny7/uJ2nzYmlMex96iGTzcPl/IPA7FBuMPkwF063+ZGn
 6IGSGCMrPSs9ILRR7tydKVU/zPIoIvLn6gdVvpAFLk60B78JpIwHCwBnLq1Ikdz9qKFB
 jxVaK4OMG0gTyjMPLT96SI0YjjU1C5NCG4SKroiu1b4+BWZP54U1cmMzGO8LGxF69Bs8
 LsAX+i3xCdvYUvvTQgYhj9TkR4x2ypNpUdll0WVqinW5SJ1D/WFYk2b70AeNhCsr+sTe
 y6kQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1717359452; x=1717964252;
 h=thread-index:content-language:mime-version:message-id:date:subject
 :in-reply-to:references:to:from:x-gm-message-state:from:to:cc
 :subject:date:message-id:reply-to;
 bh=W9Jj2PFFYiNwjtkZyRJ6D4AZQAblkNzjswX2MADZjvM=;
 b=r+yJnAkwT/nVsE5PMGSr5WquCaPndQCVDX5ndCJ4ck7RR+BmM7+ggeJrd+MXimG+e2
 ViC70NbSKn5BzFh91JJ1gjs5rfADKLZQbVgM291zqiOis1Xeckp6kulNmt/5xZzEJIRH
 9NayPdEuPbw1McJQx9U1HSOrmb6jSkDt09NOH+GVjLz8696NAUVU2nnktJvP3DmHJuEV
 RPaQNPLBMSvL1FrmZnJgAWUtja61dyMDWCcK1FfJrVeX3vbS3CM1FfP97Y7ed89fAae/
 H9PmCl7NuTzN5SoM8o38PlXZSEnDMRStSaQCY5m8rIpjUiCRU49hAIRKoXljXj1DZ6W/
 DG/A==
X-Gm-Message-State: AOJu0YzaoIRiFYM7rp3v9wdSBsyiH44sqtH+gFbac6N2Q5iW9fTJKHaG
 1NFEXA6gWBM1m956sG+DSi2Dkxt0xx86cXP6zfk4fK7YhnIj/23hD1N8BQ==
X-Google-Smtp-Source: AGHT+IGk4oe4tDL0WJxnXS2h31OlTMinC4/wwWviNsbbpFigL0dbhOg+WavUMMTlc9XqWpEPAV+JXA==
X-Received: by 2002:ac2:5b1e:0:b0:528:21dc:5364 with SMTP id
 2adb3069b0e04-52b89559b48mr4564554e87.1.1717359451327; 
 Sun, 02 Jun 2024 13:17:31 -0700 (PDT)
Received: from Mishu ([2a02:2f0f:8201:b100:a995:4a6c:2ae8:1977])
 by smtp.gmail.com with ESMTPSA id
 a640c23a62f3a-a67e73fb54esm381850766b.61.2024.06.02.13.17.30
 for <ffmpeg-devel@ffmpeg.org>
 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
 Sun, 02 Jun 2024 13:17:30 -0700 (PDT)
From: <radu.taraibuta@gmail.com>
To: "'FFmpeg development discussions and patches'" <ffmpeg-devel@ffmpeg.org>
References: <000c01daa54d$8a0eedf0$9e2cc9d0$@gmail.com>
 <20240530213132.GE2821752@pb2>
In-Reply-To: <20240530213132.GE2821752@pb2>
Date: Sun, 2 Jun 2024 23:17:29 +0300
Message-ID: <008e01dab529$e5510630$aff31290$@gmail.com>
MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="----=_NextPart_000_008F_01DAB543.0A9E3E30"
X-Mailer: Microsoft Outlook 16.0
Content-Language: ro
Thread-Index: AQF53sdsVM9iibY6Rm6BSu+PKGSxTgKF963BsmJlSxA=
Subject: Re: [FFmpeg-devel] [PATCH] area changed: scdet filter
X-BeenThere: ffmpeg-devel@ffmpeg.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org>
List-Unsubscribe: <https://ffmpeg.org/mailman/options/ffmpeg-devel>,
 <mailto:ffmpeg-devel-request@ffmpeg.org?subject=unsubscribe>
List-Archive: <https://ffmpeg.org/pipermail/ffmpeg-devel>
List-Post: <mailto:ffmpeg-devel@ffmpeg.org>
List-Help: <mailto:ffmpeg-devel-request@ffmpeg.org?subject=help>
List-Subscribe: <https://ffmpeg.org/mailman/listinfo/ffmpeg-devel>,
 <mailto:ffmpeg-devel-request@ffmpeg.org?subject=subscribe>
Reply-To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
Errors-To: ffmpeg-devel-bounces@ffmpeg.org
Sender: "ffmpeg-devel" <ffmpeg-devel-bounces@ffmpeg.org>
Archived-At: <https://master.gitmailbox.com/ffmpegdev/008e01dab529$e5510630$aff31290$@gmail.com/>
List-Archive: <https://master.gitmailbox.com/ffmpegdev/>
List-Post: <mailto:ffmpegdev@gitmailbox.com>

This is a multipart message in MIME format.

------=_NextPart_000_008F_01DAB543.0A9E3E30
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit



> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Michael Niedermayer
> Sent: vineri, 31 mai 2024 00:32
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH] area changed: scdet filter
> 
> On Mon, May 13, 2024 at 06:52:19PM +0300, radu.taraibuta@gmail.com
> wrote:
> > Previous observations:
> >
> >  - Inconsistent code style with other filters. (Mostly using
> > AVFilterLink* link instead of AVFilterLink *link).
> > I hope it's fine now.
> >
> >  - Unrelated changes, please split trivial unrelated changes into
> > separate patches.
> > Removed trivial changes from this patch.
> >
> >  - Can't tables be generated at .init/.config_props time? No point in
> > storing them into binary.
> > Done.
> >
> >  - Adding extra delay is not backward compatible change, it should be
> > implemented properly by adding option for users to select mode: next &
> > prev frame or just next or prev frame.
> > Added legacy option to the mode parameter.
> >
> >  - Could split frame clone change into earlier separate patch.
> > Cannot be done. It's either frame clone or 1 frame delay.
> >
> >  - Where are results of improvements with accuracy so it can be
confirmed?
> > Here are my test results with manual labeling of scene changes:
> > 2379	Full length movie
> >
> > Method	Threshold	TP	FP	FN		Precision
> > Recall	F
> > Cubic	7	2357	423	22		0.847841727
0.990752417
> > 0.913742973
> > Cubic	10	2297	200	82		0.919903885
0.965531736
> > 0.94216571
> > Cubic	12	2217	146	162		0.938214135
0.931904161
> > 0.935048503
> > Cubic	15	2049	101	330		0.953023256
0.861286255
> > 0.904835505
> > Linear	2.8	2357	1060	22		0.689786362
0.990752417
> > 0.813319531
> > Linear	8	2099	236	280		0.898929336
0.882303489
> > 0.890538821
> > Linear	10	1886	173	493		0.91597863
0.792770071
> > 0.849932402
> > Legacy	5	2235	1260	144		0.639484979
> 	0.939470366
> > 0.760980592
> > Legacy	8	1998	414	381		0.828358209
> 	0.839848676
> > 0.83406387
> > Legacy	10	1743	193	636		0.900309917
> 	0.732660782
> > 0.80787949
> >
> > 15	HDR10Plus_PB_EAC3JOC
> > https://mega.nz/file/nehDka6Z#C5_OPbSZkONdOp1jRmc09C9-
> viDc3zMj8ZHruHcW
> > KyA
> >
> > Method	Threshold	TP	FP	FN		Precision
> > Recall	F
> > Cubic	10	15	0	0		1	1	1
> > Linear	5	13	1	2		0.928571429
0.866666667
> > 0.896551724
> > Legacy	5	12	2	3		0.857142857	0.8
> > 0.827586207
> >
> > 21	(HDR HEVC 10-bit BT.2020 24fps) Exodus Sample
> >
> https://mega.nz/file/Sfw1hDpK#ErxCOpQDVjcI1gq6ZbX3vIfdtXZompkFe0jq47E
> h
> > R2o
> >
> > Method	Threshold	TP	FP	FN		Precision
> > Recall	F
> > Cubic	10	21	0	0		1	1	1
> > Linear	4	20	0	1		1	0.952380952
> > 0.975609756
> > Legacy	4	19	0	2		1	0.904761905
> 	0.95
> >
> > 94	Bieber Grammys
> > https://mega.nz/#!c9dhAaKA!MG5Yi-
> MJNATE2_KqcnNJZCRKtTWvdjJP1NwG8Ggdw3E
> >
> > Method	Threshold	TP	FP	FN		Precision
> > Recall	F
> > Cubic	15	91	23	3		0.798245614
0.968085106
> > 0.875
> > Cubic	18	85	9	9		0.904255319
0.904255319
> > 0.904255319
> > Linear	7	79	49	15		0.6171875
0.840425532
> > 0.711711712
> > Linear	8	74	28	20		0.725490196
0.787234043
> > 0.755102041
> > Legacy	7	74	40	20		0.649122807
> 	0.787234043
> > 0.711538462
> > Legacy	8	71	26	23		0.731958763
> 	0.755319149
> > 0.743455497
> >
> >
> > Improve scene detection accuracy by comparing frame with both previous
> > and next frame (creates one frame delay).
> > Add new mode parameter and new method to compute the frame difference
> > using cubic square to increase the weight of small changes and new mean
> formula.
> > This improves accuracy significantly. Slightly improve performance by
> > not using frame clone.
> > Add legacy mode for backward compatibility.
> >
> > Signed-off-by: raduct <radu.taraibuta@gmail.com>
> > ---
> >  doc/filters.texi            |  16 ++++
> >  libavfilter/scene_sad.c     | 151 ++++++++++++++++++++++++++++++++++
> >  libavfilter/scene_sad.h     |   6 ++
> >  libavfilter/vf_scdet.c      | 156 +++++++++++++++++++++++++-----------
> >  tests/fate/filter-video.mak |   3 +
> >  5 files changed, 284 insertions(+), 48 deletions(-)
> >
> > diff --git a/doc/filters.texi b/doc/filters.texi index
> > bfa8ccec8b..53814e003b 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -21797,6 +21797,22 @@ Default value is @code{10.}.
> >  @item sc_pass, s
> >  Set the flag to pass scene change frames to the next filter. Default
> > value is @code{0}
> 
> The patch is corrupted by linebreaks:
> 
> Applying: area changed: scdet filter
> error: corrupt patch at line 16
> Patch failed at 0001 area changed: scdet filter
> 
> please check the linebreak settings or attach the patch or use git
send-email
> 
> thx
> 
> [...]
> --
> Michael     GnuPG fingerprint:
> 9FF2128B147EF6730BADF133611EC787040B0FAB
> 
> Homeopathy is like voting while filling the ballot out with transparent
ink.
> Sometimes the outcome one wanted occurs. Rarely its worse than filling out
a
> ballot properly.

Please find attached the patch.


------=_NextPart_000_008F_01DAB543.0A9E3E30
Content-Type: application/octet-stream;
	name="0001-area-changed-scdet-filter.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="0001-area-changed-scdet-filter.patch"

>From 6d55c65d92376b0ab6e3bb2439af30fbcc430d0b Mon Sep 17 00:00:00 2001=0A=
From: raduct <radu.taraibuta@gmail.com>=0A=
Date: Wed, 8 May 2024 08:24:46 +0300=0A=
Subject: [PATCH] area changed: scdet filter=0A=
=0A=
Improve scene detection accuracy by comparing frame with both previous =
and next frame (creates one frame delay).=0A=
Add new mode parameter and new method to compute the frame difference =
using cubic square to increase the weight of small changes and new mean =
formula. This improves accuracy significantly. Slightly improve =
performance by not using frame clone.=0A=
Add legacy mode for backward compatibility.=0A=
=0A=
Signed-off-by: raduct <radu.taraibuta@gmail.com>=0A=
---=0A=
 doc/filters.texi            |  16 ++++=0A=
 libavfilter/scene_sad.c     | 151 ++++++++++++++++++++++++++++++++++=0A=
 libavfilter/scene_sad.h     |   6 ++=0A=
 libavfilter/vf_scdet.c      | 156 +++++++++++++++++++++++++-----------=0A=
 tests/fate/filter-video.mak |   3 +=0A=
 5 files changed, 284 insertions(+), 48 deletions(-)=0A=
=0A=
diff --git a/doc/filters.texi b/doc/filters.texi=0A=
index bfa8ccec8b..53814e003b 100644=0A=
--- a/doc/filters.texi=0A=
+++ b/doc/filters.texi=0A=
@@ -21797,6 +21797,22 @@ Default value is @code{10.}.=0A=
 @item sc_pass, s=0A=
 Set the flag to pass scene change frames to the next filter. Default =
value is @code{0}=0A=
 You can enable it if you want to get snapshot of scene change frames =
only.=0A=
+=0A=
+@item mode=0A=
+Set the scene change detection method. Default value is @code{-1}=0A=
+Available values are:=0A=
+=0A=
+@table @samp=0A=
+@item -1=0A=
+Legacy mode for sum of absolute linear differences. Compare frame with =
previous only and no delay.=0A=
+=0A=
+@item 0=0A=
+Sum of absolute linear differences. Compare frame with both previous =
and next which introduces a 1 frame delay.=0A=
+=0A=
+@item 1=0A=
+Sum of mean of cubic root differences. Compare frame with both previous =
and next which introduces a 1 frame delay.=0A=
+=0A=
+@end table=0A=
 @end table=0A=
 =0A=
 @anchor{selectivecolor}=0A=
diff --git a/libavfilter/scene_sad.c b/libavfilter/scene_sad.c=0A=
index caf911eb5d..9b80d426bc 100644=0A=
--- a/libavfilter/scene_sad.c=0A=
+++ b/libavfilter/scene_sad.c=0A=
@@ -21,6 +21,7 @@=0A=
  * Scene SAD functions=0A=
  */=0A=
 =0A=
+#include "libavutil/thread.h"=0A=
 #include "scene_sad.h"=0A=
 =0A=
 void ff_scene_sad16_c(SCENE_SAD_PARAMS)=0A=
@@ -71,3 +72,153 @@ ff_scene_sad_fn ff_scene_sad_get_fn(int depth)=0A=
     return sad;=0A=
 }=0A=
 =0A=
+static AVMutex cbrt_mutex =3D AV_MUTEX_INITIALIZER;=0A=
+static uint8_t *cbrt_table[16] =3D { NULL };=0A=
+static int cbrt_table_ref[16] =3D { 0 };=0A=
+=0A=
+int ff_init_cbrt(int bitdepth)=0A=
+{=0A=
+    if (bitdepth < 4 || bitdepth > 16)=0A=
+        return AVERROR(EINVAL);=0A=
+=0A=
+    ff_mutex_lock(&cbrt_mutex);=0A=
+=0A=
+    uint8_t *table =3D cbrt_table[bitdepth];=0A=
+    if (table) {=0A=
+        cbrt_table_ref[bitdepth]++;=0A=
+        goto end;=0A=
+    }=0A=
+=0A=
+    table =3D av_malloc((1 << bitdepth) * (bitdepth > 8 ? 2 : 1));=0A=
+    if (!table)=0A=
+        goto end;=0A=
+    cbrt_table[bitdepth] =3D table;=0A=
+    cbrt_table_ref[bitdepth] =3D 1;=0A=
+=0A=
+    int size =3D 1 << bitdepth;=0A=
+    double factor =3D pow(size - 1, 2. / 3.);=0A=
+    if (bitdepth <=3D 8) {=0A=
+        for (int i =3D 0; i < size; i++)=0A=
+            table[i] =3D round(factor * pow(i, 1. / 3.));=0A=
+    } else {=0A=
+        uint16_t *tablew =3D (uint16_t*)table;=0A=
+        for (int i =3D 0; i < size; i++)=0A=
+            tablew[i] =3D round(factor * pow(i, 1. / 3.));=0A=
+    }=0A=
+=0A=
+end:=0A=
+    ff_mutex_unlock(&cbrt_mutex);=0A=
+    return table !=3D NULL;=0A=
+}=0A=
+=0A=
+void ff_uninit_cbrt(int bitdepth)=0A=
+{=0A=
+    if (bitdepth < 4 || bitdepth > 16)=0A=
+        return;=0A=
+    ff_mutex_lock(&cbrt_mutex);=0A=
+    if (!--cbrt_table_ref[bitdepth]) {=0A=
+        av_free(cbrt_table[bitdepth]);=0A=
+        cbrt_table[bitdepth] =3D NULL;=0A=
+    }=0A=
+    ff_mutex_unlock(&cbrt_mutex);=0A=
+}=0A=
+=0A=
+void ff_scene_scrd_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    uint64_t scrdPlus =3D 0;=0A=
+    uint64_t scrdMinus =3D 0;=0A=
+    int x, y;=0A=
+=0A=
+    uint8_t *table =3D cbrt_table[8];=0A=
+    if (!table) {=0A=
+        *sum =3D 0;=0A=
+        return;=0A=
+    }=0A=
+=0A=
+    for (y =3D 0; y < height; y++) {=0A=
+        for (x =3D 0; x < width; x++)=0A=
+            if (src1[x] > src2[x])=0A=
+                scrdMinus +=3D table[src1[x] - src2[x]];=0A=
+            else=0A=
+                scrdPlus +=3D table[src2[x] - src1[x]];=0A=
+        src1 +=3D stride1;=0A=
+        src2 +=3D stride2;=0A=
+    }=0A=
+=0A=
+    double mean =3D (sqrt(scrdPlus) + sqrt(scrdMinus)) / 2.0;=0A=
+    *sum =3D 2.0 * mean * mean;=0A=
+}=0A=
+=0A=
+void ff_scene_scrd2B_c(SCENE_SAD_PARAMS, int bitdepth)=0A=
+{=0A=
+    uint64_t scrdPlus =3D 0;=0A=
+    uint64_t scrdMinus =3D 0;=0A=
+    const uint16_t *src1w =3D (const uint16_t*)src1;=0A=
+    const uint16_t *src2w =3D (const uint16_t*)src2;=0A=
+    int x, y;=0A=
+=0A=
+    uint16_t *table =3D (uint16_t*)cbrt_table[bitdepth];=0A=
+    if (!table) {=0A=
+        *sum =3D 0;=0A=
+        return;=0A=
+    }=0A=
+=0A=
+    stride1 /=3D 2;=0A=
+    stride2 /=3D 2;=0A=
+=0A=
+    for (y =3D 0; y < height; y++) {=0A=
+        for (x =3D 0; x < width; x++)=0A=
+            if (src1w[x] > src2w[x])=0A=
+                scrdMinus +=3D table[src1w[x] - src2w[x]];=0A=
+            else=0A=
+                scrdPlus +=3D table[src2w[x] - src1w[x]];=0A=
+        src1w +=3D stride1;=0A=
+        src2w +=3D stride2;=0A=
+    }=0A=
+=0A=
+    double mean =3D (sqrt(scrdPlus) + sqrt(scrdMinus)) / 2.0;=0A=
+    *sum =3D 2.0 * mean * mean;=0A=
+}=0A=
+=0A=
+void ff_scene_scrd9_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, =
9);=0A=
+}=0A=
+=0A=
+void ff_scene_scrd10_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, =
10);=0A=
+}=0A=
+=0A=
+void ff_scene_scrd12_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, =
12);=0A=
+}=0A=
+=0A=
+void ff_scene_scrd14_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, =
14);=0A=
+}=0A=
+=0A=
+void ff_scene_scrd16_c(SCENE_SAD_PARAMS)=0A=
+{=0A=
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, =
16);=0A=
+}=0A=
+=0A=
+ff_scene_sad_fn ff_scene_scrd_get_fn(int depth)=0A=
+{=0A=
+    ff_scene_sad_fn scrd =3D NULL;=0A=
+    if (depth =3D=3D 8)=0A=
+        scrd =3D ff_scene_scrd_c;=0A=
+    else if (depth =3D=3D 9)=0A=
+        scrd =3D ff_scene_scrd9_c;=0A=
+    else if (depth =3D=3D 10)=0A=
+        scrd =3D ff_scene_scrd10_c;=0A=
+    else if (depth =3D=3D 12)=0A=
+        scrd =3D ff_scene_scrd12_c;=0A=
+    else if (depth =3D=3D 14)=0A=
+        scrd =3D ff_scene_scrd14_c;=0A=
+    else if (depth =3D=3D 16)=0A=
+        scrd =3D ff_scene_scrd16_c;=0A=
+    return scrd;=0A=
+}=0A=
diff --git a/libavfilter/scene_sad.h b/libavfilter/scene_sad.h=0A=
index 173a051f2b..c294bd90f9 100644=0A=
--- a/libavfilter/scene_sad.h=0A=
+++ b/libavfilter/scene_sad.h=0A=
@@ -41,4 +41,10 @@ ff_scene_sad_fn ff_scene_sad_get_fn_x86(int depth);=0A=
 =0A=
 ff_scene_sad_fn ff_scene_sad_get_fn(int depth);=0A=
 =0A=
+ff_scene_sad_fn ff_scene_scrd_get_fn(int depth);=0A=
+=0A=
+int ff_init_cbrt(int bitdepth);=0A=
+=0A=
+void ff_uninit_cbrt(int bitdepth);=0A=
+=0A=
 #endif /* AVFILTER_SCENE_SAD_H */=0A=
diff --git a/libavfilter/vf_scdet.c b/libavfilter/vf_scdet.c=0A=
index 15399cfebf..93da5837b3 100644=0A=
--- a/libavfilter/vf_scdet.c=0A=
+++ b/libavfilter/vf_scdet.c=0A=
@@ -31,6 +31,18 @@=0A=
 #include "scene_sad.h"=0A=
 #include "video.h"=0A=
 =0A=
+enum SCDETMode {=0A=
+    MODE_LEGACY =3D -1,=0A=
+    MODE_LINEAR =3D 0,=0A=
+    MODE_MEAN_CBRT =3D 1=0A=
+};=0A=
+=0A=
+typedef struct SCDETFrameInfo {=0A=
+    AVFrame *picref;=0A=
+    double mafd;=0A=
+    double diff;=0A=
+} SCDETFrameInfo;=0A=
+=0A=
 typedef struct SCDetContext {=0A=
     const AVClass *class;=0A=
 =0A=
@@ -39,11 +51,12 @@ typedef struct SCDetContext {=0A=
     int nb_planes;=0A=
     int bitdepth;=0A=
     ff_scene_sad_fn sad;=0A=
-    double prev_mafd;=0A=
-    double scene_score;=0A=
-    AVFrame *prev_picref;=0A=
+    SCDETFrameInfo curr_frame;=0A=
+    SCDETFrameInfo prev_frame;=0A=
+=0A=
     double threshold;=0A=
     int sc_pass;=0A=
+    enum SCDETMode mode;=0A=
 } SCDetContext;=0A=
 =0A=
 #define OFFSET(x) offsetof(SCDetContext, x)=0A=
@@ -55,6 +68,7 @@ static const AVOption scdet_options[] =3D {=0A=
     { "t",           "set scene change detect threshold",        =
OFFSET(threshold),  AV_OPT_TYPE_DOUBLE,   {.dbl =3D 10.},     0,  100., =
V|F },=0A=
     { "sc_pass",     "Set the flag to pass scene change frames", =
OFFSET(sc_pass),    AV_OPT_TYPE_BOOL,     {.dbl =3D  0  },    0,    1,  =
V|F },=0A=
     { "s",           "Set the flag to pass scene change frames", =
OFFSET(sc_pass),    AV_OPT_TYPE_BOOL,     {.dbl =3D  0  },    0,    1,  =
V|F },=0A=
+    { "mode",        "scene change detection method",            =
OFFSET(mode),       AV_OPT_TYPE_INT,      {.i64 =3D MODE_LEGACY}, =
MODE_LEGACY, MODE_MEAN_CBRT, V|F },=0A=
     {NULL}=0A=
 };=0A=
 =0A=
@@ -91,7 +105,14 @@ static int config_input(AVFilterLink *inlink)=0A=
         s->height[plane] =3D inlink->h >> ((plane =3D=3D 1 || plane =
=3D=3D 2) ? desc->log2_chroma_h : 0);=0A=
     }=0A=
 =0A=
-    s->sad =3D ff_scene_sad_get_fn(s->bitdepth =3D=3D 8 ? 8 : 16);=0A=
+    if (s->mode =3D=3D MODE_LINEAR || s->mode =3D=3D MODE_LEGACY)=0A=
+        s->sad =3D ff_scene_sad_get_fn(s->bitdepth =3D=3D 8 ? 8 : 16);=0A=
+    else if (s->mode =3D=3D MODE_MEAN_CBRT) {=0A=
+        int ret =3D ff_init_cbrt(s->bitdepth);=0A=
+        if (ret < 0)=0A=
+            return ret;=0A=
+        s->sad =3D ff_scene_scrd_get_fn(s->bitdepth);=0A=
+    }=0A=
     if (!s->sad)=0A=
         return AVERROR(EINVAL);=0A=
 =0A=
@@ -101,46 +122,97 @@ static int config_input(AVFilterLink *inlink)=0A=
 static av_cold void uninit(AVFilterContext *ctx)=0A=
 {=0A=
     SCDetContext *s =3D ctx->priv;=0A=
-=0A=
-    av_frame_free(&s->prev_picref);=0A=
+    if (s->mode =3D=3D MODE_LEGACY)=0A=
+        av_frame_free(&s->prev_frame.picref);=0A=
+    if (s->mode =3D=3D MODE_MEAN_CBRT)=0A=
+        ff_uninit_cbrt(s->bitdepth);=0A=
 }=0A=
 =0A=
-static double get_scene_score(AVFilterContext *ctx, AVFrame *frame)=0A=
+static void compute_diff(AVFilterContext *ctx)=0A=
 {=0A=
-    double ret =3D 0;=0A=
     SCDetContext *s =3D ctx->priv;=0A=
-    AVFrame *prev_picref =3D s->prev_picref;=0A=
+    AVFrame *prev_picref =3D s->prev_frame.picref;=0A=
+    AVFrame *curr_picref =3D s->curr_frame.picref;=0A=
 =0A=
-    if (prev_picref && frame->height =3D=3D prev_picref->height=0A=
-                    && frame->width  =3D=3D prev_picref->width) {=0A=
-        uint64_t sad =3D 0;=0A=
-        double mafd, diff;=0A=
-        uint64_t count =3D 0;=0A=
+    if (prev_picref && curr_picref=0A=
+            && curr_picref->height =3D=3D prev_picref->height=0A=
+            && curr_picref->width  =3D=3D prev_picref->width) {=0A=
 =0A=
+        uint64_t sum =3D 0;=0A=
+        uint64_t count =3D 0;=0A=
         for (int plane =3D 0; plane < s->nb_planes; plane++) {=0A=
-            uint64_t plane_sad;=0A=
+            uint64_t plane_sum;=0A=
             s->sad(prev_picref->data[plane], =
prev_picref->linesize[plane],=0A=
-                    frame->data[plane], frame->linesize[plane],=0A=
-                    s->width[plane], s->height[plane], &plane_sad);=0A=
-            sad +=3D plane_sad;=0A=
+                    curr_picref->data[plane], =
curr_picref->linesize[plane],=0A=
+                    s->width[plane], s->height[plane], &plane_sum);=0A=
+            sum +=3D plane_sum;=0A=
             count +=3D s->width[plane] * s->height[plane];=0A=
         }=0A=
 =0A=
-        mafd =3D (double)sad * 100. / count / (1ULL << s->bitdepth);=0A=
-        diff =3D fabs(mafd - s->prev_mafd);=0A=
-        ret  =3D av_clipf(FFMIN(mafd, diff), 0, 100.);=0A=
-        s->prev_mafd =3D mafd;=0A=
-        av_frame_free(&prev_picref);=0A=
+        s->curr_frame.mafd =3D (double)sum * 100. / count / (1ULL << =
s->bitdepth);=0A=
+        if (s->mode =3D=3D MODE_LEGACY)=0A=
+            s->curr_frame.diff =3D fabs(s->curr_frame.mafd - =
s->prev_frame.mafd);=0A=
+        else=0A=
+            s->curr_frame.diff =3D s->curr_frame.mafd - =
s->prev_frame.mafd;=0A=
+    } else {=0A=
+        s->curr_frame.mafd =3D 0;=0A=
+        s->curr_frame.diff =3D 0;=0A=
     }=0A=
-    s->prev_picref =3D av_frame_clone(frame);=0A=
-    return ret;=0A=
 }=0A=
 =0A=
-static int set_meta(SCDetContext *s, AVFrame *frame, const char *key, =
const char *value)=0A=
+static int set_meta(AVFrame *frame, const char *key, const char *value)=0A=
 {=0A=
     return av_dict_set(&frame->metadata, key, value, 0);=0A=
 }=0A=
 =0A=
+static int filter_frame(AVFilterContext *ctx, AVFrame *frame)=0A=
+{=0A=
+    AVFilterLink *inlink =3D ctx->inputs[0];=0A=
+    AVFilterLink *outlink =3D ctx->outputs[0];=0A=
+    SCDetContext *s =3D ctx->priv;=0A=
+=0A=
+    s->prev_frame =3D s->curr_frame;=0A=
+    s->curr_frame.picref =3D frame;=0A=
+=0A=
+    if ((s->mode !=3D MODE_LEGACY && s->prev_frame.picref) || (s->mode =
=3D=3D MODE_LEGACY && frame !=3D NULL)) {=0A=
+        compute_diff(ctx);=0A=
+=0A=
+        if (s->mode =3D=3D MODE_LEGACY) {=0A=
+            av_frame_free(&s->prev_frame.picref);=0A=
+            s->prev_frame =3D s->curr_frame;=0A=
+            s->curr_frame.picref =3D =
av_frame_clone(s->curr_frame.picref);=0A=
+        } else if (s->prev_frame.diff < -s->curr_frame.diff) {=0A=
+            s->prev_frame.diff =3D -s->curr_frame.diff;=0A=
+            s->prev_frame.mafd =3D s->curr_frame.mafd;=0A=
+        }=0A=
+        double scene_score =3D av_clipf(s->mode =3D=3D MODE_LEGACY ? =
FFMIN(s->prev_frame.mafd, s->prev_frame.diff) : =
FFMAX(s->prev_frame.diff, 0), 0, 100.);=0A=
+=0A=
+        char buf[64];=0A=
+        snprintf(buf, sizeof(buf), "%0.3f", s->prev_frame.mafd);=0A=
+        set_meta(s->prev_frame.picref, "lavfi.scd.mafd", buf);=0A=
+        snprintf(buf, sizeof(buf), "%0.3f", scene_score);=0A=
+        set_meta(s->prev_frame.picref, "lavfi.scd.score", buf);=0A=
+=0A=
+        if (scene_score >=3D s->threshold) {=0A=
+            av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, =
lavfi.scd.time: %s\n",=0A=
+                scene_score, av_ts2timestr(s->prev_frame.picref->pts, =
&inlink->time_base));=0A=
+            set_meta(s->prev_frame.picref, "lavfi.scd.time",=0A=
+                av_ts2timestr(s->prev_frame.picref->pts, =
&inlink->time_base));=0A=
+        }=0A=
+=0A=
+        if (s->sc_pass) {=0A=
+            if (scene_score >=3D s->threshold)=0A=
+                return ff_filter_frame(outlink, s->prev_frame.picref);=0A=
+            else=0A=
+                av_frame_free(&s->prev_frame.picref);=0A=
+        }=0A=
+        else=0A=
+            return ff_filter_frame(outlink, s->prev_frame.picref);=0A=
+    }=0A=
+=0A=
+    return 0;=0A=
+}=0A=
+=0A=
 static int activate(AVFilterContext *ctx)=0A=
 {=0A=
     int ret;=0A=
@@ -148,6 +220,8 @@ static int activate(AVFilterContext *ctx)=0A=
     AVFilterLink *outlink =3D ctx->outputs[0];=0A=
     SCDetContext *s =3D ctx->priv;=0A=
     AVFrame *frame;=0A=
+    int64_t pts;=0A=
+    int status;=0A=
 =0A=
     FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);=0A=
 =0A=
@@ -155,31 +229,17 @@ static int activate(AVFilterContext *ctx)=0A=
     if (ret < 0)=0A=
         return ret;=0A=
 =0A=
-    if (frame) {=0A=
-        char buf[64];=0A=
-        s->scene_score =3D get_scene_score(ctx, frame);=0A=
-        snprintf(buf, sizeof(buf), "%0.3f", s->prev_mafd);=0A=
-        set_meta(s, frame, "lavfi.scd.mafd", buf);=0A=
-        snprintf(buf, sizeof(buf), "%0.3f", s->scene_score);=0A=
-        set_meta(s, frame, "lavfi.scd.score", buf);=0A=
+    if (ret > 0)=0A=
+        return filter_frame(ctx, frame);=0A=
 =0A=
-        if (s->scene_score >=3D s->threshold) {=0A=
-            av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, =
lavfi.scd.time: %s\n",=0A=
-                    s->scene_score, av_ts2timestr(frame->pts, =
&inlink->time_base));=0A=
-            set_meta(s, frame, "lavfi.scd.time",=0A=
-                    av_ts2timestr(frame->pts, &inlink->time_base));=0A=
-        }=0A=
-        if (s->sc_pass) {=0A=
-            if (s->scene_score >=3D s->threshold)=0A=
-                return ff_filter_frame(outlink, frame);=0A=
-            else {=0A=
-                av_frame_free(&frame);=0A=
-            }=0A=
-        } else=0A=
-            return ff_filter_frame(outlink, frame);=0A=
+    if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {=0A=
+        if (status =3D=3D AVERROR_EOF)=0A=
+            ret =3D filter_frame(ctx, NULL);=0A=
+=0A=
+        ff_outlink_set_status(outlink, status, pts);=0A=
+        return ret;=0A=
     }=0A=
 =0A=
-    FF_FILTER_FORWARD_STATUS(inlink, outlink);=0A=
     FF_FILTER_FORWARD_WANTED(outlink, inlink);=0A=
 =0A=
     return FFERROR_NOT_READY;=0A=
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak=0A=
index ee9f0f5e40..cff48e33d9 100644=0A=
--- a/tests/fate/filter-video.mak=0A=
+++ b/tests/fate/filter-video.mak=0A=
@@ -672,6 +672,9 @@ SCDET_DEPS =3D LAVFI_INDEV FILE_PROTOCOL =
MOVIE_FILTER SCDET_FILTER SCALE_FILTER \=0A=
 FATE_METADATA_FILTER-$(call ALLYES, $(SCDET_DEPS)) +=3D =
fate-filter-metadata-scdet=0A=
 fate-filter-metadata-scdet: SRC =3D =
$(TARGET_SAMPLES)/svq3/Vertical400kbit.sorenson3.mov=0A=
 fate-filter-metadata-scdet: CMD =3D run $(FILTER_METADATA_COMMAND) =
"sws_flags=3D+accurate_rnd+bitexact;movie=3D'$(SRC)',scdet=3Ds=3D1"=0A=
+FATE_METADATA_FILTER-$(call ALLYES, $(SCDET_DEPS)) +=3D =
fate-filter-metadata-scdet1=0A=
+fate-filter-metadata-scdet1: SRC =3D =
$(TARGET_SAMPLES)/svq3/Vertical400kbit.sorenson3.mov=0A=
+fate-filter-metadata-scdet1: CMD =3D run $(FILTER_METADATA_COMMAND) =
"sws_flags=3D+accurate_rnd+bitexact;movie=3D'$(SRC)',scdet=3Ds=3D1:t=3D6.=
5:mode=3D1"=0A=
 =0A=
 CROPDETECT_DEPS =3D LAVFI_INDEV FILE_PROTOCOL MOVIE_FILTER MOVIE_FILTER =
MESTIMATE_FILTER CROPDETECT_FILTER \=0A=
                   SCALE_FILTER MOV_DEMUXER H264_DECODER=0A=
-- =0A=
2.44.1.windows.1=0A=
=0A=

------=_NextPart_000_008F_01DAB543.0A9E3E30
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
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".

------=_NextPart_000_008F_01DAB543.0A9E3E30--