mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avfilter/dctdnoiz: add 8x8 dct and make it the default
8x8 is about 5x faster than 16x16 on 1080p input. Since a block size of 8x8 makes the filter almost usable (time wise) and it's not obvious if 8x8 or 16x16 is better from a quality PoV (it really depends on the input and parameters), the filter now defaults to 8x8, and as a result libavfilter is micro bumped.
This commit is contained in:
parent
ec0b08d20b
commit
cec59eb63f
@ -3219,7 +3219,7 @@ curves=psfile='MyCurvesPresets/purple.asv':green='0.45/0.53'
|
||||
|
||||
Denoise frames using 2D DCT (frequency domain filtering).
|
||||
|
||||
This filter is not designed for real time and can be extremely slow.
|
||||
This filter is not designed for real time.
|
||||
|
||||
The filter accepts the following options:
|
||||
|
||||
@ -3235,14 +3235,14 @@ If you need a more advanced filtering, see @option{expr}.
|
||||
Default is @code{0}.
|
||||
|
||||
@item overlap
|
||||
Set number overlapping pixels for each block. Each block is of size
|
||||
@code{16x16}. Since the filter can be slow, you may want to reduce this value,
|
||||
at the cost of a less effective filter and the risk of various artefacts.
|
||||
Set number overlapping pixels for each block. Since the filter can be slow, you
|
||||
may want to reduce this value, at the cost of a less effective filter and the
|
||||
risk of various artefacts.
|
||||
|
||||
If the overlapping value doesn't allow to process the whole input width or
|
||||
height, a warning will be displayed and according borders won't be denoised.
|
||||
|
||||
Default value is @code{15}.
|
||||
Default value is @var{blocksize}-1, which is the best possible setting.
|
||||
|
||||
@item expr, e
|
||||
Set the coefficient factor expression.
|
||||
@ -3254,6 +3254,15 @@ If this is option is set, the @option{sigma} option will be ignored.
|
||||
|
||||
The absolute value of the coefficient can be accessed through the @var{c}
|
||||
variable.
|
||||
|
||||
@item n
|
||||
Set the @var{blocksize} using the number of bits. @code{1<<@var{n}} defines the
|
||||
@var{blocksize}, which is the width and height of the processed blocks.
|
||||
|
||||
The default value is @var{3} (8x8) and can be raised to @var{4} for a
|
||||
@var{blocksize} of 16x16. Note that changing this setting has huge consequences
|
||||
on the speed processing. Also, a larger block size does not necessarily means a
|
||||
better de-noising.
|
||||
@end table
|
||||
|
||||
@subsection Examples
|
||||
@ -3268,6 +3277,11 @@ The same operation can be achieved using the expression system:
|
||||
dctdnoiz=e='gte(c, 4.5*3)'
|
||||
@end example
|
||||
|
||||
Violent denoise using a block size of @code{16x16}:
|
||||
@example
|
||||
dctdnoiz=15:n=4
|
||||
@end example
|
||||
|
||||
@anchor{decimate}
|
||||
@section decimate
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
#define LIBAVFILTER_VERSION_MAJOR 4
|
||||
#define LIBAVFILTER_VERSION_MINOR 11
|
||||
#define LIBAVFILTER_VERSION_MICRO 102
|
||||
#define LIBAVFILTER_VERSION_MICRO 103
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
LIBAVFILTER_VERSION_MINOR, \
|
||||
|
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple, relatively efficient and extremely slow DCT image denoiser.
|
||||
* A simple, relatively efficient and slow DCT image denoiser.
|
||||
*
|
||||
* @see http://www.ipol.im/pub/art/2011/ys-dct/
|
||||
*
|
||||
@ -28,14 +28,12 @@
|
||||
* Tasche (DOI: 10.1016/j.laa.2004.07.015).
|
||||
*/
|
||||
|
||||
#include "libavutil/avassert.h"
|
||||
#include "libavutil/eval.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "drawutils.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NBITS 4
|
||||
#define BSIZE (1<<(NBITS))
|
||||
|
||||
static const char *const var_names[] = { "c", NULL };
|
||||
enum { VAR_C, VAR_VARS_NB };
|
||||
|
||||
@ -55,32 +53,122 @@ typedef struct DCTdnoizContext {
|
||||
float *weights; // dct coeff are cumulated with overlapping; these values are used for averaging
|
||||
int p_linesize; // line sizes for color and weights
|
||||
int overlap; // number of block overlapping pixels
|
||||
int step; // block step increment (BSIZE - overlap)
|
||||
int step; // block step increment (blocksize - overlap)
|
||||
int n; // 1<<n is the block size
|
||||
int bsize; // block size, 1<<n
|
||||
void (*filter_freq_func)(struct DCTdnoizContext *s,
|
||||
const float *src, int src_linesize,
|
||||
float *dst, int dst_linesize);
|
||||
} DCTdnoizContext;
|
||||
|
||||
#define MIN_NBITS 3 /* blocksize = 1<<3 = 8 */
|
||||
#define MAX_NBITS 4 /* blocksize = 1<<4 = 16 */
|
||||
#define DEFAULT_NBITS 3
|
||||
|
||||
#define OFFSET(x) offsetof(DCTdnoizContext, x)
|
||||
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
||||
static const AVOption dctdnoiz_options[] = {
|
||||
{ "sigma", "set noise sigma constant", OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 999, .flags = FLAGS },
|
||||
{ "s", "set noise sigma constant", OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 999, .flags = FLAGS },
|
||||
{ "overlap", "set number of block overlapping pixels", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64=(1<<NBITS)-1}, 0, (1<<NBITS)-1, .flags = FLAGS },
|
||||
{ "overlap", "set number of block overlapping pixels", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64=-1}, -1, (1<<MAX_NBITS)-1, .flags = FLAGS },
|
||||
{ "expr", "set coefficient factor expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
||||
{ "e", "set coefficient factor expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
|
||||
{ "n", "set the block size, expressed in bits", OFFSET(n), AV_OPT_TYPE_INT, {.i64=DEFAULT_NBITS}, MIN_NBITS, MAX_NBITS, .flags = FLAGS },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
AVFILTER_DEFINE_CLASS(dctdnoiz);
|
||||
|
||||
static void av_always_inline fdct8_1d(float *dst, const float *src,
|
||||
int dst_stridea, int dst_strideb,
|
||||
int src_stridea, int src_strideb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
const float x00 = src[0*src_stridea] + src[7*src_stridea];
|
||||
const float x01 = src[1*src_stridea] + src[6*src_stridea];
|
||||
const float x02 = src[2*src_stridea] + src[5*src_stridea];
|
||||
const float x03 = src[3*src_stridea] + src[4*src_stridea];
|
||||
const float x04 = src[0*src_stridea] - src[7*src_stridea];
|
||||
const float x05 = src[1*src_stridea] - src[6*src_stridea];
|
||||
const float x06 = src[2*src_stridea] - src[5*src_stridea];
|
||||
const float x07 = src[3*src_stridea] - src[4*src_stridea];
|
||||
const float x08 = x00 + x03;
|
||||
const float x09 = x01 + x02;
|
||||
const float x0a = x00 - x03;
|
||||
const float x0b = x01 - x02;
|
||||
const float x0c = 1.38703984532215*x04 + 0.275899379282943*x07;
|
||||
const float x0d = 1.17587560241936*x05 + 0.785694958387102*x06;
|
||||
const float x0e = -0.785694958387102*x05 + 1.17587560241936*x06;
|
||||
const float x0f = 0.275899379282943*x04 - 1.38703984532215*x07;
|
||||
const float x10 = 0.353553390593274 * (x0c - x0d);
|
||||
const float x11 = 0.353553390593274 * (x0e - x0f);
|
||||
dst[0*dst_stridea] = 0.353553390593274 * (x08 + x09);
|
||||
dst[1*dst_stridea] = 0.353553390593274 * (x0c + x0d);
|
||||
dst[2*dst_stridea] = 0.461939766255643*x0a + 0.191341716182545*x0b;
|
||||
dst[3*dst_stridea] = 0.707106781186547 * (x10 - x11);
|
||||
dst[4*dst_stridea] = 0.353553390593274 * (x08 - x09);
|
||||
dst[5*dst_stridea] = 0.707106781186547 * (x10 + x11);
|
||||
dst[6*dst_stridea] = 0.191341716182545*x0a - 0.461939766255643*x0b;
|
||||
dst[7*dst_stridea] = 0.353553390593274 * (x0e + x0f);
|
||||
dst += dst_strideb;
|
||||
src += src_strideb;
|
||||
}
|
||||
}
|
||||
|
||||
static void av_always_inline idct8_1d(float *dst, const float *src,
|
||||
int dst_stridea, int dst_strideb,
|
||||
int src_stridea, int src_strideb,
|
||||
int add)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
const float x00 = 1.4142135623731*src[0*src_stridea];
|
||||
const float x01 = 1.38703984532215*src[1*src_stridea] + 0.275899379282943*src[7*src_stridea];
|
||||
const float x02 = 1.30656296487638*src[2*src_stridea] + 0.541196100146197*src[6*src_stridea];
|
||||
const float x03 = 1.17587560241936*src[3*src_stridea] + 0.785694958387102*src[5*src_stridea];
|
||||
const float x04 = 1.4142135623731*src[4*src_stridea];
|
||||
const float x05 = -0.785694958387102*src[3*src_stridea] + 1.17587560241936*src[5*src_stridea];
|
||||
const float x06 = 0.541196100146197*src[2*src_stridea] - 1.30656296487638*src[6*src_stridea];
|
||||
const float x07 = -0.275899379282943*src[1*src_stridea] + 1.38703984532215*src[7*src_stridea];
|
||||
const float x09 = x00 + x04;
|
||||
const float x0a = x01 + x03;
|
||||
const float x0b = 1.4142135623731*x02;
|
||||
const float x0c = x00 - x04;
|
||||
const float x0d = x01 - x03;
|
||||
const float x0e = 0.353553390593274 * (x09 - x0b);
|
||||
const float x0f = 0.353553390593274 * (x0c + x0d);
|
||||
const float x10 = 0.353553390593274 * (x0c - x0d);
|
||||
const float x11 = 1.4142135623731*x06;
|
||||
const float x12 = x05 + x07;
|
||||
const float x13 = x05 - x07;
|
||||
const float x14 = 0.353553390593274 * (x11 + x12);
|
||||
const float x15 = 0.353553390593274 * (x11 - x12);
|
||||
const float x16 = 0.5*x13;
|
||||
const float x08 = -x15;
|
||||
dst[0*dst_stridea] = (add ? dst[ 0*dst_stridea] : 0) + 0.25 * (x09 + x0b) + 0.353553390593274*x0a;
|
||||
dst[1*dst_stridea] = (add ? dst[ 1*dst_stridea] : 0) + 0.707106781186547 * (x0f - x08);
|
||||
dst[2*dst_stridea] = (add ? dst[ 2*dst_stridea] : 0) + 0.707106781186547 * (x0f + x08);
|
||||
dst[3*dst_stridea] = (add ? dst[ 3*dst_stridea] : 0) + 0.707106781186547 * (x0e + x16);
|
||||
dst[4*dst_stridea] = (add ? dst[ 4*dst_stridea] : 0) + 0.707106781186547 * (x0e - x16);
|
||||
dst[5*dst_stridea] = (add ? dst[ 5*dst_stridea] : 0) + 0.707106781186547 * (x10 - x14);
|
||||
dst[6*dst_stridea] = (add ? dst[ 6*dst_stridea] : 0) + 0.707106781186547 * (x10 + x14);
|
||||
dst[7*dst_stridea] = (add ? dst[ 7*dst_stridea] : 0) + 0.25 * (x09 + x0b) - 0.353553390593274*x0a;
|
||||
dst += dst_strideb;
|
||||
src += src_strideb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void av_always_inline fdct16_1d(float *dst, const float *src,
|
||||
int dst_stridea, int dst_strideb,
|
||||
int src_stridea, int src_strideb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BSIZE; i++) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
const float x00 = src[ 0*src_stridea] + src[15*src_stridea];
|
||||
const float x01 = src[ 1*src_stridea] + src[14*src_stridea];
|
||||
const float x02 = src[ 2*src_stridea] + src[13*src_stridea];
|
||||
@ -165,7 +253,7 @@ static void av_always_inline idct16_1d(float *dst, const float *src,
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BSIZE; i++) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
const float x00 = 1.4142135623731 *src[ 0*src_stridea];
|
||||
const float x01 = 1.40740373752638 *src[ 1*src_stridea] + 0.138617169199091*src[15*src_stridea];
|
||||
const float x02 = 1.38703984532215 *src[ 2*src_stridea] + 0.275899379282943*src[14*src_stridea];
|
||||
@ -256,55 +344,60 @@ static void av_always_inline idct16_1d(float *dst, const float *src,
|
||||
}
|
||||
}
|
||||
|
||||
static av_always_inline void filter_freq(const float *src, int src_linesize,
|
||||
float *dst, int dst_linesize,
|
||||
AVExpr *expr, double *var_values,
|
||||
int sigma_th)
|
||||
{
|
||||
unsigned i;
|
||||
DECLARE_ALIGNED(32, float, tmp_block1)[BSIZE * BSIZE];
|
||||
DECLARE_ALIGNED(32, float, tmp_block2)[BSIZE * BSIZE];
|
||||
|
||||
/* forward DCT */
|
||||
fdct16_1d(tmp_block1, src, 1, BSIZE, 1, src_linesize);
|
||||
fdct16_1d(tmp_block2, tmp_block1, BSIZE, 1, BSIZE, 1);
|
||||
|
||||
for (i = 0; i < BSIZE*BSIZE; i++) {
|
||||
float *b = &tmp_block2[i];
|
||||
/* frequency filtering */
|
||||
if (expr) {
|
||||
var_values[VAR_C] = FFABS(*b);
|
||||
*b *= av_expr_eval(expr, var_values, NULL);
|
||||
} else {
|
||||
if (FFABS(*b) < sigma_th)
|
||||
*b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* inverse DCT */
|
||||
idct16_1d(tmp_block1, tmp_block2, 1, BSIZE, 1, BSIZE, 0);
|
||||
idct16_1d(dst, tmp_block1, dst_linesize, 1, BSIZE, 1, 1);
|
||||
#define DEF_FILTER_FREQ_FUNCS(bsize) \
|
||||
static av_always_inline void filter_freq_##bsize(const float *src, int src_linesize, \
|
||||
float *dst, int dst_linesize, \
|
||||
AVExpr *expr, double *var_values, \
|
||||
int sigma_th) \
|
||||
{ \
|
||||
unsigned i; \
|
||||
DECLARE_ALIGNED(32, float, tmp_block1)[bsize * bsize]; \
|
||||
DECLARE_ALIGNED(32, float, tmp_block2)[bsize * bsize]; \
|
||||
\
|
||||
/* forward DCT */ \
|
||||
fdct##bsize##_1d(tmp_block1, src, 1, bsize, 1, src_linesize); \
|
||||
fdct##bsize##_1d(tmp_block2, tmp_block1, bsize, 1, bsize, 1); \
|
||||
\
|
||||
for (i = 0; i < bsize*bsize; i++) { \
|
||||
float *b = &tmp_block2[i]; \
|
||||
/* frequency filtering */ \
|
||||
if (expr) { \
|
||||
var_values[VAR_C] = FFABS(*b); \
|
||||
*b *= av_expr_eval(expr, var_values, NULL); \
|
||||
} else { \
|
||||
if (FFABS(*b) < sigma_th) \
|
||||
*b = 0; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* inverse DCT */ \
|
||||
idct##bsize##_1d(tmp_block1, tmp_block2, 1, bsize, 1, bsize, 0); \
|
||||
idct##bsize##_1d(dst, tmp_block1, dst_linesize, 1, bsize, 1, 1); \
|
||||
} \
|
||||
\
|
||||
static void filter_freq_sigma_##bsize(DCTdnoizContext *s, \
|
||||
const float *src, int src_linesize, \
|
||||
float *dst, int dst_linesize) \
|
||||
{ \
|
||||
filter_freq_##bsize(src, src_linesize, dst, dst_linesize, NULL, NULL, s->th); \
|
||||
} \
|
||||
\
|
||||
static void filter_freq_expr_##bsize(DCTdnoizContext *s, \
|
||||
const float *src, int src_linesize, \
|
||||
float *dst, int dst_linesize) \
|
||||
{ \
|
||||
filter_freq_##bsize(src, src_linesize, dst, dst_linesize, s->expr, s->var_values, 0); \
|
||||
}
|
||||
|
||||
static void filter_freq_sigma(DCTdnoizContext *s,
|
||||
const float *src, int src_linesize,
|
||||
float *dst, int dst_linesize)
|
||||
{
|
||||
filter_freq(src, src_linesize, dst, dst_linesize, NULL, NULL, s->th);
|
||||
}
|
||||
|
||||
static void filter_freq_expr(DCTdnoizContext *s,
|
||||
const float *src, int src_linesize,
|
||||
float *dst, int dst_linesize)
|
||||
{
|
||||
filter_freq(src, src_linesize, dst, dst_linesize, s->expr, s->var_values, 0);
|
||||
}
|
||||
DEF_FILTER_FREQ_FUNCS(8)
|
||||
DEF_FILTER_FREQ_FUNCS(16)
|
||||
|
||||
static int config_input(AVFilterLink *inlink)
|
||||
{
|
||||
AVFilterContext *ctx = inlink->dst;
|
||||
DCTdnoizContext *s = ctx->priv;
|
||||
int i, x, y, bx, by, linesize, *iweights;
|
||||
const int bsize = 1 << s->n;
|
||||
const float dct_3x3[3][3] = {
|
||||
{ 1./sqrt(3), 1./sqrt(3), 1./sqrt(3) },
|
||||
{ 1./sqrt(2), 0, -1./sqrt(2) },
|
||||
@ -317,8 +410,8 @@ static int config_input(AVFilterLink *inlink)
|
||||
for (x = 0; x < 3; x++)
|
||||
s->color_dct[y][x] = dct_3x3[rgba_map[y]][rgba_map[x]];
|
||||
|
||||
s->pr_width = inlink->w - (inlink->w - BSIZE) % s->step;
|
||||
s->pr_height = inlink->h - (inlink->h - BSIZE) % s->step;
|
||||
s->pr_width = inlink->w - (inlink->w - bsize) % s->step;
|
||||
s->pr_height = inlink->h - (inlink->h - bsize) % s->step;
|
||||
if (s->pr_width != inlink->w)
|
||||
av_log(ctx, AV_LOG_WARNING, "The last %d horizontal pixels won't be denoised\n",
|
||||
inlink->w - s->pr_width);
|
||||
@ -341,10 +434,10 @@ static int config_input(AVFilterLink *inlink)
|
||||
iweights = av_calloc(s->pr_height, linesize * sizeof(*iweights));
|
||||
if (!iweights)
|
||||
return AVERROR(ENOMEM);
|
||||
for (y = 0; y < s->pr_height - BSIZE + 1; y += s->step)
|
||||
for (x = 0; x < s->pr_width - BSIZE + 1; x += s->step)
|
||||
for (by = 0; by < BSIZE; by++)
|
||||
for (bx = 0; bx < BSIZE; bx++)
|
||||
for (y = 0; y < s->pr_height - bsize + 1; y += s->step)
|
||||
for (x = 0; x < s->pr_width - bsize + 1; x += s->step)
|
||||
for (by = 0; by < bsize; by++)
|
||||
for (bx = 0; bx < bsize; bx++)
|
||||
iweights[(y + by)*linesize + x + bx]++;
|
||||
for (y = 0; y < s->pr_height; y++)
|
||||
for (x = 0; x < s->pr_width; x++)
|
||||
@ -358,18 +451,37 @@ static av_cold int init(AVFilterContext *ctx)
|
||||
{
|
||||
DCTdnoizContext *s = ctx->priv;
|
||||
|
||||
s->bsize = 1 << s->n;
|
||||
if (s->overlap == -1)
|
||||
s->overlap = s->bsize - 1;
|
||||
|
||||
if (s->overlap > s->bsize - 1) {
|
||||
av_log(s, AV_LOG_ERROR, "Overlap value can not except %d "
|
||||
"with a block size of %dx%d\n",
|
||||
s->bsize - 1, s->bsize, s->bsize);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
if (s->expr_str) {
|
||||
int ret = av_expr_parse(&s->expr, s->expr_str, var_names,
|
||||
NULL, NULL, NULL, NULL, 0, ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
s->filter_freq_func = filter_freq_expr;
|
||||
switch (s->n) {
|
||||
case 3: s->filter_freq_func = filter_freq_expr_8; break;
|
||||
case 4: s->filter_freq_func = filter_freq_expr_16; break;
|
||||
default: av_assert0(0);
|
||||
}
|
||||
} else {
|
||||
s->filter_freq_func = filter_freq_sigma;
|
||||
switch (s->n) {
|
||||
case 3: s->filter_freq_func = filter_freq_sigma_8; break;
|
||||
case 4: s->filter_freq_func = filter_freq_sigma_16; break;
|
||||
default: av_assert0(0);
|
||||
}
|
||||
}
|
||||
|
||||
s->th = s->sigma * 3.;
|
||||
s->step = BSIZE - s->overlap;
|
||||
s->step = s->bsize - s->overlap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -445,8 +557,8 @@ static void filter_plane(AVFilterContext *ctx,
|
||||
memset(dst, 0, h * dst_linesize * sizeof(*dst));
|
||||
|
||||
// block dct sums
|
||||
for (y = 0; y < h - BSIZE + 1; y += s->step) {
|
||||
for (x = 0; x < w - BSIZE + 1; x += s->step)
|
||||
for (y = 0; y < h - s->bsize + 1; y += s->step) {
|
||||
for (x = 0; x < w - s->bsize + 1; x += s->step)
|
||||
s->filter_freq_func(s, src + x, src_linesize,
|
||||
dst + x, dst_linesize);
|
||||
src += s->step * src_linesize;
|
||||
|
Loading…
Reference in New Issue
Block a user