[FFmpeg-devel] [PATCH] avfilter: add nlmeans filter
Clément Bœsch
u at pkh.me
Sat Sep 24 11:02:33 EEST 2016
On Wed, Sep 21, 2016 at 02:39:55PM +0200, Benoit Fouet wrote:
[...]
> > diff --git a/Changelog b/Changelog
> > index 2d0a449..a5282b4 100644
> > --- a/Changelog
> > +++ b/Changelog
> > @@ -31,6 +31,7 @@ version <next>:
> > - MediaCodec HEVC decoding
> > - TrueHD encoder
> > - Meridian Lossless Packing (MLP) encoder
> > +- nlmeans filter (denoiser)
>
> The full name could be used here: Non-Local Means (nlmeans) denoising filter
>
changed.
> > version 3.1:
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index 070e57d..7e9ab60 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -9695,6 +9695,41 @@ Negate input video.
> > It accepts an integer in input; if non-zero it negates the
> > alpha component (if available). The default value in input is 0.
> > + at section nlmeans
> > +
> > +Denoise frames using Non-Local Means algorithm.
> > +
> > +Each pixel is adjusted by looking for other pixels with similar contexts. This
> > +context similarity is defined by their surrounding patch of size
>
> "is defined by comparing their surrounding patches" ?
>
OK
> > + at option{p}x at option{p}. Patches are researched in an area of
> > + at option{r}x at option{r} surrouding the pixel.
> > +
>
> surrounding, or even simply "around"
> Also "research" sounds weird (I'd use "search"), but maybe wait for someone
> native to comment
>
Sounds good, changed.
[...]
> > + int patch_size, patch_hsize; // patch size and half size
> > + int patch_size_c, patch_hsize_c; // patch size and half size for chroma planes
>
> nit: could you use _uv instead of _c (the latter has a taste of C vs ASM)?
>
Yeah, renamed.
[...]
> > +static const AVOption nlmeans_options[] = {
> > + { "s", "denoising strength", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 30.0, FLAGS },
> > + { "p", "patch size", OFFSET(patch_size), AV_OPT_TYPE_INT, { .i64 = 3*2+1 }, 0, 99, FLAGS },
> > + { "pc", "patch size for chroma planes", OFFSET(patch_size_c), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS },
> > + { "r", "research window", OFFSET(research_size), AV_OPT_TYPE_INT, { .i64 = 7*2+1 }, 0, 99, FLAGS },
>
> Nit: if there is a correlation between the default patch size and research
> window size, maybe add defines? Like:
> #define NLMEANS_DEFAULT_PATCH_SIZE (3*2+1)
> #define NLMEANS_DEFAULT_RESEARCH_WINDOW_SIZE (NLMEANS_DEFAULT_PATCH_SIZE +
> 1)
No real relationship here actually.
[...]
> > +static inline int get_ssd_patch(const uint32_t *ii, int ii_lz_32, int x, int y, int p)
>
> Actually, this is not really about SSD value here. This function does not
> really care about what has been integrated, it just compute the patch value
> only by knowing the buffer is an integral image.
> get_patch_value (maybe too much generic)?
> Anyway... this is nitpicking, feel free to just ignore, I just felt I needed
> to explain a bit more why I wanted another name :-)
>
Renamed to get_integral_patch_value()
[...]
> > + * The above line and left column of dst are always readable.
> > + *
>
> The line above dst and the column to its left are always readable.
>
changed
[...]
> > + * On the other hand, the above line and left column of dst are still always
> > + * readable.
> > + *
>
> same
>
also changed
[...]
> > + for (x = startx; x < startx + w; x++) {
> > + const int s1x = av_clip(x - r, 0, sw - 1);
> > + const int s2x = av_clip(x - (r + offx), 0, sw - 1);
> > + const int s1y = av_clip(y - r, 0, sh - 1);
> > + const int s2y = av_clip(y - (r + offy), 0, sh - 1);
> > + const uint8_t v1 = src[s1y*linesize + s1x];
> > + const uint8_t v2 = src[s2y*linesize + s2x];
> > + const int d = v1 - v2;
> > + acc += d * d;
> > + dst[y*dst_linesize_32 + x] = dst[(y-1)*dst_linesize_32 + x] + acc;
> > + }
>
> I can understand this is done on smaller portions, but it would still be
> good to (at least) move the y-only parts out of the x loop.
>
Yeah sure, moved s1y and s2y out of the x loop
[...]
> > + // allocate weighted average for every pixel
> > + s->wa_linesize = inlink->w;
> > + s->wa = av_malloc_array(s->wa_linesize, inlink->h * sizeof(*s->wa));
> > + if (!s->wa)
> > + return AVERROR(ENOMEM);
>
> this leaks s->ii_orig
>
No that's fine, uninit() is called when init() fails
> > +static av_cold void uninit(AVFilterContext *ctx)
> > +{
> > + NLMeansContext *s = ctx->priv;
> > + av_freep(&s->ii_orig);
>
> s->wa also needs to be freed
>
Ah I fixed that in another branch but forgot to backport. Fixed.
Patch applied, thanks for the review
--
Clément B.
More information about the ffmpeg-devel
mailing list