]> git.sesse.net Git - ffmpeg/blob - libavfilter/af_anlmdn.c
avfilter/af_anlmdn: ignore too small values
[ffmpeg] / libavfilter / af_anlmdn.c
1 /*
2  * Copyright (c) 2019 Paul B Mahol
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include <float.h>
22
23 #include "libavutil/avassert.h"
24 #include "libavutil/audio_fifo.h"
25 #include "libavutil/opt.h"
26 #include "avfilter.h"
27 #include "audio.h"
28 #include "formats.h"
29
30 #define SQR(x) ((x) * (x))
31
32 typedef struct AudioNLMeansContext {
33     const AVClass *class;
34
35     float a;
36     int64_t pd;
37     int64_t rd;
38
39     int K;
40     int S;
41     int N;
42     int H;
43
44     int offset;
45     AVFrame *in;
46     AVFrame *cache;
47
48     int64_t pts;
49
50     AVAudioFifo *fifo;
51
52     float (*compute_distance)(const float *f1, const float *f2, int K);
53 } AudioNLMeansContext;
54
55 #define OFFSET(x) offsetof(AudioNLMeansContext, x)
56 #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
57
58 static const AVOption anlmdn_options[] = {
59     { "s", "set denoising strength", OFFSET(a),  AV_OPT_TYPE_FLOAT,    {.dbl=1},       1,   9999, AF },
60     { "p", "set patch duration",     OFFSET(pd), AV_OPT_TYPE_DURATION, {.i64=2000}, 1000, 100000, AF },
61     { "r", "set research duration",  OFFSET(rd), AV_OPT_TYPE_DURATION, {.i64=6000}, 2000, 300000, AF },
62     { NULL }
63 };
64
65 AVFILTER_DEFINE_CLASS(anlmdn);
66
67 static int query_formats(AVFilterContext *ctx)
68 {
69     AVFilterFormats *formats = NULL;
70     AVFilterChannelLayouts *layouts = NULL;
71     static const enum AVSampleFormat sample_fmts[] = {
72         AV_SAMPLE_FMT_FLTP,
73         AV_SAMPLE_FMT_NONE
74     };
75     int ret;
76
77     formats = ff_make_format_list(sample_fmts);
78     if (!formats)
79         return AVERROR(ENOMEM);
80     ret = ff_set_common_formats(ctx, formats);
81     if (ret < 0)
82         return ret;
83
84     layouts = ff_all_channel_counts();
85     if (!layouts)
86         return AVERROR(ENOMEM);
87
88     ret = ff_set_common_channel_layouts(ctx, layouts);
89     if (ret < 0)
90         return ret;
91
92     formats = ff_all_samplerates();
93     return ff_set_common_samplerates(ctx, formats);
94 }
95
96 static float compute_distance_ssd(const float *f1, const float *f2, int K)
97 {
98     float distance = 0.;
99
100     for (int k = -K; k <= K; k++)
101         distance += SQR(f1[k] - f2[k]);
102
103     return distance;
104 }
105
106 static int config_output(AVFilterLink *outlink)
107 {
108     AVFilterContext *ctx = outlink->src;
109     AudioNLMeansContext *s = ctx->priv;
110
111     s->K = av_rescale(s->pd, outlink->sample_rate, AV_TIME_BASE);
112     s->S = av_rescale(s->rd, outlink->sample_rate, AV_TIME_BASE);
113
114     s->pts = AV_NOPTS_VALUE;
115     s->H = s->K * 2 + 1;
116     s->N = s->H + (s->K + s->S) * 2;
117
118     av_frame_free(&s->in);
119     av_frame_free(&s->cache);
120     s->in = ff_get_audio_buffer(outlink, s->N);
121     if (!s->in)
122         return AVERROR(ENOMEM);
123
124     s->cache = ff_get_audio_buffer(outlink, s->S * 2);
125     if (!s->cache)
126         return AVERROR(ENOMEM);
127
128     s->fifo = av_audio_fifo_alloc(outlink->format, outlink->channels, s->N);
129     if (!s->fifo)
130         return AVERROR(ENOMEM);
131
132     s->compute_distance = compute_distance_ssd;
133
134     return 0;
135 }
136
137 static int filter_channel(AVFilterContext *ctx, void *arg, int ch, int nb_jobs)
138 {
139     AudioNLMeansContext *s = ctx->priv;
140     AVFrame *out = arg;
141     const int S = s->S;
142     const int K = s->K;
143     const float *f = (const float *)(s->in->extended_data[ch]) + K;
144     float *cache = (float *)s->cache->extended_data[ch];
145     const float sw = 32768.f / s->a;
146     float *dst = (float *)out->extended_data[ch] + s->offset;
147
148     for (int i = S; i < s->H + S; i++) {
149         float P = 0.f, Q = 0.f;
150         int v = 0;
151
152         if (i == S) {
153             for (int j = i - S; j <= i + S; j++) {
154                 if (i == j)
155                     continue;
156                 cache[v++] = s->compute_distance(f + i, f + j, K);
157             }
158         } else {
159             for (int j = i - S; j < i; j++, v++)
160                 cache[v] = cache[v] - SQR(f[i - K - 1] - f[j - K - 1]) + SQR(f[i + K] - f[j + K]);
161
162             for (int j = i + 1; j <= i + S; j++, v++)
163                 cache[v] = cache[v] - SQR(f[i - K - 1] - f[j - K - 1]) + SQR(f[i + K] - f[j + K]);
164         }
165
166         for (int j = 0; j < v; j++) {
167             const float distance = cache[j];
168             float w;
169
170             av_assert0(distance >= 0.f);
171             w = -distance * sw;
172             if (w < -11.f)
173                 continue;
174             w = expf(w);
175             P += w * f[i - S + j + (j >= S)];
176             Q += w;
177         }
178
179         P += f[i];
180         Q += 1;
181
182         dst[i - S] = P / Q;
183     }
184
185     return 0;
186 }
187
188 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
189 {
190     AVFilterContext *ctx = inlink->dst;
191     AVFilterLink *outlink = ctx->outputs[0];
192     AudioNLMeansContext *s = ctx->priv;
193     AVFrame *out = NULL;
194     int available, wanted, ret;
195
196     if (s->pts == AV_NOPTS_VALUE)
197         s->pts = in->pts;
198
199     ret = av_audio_fifo_write(s->fifo, (void **)in->extended_data,
200                               in->nb_samples);
201     av_frame_free(&in);
202
203     s->offset = 0;
204     available = av_audio_fifo_size(s->fifo);
205     wanted = (available / s->H) * s->H;
206
207     if (wanted >= s->H && available >= s->N) {
208         out = ff_get_audio_buffer(outlink, wanted);
209         if (!out)
210             return AVERROR(ENOMEM);
211     }
212
213     while (available >= s->N) {
214         ret = av_audio_fifo_peek(s->fifo, (void **)s->in->extended_data, s->N);
215         if (ret < 0)
216             break;
217
218         ctx->internal->execute(ctx, filter_channel, out, NULL, inlink->channels);
219
220         av_audio_fifo_drain(s->fifo, s->H);
221
222         s->offset += s->H;
223         available -= s->H;
224     }
225
226     if (out) {
227         out->pts = s->pts;
228         out->nb_samples = s->offset;
229         s->pts += s->offset;
230
231         return ff_filter_frame(outlink, out);
232     }
233
234     return ret;
235 }
236
237 static av_cold void uninit(AVFilterContext *ctx)
238 {
239     AudioNLMeansContext *s = ctx->priv;
240
241     av_audio_fifo_free(s->fifo);
242     av_frame_free(&s->in);
243     av_frame_free(&s->cache);
244 }
245
246 static const AVFilterPad inputs[] = {
247     {
248         .name         = "default",
249         .type         = AVMEDIA_TYPE_AUDIO,
250         .filter_frame = filter_frame,
251     },
252     { NULL }
253 };
254
255 static const AVFilterPad outputs[] = {
256     {
257         .name          = "default",
258         .type          = AVMEDIA_TYPE_AUDIO,
259         .config_props  = config_output,
260     },
261     { NULL }
262 };
263
264 AVFilter ff_af_anlmdn = {
265     .name          = "anlmdn",
266     .description   = NULL_IF_CONFIG_SMALL("Reduce broadband noise from stream using Non-Local Means."),
267     .query_formats = query_formats,
268     .priv_size     = sizeof(AudioNLMeansContext),
269     .priv_class    = &anlmdn_class,
270     .uninit        = uninit,
271     .inputs        = inputs,
272     .outputs       = outputs,
273     .flags         = AVFILTER_FLAG_SLICE_THREADS,
274 };