• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007 Michael Niedermayer <michaelni@gmx.at>
3  * Copyright (c) 2013 Clément Bœsch <u pkh me>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 // The code written by Michael Niedermayer in 70024b6b47b9eacfe01e8f92349ca9bf1ccd7d5a:libavfilter/vf_owdenoise.c
23 // can also be used under the LGPL due to:
24 // <michaelni> durandal_1707, if you do all the "todo" points from vf_owdenoise.c that are in that file since 2013 then sure i would be more than happy to relicense my part of it to LGPL
25 // <durandal_1707> michaelni: first relicense than work
26 
27 /**
28  * @todo try to change to int
29  * @todo try lifting based implementation
30  * @todo optimize optimize optimize
31  * @todo hard thresholding
32  * @todo use QP to decide filter strength
33  * @todo wavelet normalization / least squares optimal signal vs. noise thresholds
34  */
35 
36 #include "libavutil/imgutils.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/mem_internal.h"
39 #include "libavutil/pixdesc.h"
40 #include "avfilter.h"
41 #include "internal.h"
42 
43 typedef struct OWDenoiseContext {
44     const AVClass *class;
45     double luma_strength;
46     double chroma_strength;
47     int depth;
48     float *plane[16+1][4];
49     int linesize;
50     int hsub, vsub;
51     int pixel_depth;
52 } OWDenoiseContext;
53 
54 #define OFFSET(x) offsetof(OWDenoiseContext, x)
55 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
56 static const AVOption owdenoise_options[] = {
57     { "depth",           "set depth",           OFFSET(depth),           AV_OPT_TYPE_INT,    {.i64 =   8}, 8,   16, FLAGS },
58     { "luma_strength",   "set luma strength",   OFFSET(luma_strength),   AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0, 1000, FLAGS },
59     { "ls",              "set luma strength",   OFFSET(luma_strength),   AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0, 1000, FLAGS },
60     { "chroma_strength", "set chroma strength", OFFSET(chroma_strength), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0, 1000, FLAGS },
61     { "cs",              "set chroma strength", OFFSET(chroma_strength), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0, 1000, FLAGS },
62     { NULL }
63 };
64 
65 AVFILTER_DEFINE_CLASS(owdenoise);
66 
67 DECLARE_ALIGNED(8, static const uint8_t, dither)[8][8] = {
68     {  0,  48,  12,  60,   3,  51,  15,  63 },
69     { 32,  16,  44,  28,  35,  19,  47,  31 },
70     {  8,  56,   4,  52,  11,  59,   7,  55 },
71     { 40,  24,  36,  20,  43,  27,  39,  23 },
72     {  2,  50,  14,  62,   1,  49,  13,  61 },
73     { 34,  18,  46,  30,  33,  17,  45,  29 },
74     { 10,  58,   6,  54,   9,  57,   5,  53 },
75     { 42,  26,  38,  22,  41,  25,  37,  21 },
76 };
77 
78 static const double coeff[2][5] = {
79     {
80          0.6029490182363579  * M_SQRT2,
81          0.2668641184428723  * M_SQRT2,
82         -0.07822326652898785 * M_SQRT2,
83         -0.01686411844287495 * M_SQRT2,
84          0.02674875741080976 * M_SQRT2,
85     },{
86          1.115087052456994   / M_SQRT2,
87         -0.5912717631142470  / M_SQRT2,
88         -0.05754352622849957 / M_SQRT2,
89          0.09127176311424948 / M_SQRT2,
90     }
91 };
92 
93 static const double icoeff[2][5] = {
94     {
95          1.115087052456994   / M_SQRT2,
96          0.5912717631142470  / M_SQRT2,
97         -0.05754352622849957 / M_SQRT2,
98         -0.09127176311424948 / M_SQRT2,
99     },{
100          0.6029490182363579  * M_SQRT2,
101         -0.2668641184428723  * M_SQRT2,
102         -0.07822326652898785 * M_SQRT2,
103          0.01686411844287495 * M_SQRT2,
104          0.02674875741080976 * M_SQRT2,
105     }
106 };
107 
108 
decompose(float * dst_l,float * dst_h,const float * src,int linesize,int w)109 static inline void decompose(float *dst_l, float *dst_h, const float *src,
110                              int linesize, int w)
111 {
112     int x, i;
113     for (x = 0; x < w; x++) {
114         double sum_l = src[x * linesize] * coeff[0][0];
115         double sum_h = src[x * linesize] * coeff[1][0];
116         for (i = 1; i <= 4; i++) {
117             const double s = src[avpriv_mirror(x - i, w - 1) * linesize]
118                            + src[avpriv_mirror(x + i, w - 1) * linesize];
119 
120             sum_l += coeff[0][i] * s;
121             sum_h += coeff[1][i] * s;
122         }
123         dst_l[x * linesize] = sum_l;
124         dst_h[x * linesize] = sum_h;
125     }
126 }
127 
compose(float * dst,const float * src_l,const float * src_h,int linesize,int w)128 static inline void compose(float *dst, const float *src_l, const float *src_h,
129                            int linesize, int w)
130 {
131     int x, i;
132     for (x = 0; x < w; x++) {
133         double sum_l = src_l[x * linesize] * icoeff[0][0];
134         double sum_h = src_h[x * linesize] * icoeff[1][0];
135         for (i = 1; i <= 4; i++) {
136             const int x0 = avpriv_mirror(x - i, w - 1) * linesize;
137             const int x1 = avpriv_mirror(x + i, w - 1) * linesize;
138 
139             sum_l += icoeff[0][i] * (src_l[x0] + src_l[x1]);
140             sum_h += icoeff[1][i] * (src_h[x0] + src_h[x1]);
141         }
142         dst[x * linesize] = (sum_l + sum_h) * 0.5;
143     }
144 }
145 
decompose2D(float * dst_l,float * dst_h,const float * src,int xlinesize,int ylinesize,int step,int w,int h)146 static inline void decompose2D(float *dst_l, float *dst_h, const float *src,
147                                int xlinesize, int ylinesize,
148                                int step, int w, int h)
149 {
150     int y, x;
151     for (y = 0; y < h; y++)
152         for (x = 0; x < step; x++)
153             decompose(dst_l + ylinesize*y + xlinesize*x,
154                       dst_h + ylinesize*y + xlinesize*x,
155                       src   + ylinesize*y + xlinesize*x,
156                       step * xlinesize, (w - x + step - 1) / step);
157 }
158 
compose2D(float * dst,const float * src_l,const float * src_h,int xlinesize,int ylinesize,int step,int w,int h)159 static inline void compose2D(float *dst, const float *src_l, const float *src_h,
160                              int xlinesize, int ylinesize,
161                              int step, int w, int h)
162 {
163     int y, x;
164     for (y = 0; y < h; y++)
165         for (x = 0; x < step; x++)
166             compose(dst   + ylinesize*y + xlinesize*x,
167                     src_l + ylinesize*y + xlinesize*x,
168                     src_h + ylinesize*y + xlinesize*x,
169                     step * xlinesize, (w - x + step - 1) / step);
170 }
171 
decompose2D2(float * dst[4],float * src,float * temp[2],int linesize,int step,int w,int h)172 static void decompose2D2(float *dst[4], float *src, float *temp[2],
173                          int linesize, int step, int w, int h)
174 {
175     decompose2D(temp[0], temp[1], src,     1, linesize, step, w, h);
176     decompose2D( dst[0],  dst[1], temp[0], linesize, 1, step, h, w);
177     decompose2D( dst[2],  dst[3], temp[1], linesize, 1, step, h, w);
178 }
179 
compose2D2(float * dst,float * src[4],float * temp[2],int linesize,int step,int w,int h)180 static void compose2D2(float *dst, float *src[4], float *temp[2],
181                        int linesize, int step, int w, int h)
182 {
183     compose2D(temp[0],  src[0],  src[1], linesize, 1, step, h, w);
184     compose2D(temp[1],  src[2],  src[3], linesize, 1, step, h, w);
185     compose2D(dst,     temp[0], temp[1], 1, linesize, step, w, h);
186 }
187 
filter(OWDenoiseContext * s,uint8_t * dst,int dst_linesize,const uint8_t * src,int src_linesize,int width,int height,double strength)188 static void filter(OWDenoiseContext *s,
189                    uint8_t       *dst, int dst_linesize,
190                    const uint8_t *src, int src_linesize,
191                    int width, int height, double strength)
192 {
193     int x, y, i, j, depth = s->depth;
194 
195     while (1<<depth > width || 1<<depth > height)
196         depth--;
197 
198     if (s->pixel_depth <= 8) {
199         for (y = 0; y < height; y++)
200             for(x = 0; x < width; x++)
201                 s->plane[0][0][y*s->linesize + x] = src[y*src_linesize + x];
202     } else {
203         const uint16_t *src16 = (const uint16_t *)src;
204 
205         src_linesize /= 2;
206         for (y = 0; y < height; y++)
207             for(x = 0; x < width; x++)
208                 s->plane[0][0][y*s->linesize + x] = src16[y*src_linesize + x];
209     }
210 
211     for (i = 0; i < depth; i++)
212         decompose2D2(s->plane[i + 1], s->plane[i][0], s->plane[0] + 1, s->linesize, 1<<i, width, height);
213 
214     for (i = 0; i < depth; i++) {
215         for (j = 1; j < 4; j++) {
216             for (y = 0; y < height; y++) {
217                 for (x = 0; x < width; x++) {
218                     double v = s->plane[i + 1][j][y*s->linesize + x];
219                     if      (v >  strength) v -= strength;
220                     else if (v < -strength) v += strength;
221                     else                    v  = 0;
222                     s->plane[i + 1][j][x + y*s->linesize] = v;
223                 }
224             }
225         }
226     }
227     for (i = depth-1; i >= 0; i--)
228         compose2D2(s->plane[i][0], s->plane[i + 1], s->plane[0] + 1, s->linesize, 1<<i, width, height);
229 
230     if (s->pixel_depth <= 8) {
231         for (y = 0; y < height; y++) {
232             for (x = 0; x < width; x++) {
233                 i = s->plane[0][0][y*s->linesize + x] + dither[x&7][y&7]*(1.0/64) + 1.0/128; // yes the rounding is insane but optimal :)
234                 if ((unsigned)i > 255U) i = ~(i >> 31);
235                 dst[y*dst_linesize + x] = i;
236             }
237         }
238     } else {
239         uint16_t *dst16 = (uint16_t *)dst;
240 
241         dst_linesize /= 2;
242         for (y = 0; y < height; y++) {
243             for (x = 0; x < width; x++) {
244                 i = s->plane[0][0][y*s->linesize + x];
245                 dst16[y*dst_linesize + x] = i;
246             }
247         }
248     }
249 }
250 
filter_frame(AVFilterLink * inlink,AVFrame * in)251 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
252 {
253     AVFilterContext *ctx = inlink->dst;
254     OWDenoiseContext *s = ctx->priv;
255     AVFilterLink *outlink = ctx->outputs[0];
256     AVFrame *out;
257     const int cw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
258     const int ch = AV_CEIL_RSHIFT(inlink->h, s->vsub);
259 
260     if (av_frame_is_writable(in)) {
261         out = in;
262 
263         if (s->luma_strength > 0)
264             filter(s, out->data[0], out->linesize[0], in->data[0], in->linesize[0], inlink->w, inlink->h, s->luma_strength);
265         if (s->chroma_strength > 0) {
266             filter(s, out->data[1], out->linesize[1], in->data[1], in->linesize[1], cw,        ch,        s->chroma_strength);
267             filter(s, out->data[2], out->linesize[2], in->data[2], in->linesize[2], cw,        ch,        s->chroma_strength);
268         }
269     } else {
270         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
271         if (!out) {
272             av_frame_free(&in);
273             return AVERROR(ENOMEM);
274         }
275         av_frame_copy_props(out, in);
276 
277         if (s->luma_strength > 0) {
278             filter(s, out->data[0], out->linesize[0], in->data[0], in->linesize[0], inlink->w, inlink->h, s->luma_strength);
279         } else {
280             av_image_copy_plane(out->data[0], out->linesize[0], in ->data[0], in ->linesize[0], inlink->w, inlink->h);
281         }
282         if (s->chroma_strength > 0) {
283             filter(s, out->data[1], out->linesize[1], in->data[1], in->linesize[1], cw, ch, s->chroma_strength);
284             filter(s, out->data[2], out->linesize[2], in->data[2], in->linesize[2], cw, ch, s->chroma_strength);
285         } else {
286             av_image_copy_plane(out->data[1], out->linesize[1], in ->data[1], in ->linesize[1], inlink->w, inlink->h);
287             av_image_copy_plane(out->data[2], out->linesize[2], in ->data[2], in ->linesize[2], inlink->w, inlink->h);
288         }
289 
290         if (in->data[3])
291             av_image_copy_plane(out->data[3], out->linesize[3],
292                                 in ->data[3], in ->linesize[3],
293                                 inlink->w, inlink->h);
294         av_frame_free(&in);
295     }
296 
297     return ff_filter_frame(outlink, out);
298 }
299 
300 static const enum AVPixelFormat pix_fmts[] = {
301     AV_PIX_FMT_YUV444P,      AV_PIX_FMT_YUV422P,
302     AV_PIX_FMT_YUV420P,      AV_PIX_FMT_YUV411P,
303     AV_PIX_FMT_YUV410P,      AV_PIX_FMT_YUV440P,
304     AV_PIX_FMT_YUVA444P,     AV_PIX_FMT_YUVA422P,
305     AV_PIX_FMT_YUVA420P,
306     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
307     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
308     AV_PIX_FMT_YUV440P10,
309     AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
310     AV_PIX_FMT_YUV440P12,
311     AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
312     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
313     AV_PIX_FMT_NONE
314 };
315 
config_input(AVFilterLink * inlink)316 static int config_input(AVFilterLink *inlink)
317 {
318     int i, j;
319     OWDenoiseContext *s = inlink->dst->priv;
320     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
321     const int h = FFALIGN(inlink->h, 16);
322 
323     s->hsub = desc->log2_chroma_w;
324     s->vsub = desc->log2_chroma_h;
325     s->pixel_depth = desc->comp[0].depth;
326 
327     s->linesize = FFALIGN(inlink->w, 16);
328     for (j = 0; j < 4; j++) {
329         for (i = 0; i <= s->depth; i++) {
330             s->plane[i][j] = av_malloc_array(s->linesize, h * sizeof(s->plane[0][0][0]));
331             if (!s->plane[i][j])
332                 return AVERROR(ENOMEM);
333         }
334     }
335     return 0;
336 }
337 
uninit(AVFilterContext * ctx)338 static av_cold void uninit(AVFilterContext *ctx)
339 {
340     int i, j;
341     OWDenoiseContext *s = ctx->priv;
342 
343     for (j = 0; j < 4; j++)
344         for (i = 0; i <= s->depth; i++)
345             av_freep(&s->plane[i][j]);
346 }
347 
348 static const AVFilterPad owdenoise_inputs[] = {
349     {
350         .name         = "default",
351         .type         = AVMEDIA_TYPE_VIDEO,
352         .filter_frame = filter_frame,
353         .config_props = config_input,
354     },
355 };
356 
357 static const AVFilterPad owdenoise_outputs[] = {
358     {
359         .name = "default",
360         .type = AVMEDIA_TYPE_VIDEO,
361     },
362 };
363 
364 const AVFilter ff_vf_owdenoise = {
365     .name          = "owdenoise",
366     .description   = NULL_IF_CONFIG_SMALL("Denoise using wavelets."),
367     .priv_size     = sizeof(OWDenoiseContext),
368     .uninit        = uninit,
369     FILTER_INPUTS(owdenoise_inputs),
370     FILTER_OUTPUTS(owdenoise_outputs),
371     FILTER_PIXFMTS_ARRAY(pix_fmts),
372     .priv_class    = &owdenoise_class,
373     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
374 };
375