mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2026-11-06 05:17:42 +00:00
avfilter/vf_vignette: add a decimated transform option to the wavelet mode
A level works in place on the lowpass lattice of the previous one, so the mode needs 5 planes instead of 9 + 3 * levels and about an order of magnitude less computation. Shrinkage is less effective without the shift invariance of the undecimated transform. Assisted-by: Claude
This commit is contained in:
1 parent
042548c65f
commit
734743bb89
2 files changed
+81
-6
No files matched your search
+8
-3
@@ -26391,9 +26391,9 @@ also be used to create a burning effect.
|
||||
|
||||
@item wavelet
|
||||
Like @samp{backward}, but the noise amplified in the dark areas is suppressed
|
||||
in an undecimated 9/7 wavelet domain: detail coefficients are shrunk toward 0
|
||||
according to the local attenuation and the @option{noise} level, while the
|
||||
low frequencies, which average over larger areas, are kept.
|
||||
in a 9/7 wavelet domain, undecimated by default: detail coefficients are
|
||||
shrunk toward 0 according to the local attenuation and the @option{noise}
|
||||
level, while the low frequencies, which average over larger areas, are kept.
|
||||
It needs about 150 bytes of memory per pixel.
|
||||
@end table
|
||||
|
||||
@@ -26404,6 +26404,11 @@ Set the standard deviation of the input noise in 8-bit levels, used by the
|
||||
@samp{wavelet} mode. Quantization alone contributes about 0.3-0.4.
|
||||
Default is @code{0.5}.
|
||||
|
||||
@item decimated
|
||||
Use a decimated instead of an undecimated wavelet transform in the
|
||||
@samp{wavelet} mode. Several times faster and needs about 20 bytes of memory
|
||||
per pixel, the result has slightly more artifacts. Default is @code{0}.
|
||||
|
||||
@item eval
|
||||
Set evaluation mode for the expressions (@option{angle}, @option{x0}, @option{y0}).
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ typedef struct VignetteContext {
|
||||
int mode; ///< Mode
|
||||
int eval_mode; ///< EvalMode
|
||||
double noise;
|
||||
int decimated;
|
||||
double sqcoeff[2][5];
|
||||
float *wplanes;
|
||||
size_t wplane_size;
|
||||
@@ -105,6 +106,7 @@ static const AVOption vignette_options[] = {
|
||||
{ "dither", "set dithering", OFFSET(do_dither), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
|
||||
{ "aspect", "set aspect ratio", OFFSET(aspect), AV_OPT_TYPE_RATIONAL, {.dbl = 1}, 0, DBL_MAX, .flags = FLAGS },
|
||||
{ "noise", "set input noise standard deviation for wavelet mode", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl = 0.5}, 0.01, 255, FLAGS },
|
||||
{ "decimated", "use a decimated wavelet transform, faster but lower quality", OFFSET(decimated), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -234,6 +236,8 @@ static int wavelet_levels(int w, int h)
|
||||
enum PassType {
|
||||
PASS_DECOMPOSE,
|
||||
PASS_COMPOSE,
|
||||
PASS_DECOMPOSE_DECIMATED,
|
||||
PASS_COMPOSE_DECIMATED,
|
||||
PASS_BOX,
|
||||
PASS_ENERGY,
|
||||
PASS_GAIN,
|
||||
@@ -326,6 +330,14 @@ static int run_pass_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_job
|
||||
ff_wavelet97_compose_lines(p->dst[0], p->src[0], p->src[1],
|
||||
l.xstride, l.ystride, p->step, l.w, line0, line1);
|
||||
break;
|
||||
case PASS_DECOMPOSE_DECIMATED:
|
||||
ff_wavelet97_decompose_decimated_lines(p->dst[0], p->src[0], p->coeff,
|
||||
l.xstride, l.ystride, l.w, line0, line1);
|
||||
break;
|
||||
case PASS_COMPOSE_DECIMATED:
|
||||
ff_wavelet97_compose_decimated_lines(p->dst[0], p->src[0],
|
||||
l.xstride, l.ystride, l.w, line0, line1);
|
||||
break;
|
||||
case PASS_BOX: box_lines(p, line0, line1); break;
|
||||
case PASS_ENERGY: energy_lines(p, line0, line1); break;
|
||||
case PASS_GAIN: gain_lines(p, line0, line1); break;
|
||||
@@ -366,6 +378,27 @@ static void compose(AVFilterContext *ctx, float *dst, float *const src[4], float
|
||||
run_pass(ctx, &p);
|
||||
}
|
||||
|
||||
static void decompose_decimated(AVFilterContext *ctx, float *plane, float *temp,
|
||||
const double coeff[2][5], Lattice lat)
|
||||
{
|
||||
WaveletPass p = { PASS_DECOMPOSE_DECIMATED, lat, { temp }, { plane }, coeff };
|
||||
|
||||
run_pass(ctx, &p);
|
||||
p.lat = transpose(lat);
|
||||
p.dst[0] = plane; p.src[0] = temp;
|
||||
run_pass(ctx, &p);
|
||||
}
|
||||
|
||||
static void compose_decimated(AVFilterContext *ctx, float *plane, float *temp, Lattice lat)
|
||||
{
|
||||
WaveletPass p = { PASS_COMPOSE_DECIMATED, transpose(lat), { temp }, { plane } };
|
||||
|
||||
run_pass(ctx, &p);
|
||||
p.lat = lat;
|
||||
p.dst[0] = plane; p.src[0] = temp;
|
||||
run_pass(ctx, &p);
|
||||
}
|
||||
|
||||
static void box_filter(AVFilterContext *ctx, float *plane, float *temp, int step, Lattice lat)
|
||||
{
|
||||
WaveletPass p = { PASS_BOX, lat, { temp }, { plane }, NULL, step };
|
||||
@@ -393,7 +426,7 @@ static void shrink_band(AVFilterContext *ctx, float *band, const float *var, flo
|
||||
run_pass(ctx, &p);
|
||||
}
|
||||
|
||||
static void transform(AVFilterContext *ctx, float *cur, float *var, int levels, Lattice lat)
|
||||
static void transform_undecimated(AVFilterContext *ctx, float *cur, float *var, int levels, Lattice lat)
|
||||
{
|
||||
VignetteContext *s = ctx->priv;
|
||||
const size_t plane_size = s->wplane_size;
|
||||
@@ -425,6 +458,40 @@ static void transform(AVFilterContext *ctx, float *cur, float *var, int levels,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lattice of the lowpass coefficients of the given level of a decimated transform
|
||||
*/
|
||||
static Lattice level_lattice(Lattice lat, int level)
|
||||
{
|
||||
return (Lattice){ lat.xstride << level, lat.ystride << level,
|
||||
AV_CEIL_RSHIFT(lat.w, level), AV_CEIL_RSHIFT(lat.h, level) };
|
||||
}
|
||||
|
||||
static void transform_decimated(AVFilterContext *ctx, float *cur, float *var, int levels, Lattice lat)
|
||||
{
|
||||
VignetteContext *s = ctx->priv;
|
||||
const size_t plane_size = s->wplane_size;
|
||||
float *temp[3] = { var + plane_size, var + 2 * plane_size, var + 3 * plane_size };
|
||||
|
||||
for (int i = 0; i < levels; i++) {
|
||||
const Lattice level = level_lattice(lat, i);
|
||||
|
||||
decompose_decimated(ctx, cur, temp[0], ff_wavelet97_coeff, level);
|
||||
decompose_decimated(ctx, var, temp[0], s->sqcoeff, level);
|
||||
for (int j = 1; j < 4; j++) {
|
||||
const int offset = (j & 1) * level.xstride + (j >> 1) * level.ystride;
|
||||
const Lattice band = { 2 * level.xstride, 2 * level.ystride,
|
||||
(level.w + !(j & 1)) / 2, (level.h + !(j >> 1)) / 2 };
|
||||
float *scratch[3] = { temp[0] + offset, temp[1] + offset, temp[2] + offset };
|
||||
|
||||
shrink_band(ctx, cur + offset, var + offset, scratch, 1, band);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = levels - 1; i >= 0; i--)
|
||||
compose_decimated(ctx, cur, temp[0], level_lattice(lat, i));
|
||||
}
|
||||
|
||||
static void filter_plane_wavelet(AVFilterContext *ctx,
|
||||
uint8_t *dst, int dst_linesize,
|
||||
const uint8_t *src, int src_linesize, int pixel_step,
|
||||
@@ -446,7 +513,10 @@ static void filter_plane_wavelet(AVFilterContext *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
transform(ctx, cur, var, wavelet_levels(w, h), lat);
|
||||
if (s->decimated)
|
||||
transform_decimated(ctx, cur, var, wavelet_levels(w, h), lat);
|
||||
else
|
||||
transform_undecimated(ctx, cur, var, wavelet_levels(w, h), lat);
|
||||
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
@@ -597,7 +667,7 @@ static int config_props(AVFilterLink *inlink)
|
||||
|
||||
if (s->mode == MODE_WAVELET) {
|
||||
s->wplane_size = (size_t)s->fmap_linesize * inlink->h;
|
||||
s->wplanes = av_malloc_array(s->wplane_size, (9 + 3 * wavelet_levels(inlink->w, inlink->h)) * sizeof(*s->wplanes));
|
||||
s->wplanes = av_malloc_array(s->wplane_size, (s->decimated ? 5 : 9 + 3 * wavelet_levels(inlink->w, inlink->h)) * sizeof(*s->wplanes));
|
||||
if (!s->wplanes)
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user