• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2003 LeFunGus, lefungus@altern.org
3  *
4  * This file is part of FFmpeg
5  *
6  * FFmpeg is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <float.h>
22 
23 #include "libavutil/imgutils.h"
24 #include "libavutil/attributes.h"
25 #include "libavutil/common.h"
26 #include "libavutil/pixdesc.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/opt.h"
29 
30 #include "avfilter.h"
31 #include "formats.h"
32 #include "internal.h"
33 #include "video.h"
34 
35 typedef struct VagueDenoiserContext {
36     const AVClass *class;
37 
38     float threshold;
39     float percent;
40     int method;
41     int type;
42     int nsteps;
43     int planes;
44 
45     int depth;
46     int bpc;
47     int peak;
48     int nb_planes;
49     int planeheight[4];
50     int planewidth[4];
51 
52     float *block;
53     float *in;
54     float *out;
55     float *tmp;
56 
57     int hlowsize[4][32];
58     int hhighsize[4][32];
59     int vlowsize[4][32];
60     int vhighsize[4][32];
61 
62     void (*thresholding)(float *block, const int width, const int height,
63                          const int stride, const float threshold,
64                          const float percent);
65 } VagueDenoiserContext;
66 
67 #define OFFSET(x) offsetof(VagueDenoiserContext, x)
68 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
69 static const AVOption vaguedenoiser_options[] = {
70     { "threshold", "set filtering strength",   OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=2.},  0,DBL_MAX, FLAGS },
71     { "method",    "set filtering method",     OFFSET(method),    AV_OPT_TYPE_INT,   {.i64=2 },  0, 2,      FLAGS, "method" },
72         { "hard",   "hard thresholding",       0,                 AV_OPT_TYPE_CONST, {.i64=0},   0, 0,      FLAGS, "method" },
73         { "soft",   "soft thresholding",       0,                 AV_OPT_TYPE_CONST, {.i64=1},   0, 0,      FLAGS, "method" },
74         { "garrote", "garrote thresholding",   0,                 AV_OPT_TYPE_CONST, {.i64=2},   0, 0,      FLAGS, "method" },
75     { "nsteps",    "set number of steps",      OFFSET(nsteps),    AV_OPT_TYPE_INT,   {.i64=6 },  1, 32,     FLAGS },
76     { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_FLOAT, {.dbl=85},  0,100,     FLAGS },
77     { "planes",    "set planes to filter",     OFFSET(planes),    AV_OPT_TYPE_INT,   {.i64=15 }, 0, 15,     FLAGS },
78     { "type",    "set threshold type",     OFFSET(type),          AV_OPT_TYPE_INT,   {.i64=0 },  0, 1,      FLAGS, "type" },
79         { "universal",  "universal (VisuShrink)", 0,              AV_OPT_TYPE_CONST, {.i64=0},   0, 0,      FLAGS, "type" },
80         { "bayes",      "bayes (BayesShrink)",    0,              AV_OPT_TYPE_CONST, {.i64=1},   0, 0,      FLAGS, "type" },
81     { NULL }
82 };
83 
84 AVFILTER_DEFINE_CLASS(vaguedenoiser);
85 
86 #define NPAD 10
87 
88 static const float analysis_low[9] = {
89     0.037828455506995f, -0.023849465019380f, -0.110624404418423f, 0.377402855612654f,
90     0.852698679009403f, 0.377402855612654f, -0.110624404418423f, -0.023849465019380f, 0.037828455506995f
91 };
92 
93 static const float analysis_high[7] = {
94     -0.064538882628938f, 0.040689417609558f, 0.418092273222212f, -0.788485616405664f,
95     0.418092273222212f, 0.040689417609558f, -0.064538882628938f
96 };
97 
98 static const float synthesis_low[7] = {
99     -0.064538882628938f, -0.040689417609558f, 0.418092273222212f, 0.788485616405664f,
100     0.418092273222212f, -0.040689417609558f, -0.064538882628938f
101 };
102 
103 static const float synthesis_high[9] = {
104     -0.037828455506995f, -0.023849465019380f, 0.110624404418423f, 0.377402855612654f,
105     -0.852698679009403f, 0.377402855612654f, 0.110624404418423f, -0.023849465019380f, -0.037828455506995f
106 };
107 
108 static const enum AVPixelFormat pix_fmts[] = {
109     AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10,
110     AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
111     AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
112     AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
113     AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
114     AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
115     AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
116     AV_PIX_FMT_YUVJ411P,
117     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
118     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
119     AV_PIX_FMT_YUV440P10,
120     AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
121     AV_PIX_FMT_YUV440P12,
122     AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
123     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
124     AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
125     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
126     AV_PIX_FMT_YUVA420P,  AV_PIX_FMT_YUVA422P,   AV_PIX_FMT_YUVA444P,
127     AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
128     AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16,
129     AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
130     AV_PIX_FMT_GBRAP,     AV_PIX_FMT_GBRAP10,    AV_PIX_FMT_GBRAP12,    AV_PIX_FMT_GBRAP16,
131     AV_PIX_FMT_NONE
132 };
133 
config_input(AVFilterLink * inlink)134 static int config_input(AVFilterLink *inlink)
135 {
136     VagueDenoiserContext *s = inlink->dst->priv;
137     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
138     int p, i, nsteps_width, nsteps_height, nsteps_max;
139 
140     s->depth = desc->comp[0].depth;
141     s->bpc = (s->depth + 7) / 8;
142     s->nb_planes = desc->nb_components;
143 
144     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
145     s->planeheight[0] = s->planeheight[3] = inlink->h;
146     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
147     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
148 
149     s->block = av_malloc_array(inlink->w * inlink->h, sizeof(*s->block));
150     s->in    = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->in));
151     s->out   = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->out));
152     s->tmp   = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->tmp));
153 
154     if (!s->block || !s->in || !s->out || !s->tmp)
155         return AVERROR(ENOMEM);
156 
157     s->threshold *= 1 << (s->depth - 8);
158     s->peak = (1 << s->depth) - 1;
159 
160     nsteps_width  = ((s->planes & 2 || s->planes & 4) && s->nb_planes > 1) ? s->planewidth[1] : s->planewidth[0];
161     nsteps_height = ((s->planes & 2 || s->planes & 4) && s->nb_planes > 1) ? s->planeheight[1] : s->planeheight[0];
162 
163     for (nsteps_max = 1; nsteps_max < 15; nsteps_max++) {
164         if (pow(2, nsteps_max) >= nsteps_width || pow(2, nsteps_max) >= nsteps_height)
165             break;
166     }
167 
168     s->nsteps = FFMIN(s->nsteps, nsteps_max - 2);
169 
170     for (p = 0; p < 4; p++) {
171         s->hlowsize[p][0]  = (s->planewidth[p] + 1) >> 1;
172         s->hhighsize[p][0] =  s->planewidth[p] >> 1;
173         s->vlowsize[p][0]  = (s->planeheight[p] + 1) >> 1;
174         s->vhighsize[p][0] =  s->planeheight[p] >> 1;
175 
176         for (i = 1; i < s->nsteps; i++) {
177             s->hlowsize[p][i]  = (s->hlowsize[p][i - 1] + 1) >> 1;
178             s->hhighsize[p][i] =  s->hlowsize[p][i - 1] >> 1;
179             s->vlowsize[p][i]  = (s->vlowsize[p][i - 1] + 1) >> 1;
180             s->vhighsize[p][i] =  s->vlowsize[p][i - 1] >> 1;
181         }
182     }
183 
184     return 0;
185 }
186 
copy(const float * p1,float * p2,const int length)187 static inline void copy(const float *p1, float *p2, const int length)
188 {
189     memcpy(p2, p1, length * sizeof(float));
190 }
191 
copyv(const float * p1,const int stride1,float * p2,const int length)192 static inline void copyv(const float *p1, const int stride1, float *p2, const int length)
193 {
194     int i;
195 
196     for (i = 0; i < length; i++) {
197         p2[i] = *p1;
198         p1 += stride1;
199     }
200 }
201 
copyh(const float * p1,float * p2,const int stride2,const int length)202 static inline void copyh(const float *p1, float *p2, const int stride2, const int length)
203 {
204     int i;
205 
206     for (i = 0; i < length; i++) {
207         *p2 = p1[i];
208         p2 += stride2;
209     }
210 }
211 
212 // Do symmetric extension of data using prescribed symmetries
213 // Original values are in output[npad] through output[npad+size-1]
214 // New values will be placed in output[0] through output[npad] and in output[npad+size] through output[2*npad+size-1] (note: end values may not be filled in)
215 // extension at left bdry is ... 3 2 1 0 | 0 1 2 3 ...
216 // same for right boundary
217 // if right_ext=1 then ... 3 2 1 0 | 1 2 3
symmetric_extension(float * output,const int size,const int left_ext,const int right_ext)218 static void symmetric_extension(float *output, const int size, const int left_ext, const int right_ext)
219 {
220     int first = NPAD;
221     int last = NPAD - 1 + size;
222     const int originalLast = last;
223     int i, nextend, idx;
224 
225     if (left_ext == 2)
226         output[--first] = output[NPAD];
227     if (right_ext == 2)
228         output[++last] = output[originalLast];
229 
230     // extend left end
231     nextend = first;
232     for (i = 0; i < nextend; i++)
233         output[--first] = output[NPAD + 1 + i];
234 
235     idx = NPAD + NPAD - 1 + size;
236 
237     // extend right end
238     nextend = idx - last;
239     for (i = 0; i < nextend; i++)
240         output[++last] = output[originalLast - 1 - i];
241 }
242 
transform_step(float * input,float * output,const int size,const int low_size,VagueDenoiserContext * s)243 static void transform_step(float *input, float *output, const int size, const int low_size, VagueDenoiserContext *s)
244 {
245     int i;
246 
247     symmetric_extension(input, size, 1, 1);
248 
249     for (i = NPAD; i < NPAD + low_size; i++) {
250         const float a = input[2 * i - 14] * analysis_low[0];
251         const float b = input[2 * i - 13] * analysis_low[1];
252         const float c = input[2 * i - 12] * analysis_low[2];
253         const float d = input[2 * i - 11] * analysis_low[3];
254         const float e = input[2 * i - 10] * analysis_low[4];
255         const float f = input[2 * i -  9] * analysis_low[3];
256         const float g = input[2 * i -  8] * analysis_low[2];
257         const float h = input[2 * i -  7] * analysis_low[1];
258         const float k = input[2 * i -  6] * analysis_low[0];
259 
260         output[i] = a + b + c + d + e + f + g + h + k;
261     }
262 
263     for (i = NPAD; i < NPAD + low_size; i++) {
264         const float a = input[2 * i - 12] * analysis_high[0];
265         const float b = input[2 * i - 11] * analysis_high[1];
266         const float c = input[2 * i - 10] * analysis_high[2];
267         const float d = input[2 * i -  9] * analysis_high[3];
268         const float e = input[2 * i -  8] * analysis_high[2];
269         const float f = input[2 * i -  7] * analysis_high[1];
270         const float g = input[2 * i -  6] * analysis_high[0];
271 
272         output[i + low_size] = a + b + c + d + e + f + g;
273     }
274 }
275 
invert_step(const float * input,float * output,float * temp,const int size,VagueDenoiserContext * s)276 static void invert_step(const float *input, float *output, float *temp, const int size, VagueDenoiserContext *s)
277 {
278     const int low_size = (size + 1) >> 1;
279     const int high_size = size >> 1;
280     int left_ext = 1, right_ext, i;
281     int findex;
282 
283     memcpy(temp + NPAD, input + NPAD, low_size * sizeof(float));
284 
285     right_ext = (size % 2 == 0) ? 2 : 1;
286     symmetric_extension(temp, low_size, left_ext, right_ext);
287 
288     memset(output, 0, (NPAD + NPAD + size) * sizeof(float));
289     findex = (size + 2) >> 1;
290 
291     for (i = 9; i < findex + 11; i++) {
292         const float a = temp[i] * synthesis_low[0];
293         const float b = temp[i] * synthesis_low[1];
294         const float c = temp[i] * synthesis_low[2];
295         const float d = temp[i] * synthesis_low[3];
296 
297         output[2 * i - 13] += a;
298         output[2 * i - 12] += b;
299         output[2 * i - 11] += c;
300         output[2 * i - 10] += d;
301         output[2 * i -  9] += c;
302         output[2 * i -  8] += b;
303         output[2 * i -  7] += a;
304     }
305 
306     memcpy(temp + NPAD, input + NPAD + low_size, high_size * sizeof(float));
307 
308     left_ext = 2;
309     right_ext = (size % 2 == 0) ? 1 : 2;
310     symmetric_extension(temp, high_size, left_ext, right_ext);
311 
312     for (i = 8; i < findex + 11; i++) {
313         const float a = temp[i] * synthesis_high[0];
314         const float b = temp[i] * synthesis_high[1];
315         const float c = temp[i] * synthesis_high[2];
316         const float d = temp[i] * synthesis_high[3];
317         const float e = temp[i] * synthesis_high[4];
318 
319         output[2 * i - 13] += a;
320         output[2 * i - 12] += b;
321         output[2 * i - 11] += c;
322         output[2 * i - 10] += d;
323         output[2 * i -  9] += e;
324         output[2 * i -  8] += d;
325         output[2 * i -  7] += c;
326         output[2 * i -  6] += b;
327         output[2 * i -  5] += a;
328     }
329 }
330 
hard_thresholding(float * block,const int width,const int height,const int stride,const float threshold,const float percent)331 static void hard_thresholding(float *block, const int width, const int height,
332                               const int stride, const float threshold,
333                               const float percent)
334 {
335     const float frac = 1.f - percent * 0.01f;
336     int y, x;
337 
338     for (y = 0; y < height; y++) {
339         for (x = 0; x < width; x++) {
340             if (FFABS(block[x]) <= threshold)
341                 block[x] *= frac;
342         }
343         block += stride;
344     }
345 }
346 
soft_thresholding(float * block,const int width,const int height,const int stride,const float threshold,const float percent)347 static void soft_thresholding(float *block, const int width, const int height, const int stride,
348                               const float threshold, const float percent)
349 {
350     const float frac = 1.f - percent * 0.01f;
351     const float shift = threshold * 0.01f * percent;
352     int y, x;
353 
354     for (y = 0; y < height; y++) {
355         for (x = 0; x < width; x++) {
356             const float temp = FFABS(block[x]);
357             if (temp <= threshold)
358                 block[x] *= frac;
359             else
360                 block[x] = (block[x] < 0.f ? -1.f : (block[x] > 0.f ? 1.f : 0.f)) * (temp - shift);
361         }
362         block += stride;
363     }
364 }
365 
qian_thresholding(float * block,const int width,const int height,const int stride,const float threshold,const float percent)366 static void qian_thresholding(float *block, const int width, const int height,
367                               const int stride, const float threshold,
368                               const float percent)
369 {
370     const float percent01 = percent * 0.01f;
371     const float tr2 = threshold * threshold * percent01;
372     const float frac = 1.f - percent01;
373     int y, x;
374 
375     for (y = 0; y < height; y++) {
376         for (x = 0; x < width; x++) {
377             const float temp = FFABS(block[x]);
378             if (temp <= threshold) {
379                 block[x] *= frac;
380             } else {
381                 const float tp2 = temp * temp;
382                 block[x] *= (tp2 - tr2) / tp2;
383             }
384         }
385         block += stride;
386     }
387 }
388 
bayes_threshold(float * block,const int width,const int height,const int stride,const float threshold)389 static float bayes_threshold(float *block, const int width, const int height,
390                               const int stride, const float threshold)
391 {
392     float mean = 0.f;
393 
394     for (int y = 0; y < height; y++) {
395         for (int x = 0; x < width; x++) {
396             mean += block[x] * block[x];
397         }
398         block += stride;
399     }
400 
401     mean /= width * height;
402 
403     return threshold * threshold / (FFMAX(sqrtf(mean - threshold), FLT_EPSILON));
404 }
405 
filter(VagueDenoiserContext * s,AVFrame * in,AVFrame * out)406 static void filter(VagueDenoiserContext *s, AVFrame *in, AVFrame *out)
407 {
408     int p, y, x, i, j;
409 
410     for (p = 0; p < s->nb_planes; p++) {
411         const int height = s->planeheight[p];
412         const int width = s->planewidth[p];
413         const uint8_t *srcp8 = in->data[p];
414         const uint16_t *srcp16 = (const uint16_t *)in->data[p];
415         uint8_t *dstp8 = out->data[p];
416         uint16_t *dstp16 = (uint16_t *)out->data[p];
417         float *output = s->block;
418         int h_low_size0 = width;
419         int v_low_size0 = height;
420         int nsteps_transform = s->nsteps;
421         int nsteps_invert = s->nsteps;
422         const float *input = s->block;
423 
424         if (!((1 << p) & s->planes)) {
425             av_image_copy_plane(out->data[p], out->linesize[p], in->data[p], in->linesize[p],
426                                 s->planewidth[p] * s->bpc, s->planeheight[p]);
427             continue;
428         }
429 
430         if (s->depth <= 8) {
431             for (y = 0; y < height; y++) {
432                 for (x = 0; x < width; x++)
433                     output[x] = srcp8[x];
434                 srcp8 += in->linesize[p];
435                 output += width;
436             }
437         } else {
438             for (y = 0; y < height; y++) {
439                 for (x = 0; x < width; x++)
440                     output[x] = srcp16[x];
441                 srcp16 += in->linesize[p] / 2;
442                 output += width;
443             }
444         }
445 
446         while (nsteps_transform--) {
447             int low_size = (h_low_size0 + 1) >> 1;
448             float *input = s->block;
449             for (j = 0; j < v_low_size0; j++) {
450                 copy(input, s->in + NPAD, h_low_size0);
451                 transform_step(s->in, s->out, h_low_size0, low_size, s);
452                 copy(s->out + NPAD, input, h_low_size0);
453                 input += width;
454             }
455 
456             low_size = (v_low_size0 + 1) >> 1;
457             input = s->block;
458             for (j = 0; j < h_low_size0; j++) {
459                 copyv(input, width, s->in + NPAD, v_low_size0);
460                 transform_step(s->in, s->out, v_low_size0, low_size, s);
461                 copyh(s->out + NPAD, input, width, v_low_size0);
462                 input++;
463             }
464 
465             h_low_size0 = (h_low_size0 + 1) >> 1;
466             v_low_size0 = (v_low_size0 + 1) >> 1;
467         }
468 
469         if (s->type == 0) {
470             s->thresholding(s->block, width, height, width, s->threshold, s->percent);
471         } else {
472             for (int n = 0; n < s->nsteps; n++) {
473                 float threshold;
474                 float *block;
475 
476                 if (n == s->nsteps - 1) {
477                     threshold = bayes_threshold(s->block, s->hlowsize[p][n], s->vlowsize[p][n], width, s->threshold);
478                     s->thresholding(s->block, s->hlowsize[p][n], s->vlowsize[p][n], width, threshold, s->percent);
479                 }
480                 block = s->block + s->hlowsize[p][n];
481                 threshold = bayes_threshold(block, s->hhighsize[p][n], s->vlowsize[p][n], width, s->threshold);
482                 s->thresholding(block, s->hhighsize[p][n], s->vlowsize[p][n], width, threshold, s->percent);
483                 block = s->block + s->vlowsize[p][n] * width;
484                 threshold = bayes_threshold(block, s->hlowsize[p][n], s->vhighsize[p][n], width, s->threshold);
485                 s->thresholding(block, s->hlowsize[p][n], s->vhighsize[p][n], width, threshold, s->percent);
486                 block = s->block + s->hlowsize[p][n] + s->vlowsize[p][n] * width;
487                 threshold = bayes_threshold(block, s->hhighsize[p][n], s->vhighsize[p][n], width, s->threshold);
488                 s->thresholding(block, s->hhighsize[p][n], s->vhighsize[p][n], width, threshold, s->percent);
489             }
490         }
491 
492         while (nsteps_invert--) {
493             const int idx = s->vlowsize[p][nsteps_invert]  + s->vhighsize[p][nsteps_invert];
494             const int idx2 = s->hlowsize[p][nsteps_invert] + s->hhighsize[p][nsteps_invert];
495             float * idx3 = s->block;
496             for (i = 0; i < idx2; i++) {
497                 copyv(idx3, width, s->in + NPAD, idx);
498                 invert_step(s->in, s->out, s->tmp, idx, s);
499                 copyh(s->out + NPAD, idx3, width, idx);
500                 idx3++;
501             }
502 
503             idx3 = s->block;
504             for (i = 0; i < idx; i++) {
505                 copy(idx3, s->in + NPAD, idx2);
506                 invert_step(s->in, s->out, s->tmp, idx2, s);
507                 copy(s->out + NPAD, idx3, idx2);
508                 idx3 += width;
509             }
510         }
511 
512         if (s->depth <= 8) {
513             for (y = 0; y < height; y++) {
514                 for (x = 0; x < width; x++)
515                     dstp8[x] = av_clip_uint8(input[x] + 0.5f);
516                 input += width;
517                 dstp8 += out->linesize[p];
518             }
519         } else {
520             for (y = 0; y < height; y++) {
521                 for (x = 0; x < width; x++)
522                     dstp16[x] = av_clip(input[x] + 0.5f, 0, s->peak);
523                 input += width;
524                 dstp16 += out->linesize[p] / 2;
525             }
526         }
527     }
528 }
529 
filter_frame(AVFilterLink * inlink,AVFrame * in)530 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
531 {
532     AVFilterContext *ctx  = inlink->dst;
533     VagueDenoiserContext *s = ctx->priv;
534     AVFilterLink *outlink = ctx->outputs[0];
535     AVFrame *out;
536     int direct = av_frame_is_writable(in);
537 
538     if (direct) {
539         out = in;
540     } else {
541         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
542         if (!out) {
543             av_frame_free(&in);
544             return AVERROR(ENOMEM);
545         }
546 
547         av_frame_copy_props(out, in);
548     }
549 
550     filter(s, in, out);
551 
552     if (!direct)
553         av_frame_free(&in);
554 
555     return ff_filter_frame(outlink, out);
556 }
557 
init(AVFilterContext * ctx)558 static av_cold int init(AVFilterContext *ctx)
559 {
560     VagueDenoiserContext *s = ctx->priv;
561 
562     switch (s->method) {
563     case 0:
564         s->thresholding = hard_thresholding;
565         break;
566     case 1:
567         s->thresholding = soft_thresholding;
568         break;
569     case 2:
570         s->thresholding = qian_thresholding;
571         break;
572     }
573 
574     return 0;
575 }
576 
uninit(AVFilterContext * ctx)577 static av_cold void uninit(AVFilterContext *ctx)
578 {
579     VagueDenoiserContext *s = ctx->priv;
580 
581     av_freep(&s->block);
582     av_freep(&s->in);
583     av_freep(&s->out);
584     av_freep(&s->tmp);
585 }
586 
587 static const AVFilterPad vaguedenoiser_inputs[] = {
588     {
589         .name         = "default",
590         .type         = AVMEDIA_TYPE_VIDEO,
591         .config_props = config_input,
592         .filter_frame = filter_frame,
593     },
594 };
595 
596 
597 static const AVFilterPad vaguedenoiser_outputs[] = {
598     {
599         .name = "default",
600         .type = AVMEDIA_TYPE_VIDEO
601     },
602 };
603 
604 const AVFilter ff_vf_vaguedenoiser = {
605     .name          = "vaguedenoiser",
606     .description   = NULL_IF_CONFIG_SMALL("Apply a Wavelet based Denoiser."),
607     .priv_size     = sizeof(VagueDenoiserContext),
608     .priv_class    = &vaguedenoiser_class,
609     .init          = init,
610     .uninit        = uninit,
611     FILTER_INPUTS(vaguedenoiser_inputs),
612     FILTER_OUTPUTS(vaguedenoiser_outputs),
613     FILTER_PIXFMTS_ARRAY(pix_fmts),
614     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
615 };
616