blob: d6c14bb3d880381d08d92edf7f84846cce0bb3bd
1 | /* |
2 | * Copyright (c) 2003 Daniel Moreno <comac AT comac DOT darktech DOT org> |
3 | * Copyright (c) 2010 Baptiste Coudurier |
4 | * Copyright (c) 2012 Loren Merritt |
5 | * |
6 | * This file is part of FFmpeg, ported from MPlayer. |
7 | * |
8 | * FFmpeg is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * FFmpeg is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with FFmpeg; if not, write to the Free Software Foundation, Inc., |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21 | */ |
22 | |
23 | /** |
24 | * @file |
25 | * high quality 3d video denoiser, ported from MPlayer |
26 | * libmpcodecs/vf_hqdn3d.c. |
27 | */ |
28 | |
29 | #include <float.h> |
30 | |
31 | #include "config.h" |
32 | #include "libavutil/attributes.h" |
33 | #include "libavutil/common.h" |
34 | #include "libavutil/pixdesc.h" |
35 | #include "libavutil/intreadwrite.h" |
36 | #include "libavutil/opt.h" |
37 | |
38 | #include "avfilter.h" |
39 | #include "formats.h" |
40 | #include "internal.h" |
41 | #include "video.h" |
42 | #include "vf_hqdn3d.h" |
43 | |
44 | #define LUT_BITS (depth==16 ? 8 : 4) |
45 | #define LOAD(x) (((depth == 8 ? src[x] : AV_RN16A(src + (x) * 2)) << (16 - depth))\ |
46 | + (((1 << (16 - depth)) - 1) >> 1)) |
47 | #define STORE(x,val) (depth == 8 ? dst[x] = (val) >> (16 - depth) : \ |
48 | AV_WN16A(dst + (x) * 2, (val) >> (16 - depth))) |
49 | |
50 | av_always_inline |
51 | static uint32_t lowpass(int prev, int cur, int16_t *coef, int depth) |
52 | { |
53 | int d = (prev - cur) >> (8 - LUT_BITS); |
54 | return cur + coef[d]; |
55 | } |
56 | |
57 | av_always_inline |
58 | static void denoise_temporal(uint8_t *src, uint8_t *dst, |
59 | uint16_t *frame_ant, |
60 | int w, int h, int sstride, int dstride, |
61 | int16_t *temporal, int depth) |
62 | { |
63 | long x, y; |
64 | uint32_t tmp; |
65 | |
66 | temporal += 256 << LUT_BITS; |
67 | |
68 | for (y = 0; y < h; y++) { |
69 | for (x = 0; x < w; x++) { |
70 | frame_ant[x] = tmp = lowpass(frame_ant[x], LOAD(x), temporal, depth); |
71 | STORE(x, tmp); |
72 | } |
73 | src += sstride; |
74 | dst += dstride; |
75 | frame_ant += w; |
76 | } |
77 | } |
78 | |
79 | av_always_inline |
80 | static void denoise_spatial(HQDN3DContext *s, |
81 | uint8_t *src, uint8_t *dst, |
82 | uint16_t *line_ant, uint16_t *frame_ant, |
83 | int w, int h, int sstride, int dstride, |
84 | int16_t *spatial, int16_t *temporal, int depth) |
85 | { |
86 | long x, y; |
87 | uint32_t pixel_ant; |
88 | uint32_t tmp; |
89 | |
90 | spatial += 256 << LUT_BITS; |
91 | temporal += 256 << LUT_BITS; |
92 | |
93 | /* First line has no top neighbor. Only left one for each tmp and |
94 | * last frame */ |
95 | pixel_ant = LOAD(0); |
96 | for (x = 0; x < w; x++) { |
97 | line_ant[x] = tmp = pixel_ant = lowpass(pixel_ant, LOAD(x), spatial, depth); |
98 | frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); |
99 | STORE(x, tmp); |
100 | } |
101 | |
102 | for (y = 1; y < h; y++) { |
103 | src += sstride; |
104 | dst += dstride; |
105 | frame_ant += w; |
106 | if (s->denoise_row[depth]) { |
107 | s->denoise_row[depth](src, dst, line_ant, frame_ant, w, spatial, temporal); |
108 | continue; |
109 | } |
110 | pixel_ant = LOAD(0); |
111 | for (x = 0; x < w-1; x++) { |
112 | line_ant[x] = tmp = lowpass(line_ant[x], pixel_ant, spatial, depth); |
113 | pixel_ant = lowpass(pixel_ant, LOAD(x+1), spatial, depth); |
114 | frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); |
115 | STORE(x, tmp); |
116 | } |
117 | line_ant[x] = tmp = lowpass(line_ant[x], pixel_ant, spatial, depth); |
118 | frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); |
119 | STORE(x, tmp); |
120 | } |
121 | } |
122 | |
123 | av_always_inline |
124 | static int denoise_depth(HQDN3DContext *s, |
125 | uint8_t *src, uint8_t *dst, |
126 | uint16_t *line_ant, uint16_t **frame_ant_ptr, |
127 | int w, int h, int sstride, int dstride, |
128 | int16_t *spatial, int16_t *temporal, int depth) |
129 | { |
130 | // FIXME: For 16-bit depth, frame_ant could be a pointer to the previous |
131 | // filtered frame rather than a separate buffer. |
132 | long x, y; |
133 | uint16_t *frame_ant = *frame_ant_ptr; |
134 | if (!frame_ant) { |
135 | uint8_t *frame_src = src; |
136 | *frame_ant_ptr = frame_ant = av_malloc_array(w, h*sizeof(uint16_t)); |
137 | if (!frame_ant) |
138 | return AVERROR(ENOMEM); |
139 | for (y = 0; y < h; y++, src += sstride, frame_ant += w) |
140 | for (x = 0; x < w; x++) |
141 | frame_ant[x] = LOAD(x); |
142 | src = frame_src; |
143 | frame_ant = *frame_ant_ptr; |
144 | } |
145 | |
146 | if (spatial[0]) |
147 | denoise_spatial(s, src, dst, line_ant, frame_ant, |
148 | w, h, sstride, dstride, spatial, temporal, depth); |
149 | else |
150 | denoise_temporal(src, dst, frame_ant, |
151 | w, h, sstride, dstride, temporal, depth); |
152 | emms_c(); |
153 | return 0; |
154 | } |
155 | |
156 | #define denoise(...) \ |
157 | do { \ |
158 | int ret = AVERROR_BUG; \ |
159 | switch (s->depth) { \ |
160 | case 8: ret = denoise_depth(__VA_ARGS__, 8); break; \ |
161 | case 9: ret = denoise_depth(__VA_ARGS__, 9); break; \ |
162 | case 10: ret = denoise_depth(__VA_ARGS__, 10); break; \ |
163 | case 16: ret = denoise_depth(__VA_ARGS__, 16); break; \ |
164 | } \ |
165 | if (ret < 0) { \ |
166 | av_frame_free(&out); \ |
167 | if (!direct) \ |
168 | av_frame_free(&in); \ |
169 | return ret; \ |
170 | } \ |
171 | } while (0) |
172 | |
173 | static int16_t *precalc_coefs(double dist25, int depth) |
174 | { |
175 | int i; |
176 | double gamma, simil, C; |
177 | int16_t *ct = av_malloc((512<<LUT_BITS)*sizeof(int16_t)); |
178 | if (!ct) |
179 | return NULL; |
180 | |
181 | gamma = log(0.25) / log(1.0 - FFMIN(dist25,252.0)/255.0 - 0.00001); |
182 | |
183 | for (i = -256<<LUT_BITS; i < 256<<LUT_BITS; i++) { |
184 | double f = ((i<<(9-LUT_BITS)) + (1<<(8-LUT_BITS)) - 1) / 512.0; // midpoint of the bin |
185 | simil = FFMAX(0, 1.0 - fabs(f) / 255.0); |
186 | C = pow(simil, gamma) * 256.0 * f; |
187 | ct[(256<<LUT_BITS)+i] = lrint(C); |
188 | } |
189 | |
190 | ct[0] = !!dist25; |
191 | return ct; |
192 | } |
193 | |
194 | #define PARAM1_DEFAULT 4.0 |
195 | #define PARAM2_DEFAULT 3.0 |
196 | #define PARAM3_DEFAULT 6.0 |
197 | |
198 | static av_cold int init(AVFilterContext *ctx) |
199 | { |
200 | HQDN3DContext *s = ctx->priv; |
201 | |
202 | if (!s->strength[LUMA_SPATIAL]) |
203 | s->strength[LUMA_SPATIAL] = PARAM1_DEFAULT; |
204 | if (!s->strength[CHROMA_SPATIAL]) |
205 | s->strength[CHROMA_SPATIAL] = PARAM2_DEFAULT * s->strength[LUMA_SPATIAL] / PARAM1_DEFAULT; |
206 | if (!s->strength[LUMA_TMP]) |
207 | s->strength[LUMA_TMP] = PARAM3_DEFAULT * s->strength[LUMA_SPATIAL] / PARAM1_DEFAULT; |
208 | if (!s->strength[CHROMA_TMP]) |
209 | s->strength[CHROMA_TMP] = s->strength[LUMA_TMP] * s->strength[CHROMA_SPATIAL] / s->strength[LUMA_SPATIAL]; |
210 | |
211 | av_log(ctx, AV_LOG_VERBOSE, "ls:%f cs:%f lt:%f ct:%f\n", |
212 | s->strength[LUMA_SPATIAL], s->strength[CHROMA_SPATIAL], |
213 | s->strength[LUMA_TMP], s->strength[CHROMA_TMP]); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static av_cold void uninit(AVFilterContext *ctx) |
219 | { |
220 | HQDN3DContext *s = ctx->priv; |
221 | |
222 | av_freep(&s->coefs[0]); |
223 | av_freep(&s->coefs[1]); |
224 | av_freep(&s->coefs[2]); |
225 | av_freep(&s->coefs[3]); |
226 | av_freep(&s->line); |
227 | av_freep(&s->frame_prev[0]); |
228 | av_freep(&s->frame_prev[1]); |
229 | av_freep(&s->frame_prev[2]); |
230 | } |
231 | |
232 | static int query_formats(AVFilterContext *ctx) |
233 | { |
234 | static const enum AVPixelFormat pix_fmts[] = { |
235 | AV_PIX_FMT_YUV420P, |
236 | AV_PIX_FMT_YUV422P, |
237 | AV_PIX_FMT_YUV444P, |
238 | AV_PIX_FMT_YUV410P, |
239 | AV_PIX_FMT_YUV411P, |
240 | AV_PIX_FMT_YUV440P, |
241 | AV_PIX_FMT_YUVJ420P, |
242 | AV_PIX_FMT_YUVJ422P, |
243 | AV_PIX_FMT_YUVJ444P, |
244 | AV_PIX_FMT_YUVJ440P, |
245 | AV_PIX_FMT_YUV420P9, |
246 | AV_PIX_FMT_YUV422P9, |
247 | AV_PIX_FMT_YUV444P9, |
248 | AV_PIX_FMT_YUV420P10, |
249 | AV_PIX_FMT_YUV422P10, |
250 | AV_PIX_FMT_YUV444P10, |
251 | AV_PIX_FMT_YUV420P16, |
252 | AV_PIX_FMT_YUV422P16, |
253 | AV_PIX_FMT_YUV444P16, |
254 | AV_PIX_FMT_NONE |
255 | }; |
256 | AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); |
257 | if (!fmts_list) |
258 | return AVERROR(ENOMEM); |
259 | return ff_set_common_formats(ctx, fmts_list); |
260 | } |
261 | |
262 | static int config_input(AVFilterLink *inlink) |
263 | { |
264 | HQDN3DContext *s = inlink->dst->priv; |
265 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
266 | int i; |
267 | |
268 | uninit(inlink->dst); |
269 | |
270 | s->hsub = desc->log2_chroma_w; |
271 | s->vsub = desc->log2_chroma_h; |
272 | s->depth = desc->comp[0].depth; |
273 | |
274 | s->line = av_malloc_array(inlink->w, sizeof(*s->line)); |
275 | if (!s->line) |
276 | return AVERROR(ENOMEM); |
277 | |
278 | for (i = 0; i < 4; i++) { |
279 | s->coefs[i] = precalc_coefs(s->strength[i], s->depth); |
280 | if (!s->coefs[i]) |
281 | return AVERROR(ENOMEM); |
282 | } |
283 | |
284 | if (ARCH_X86) |
285 | ff_hqdn3d_init_x86(s); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
291 | { |
292 | AVFilterContext *ctx = inlink->dst; |
293 | HQDN3DContext *s = ctx->priv; |
294 | AVFilterLink *outlink = ctx->outputs[0]; |
295 | |
296 | AVFrame *out; |
297 | int c, direct = av_frame_is_writable(in) && !ctx->is_disabled; |
298 | |
299 | if (direct) { |
300 | out = in; |
301 | } else { |
302 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
303 | if (!out) { |
304 | av_frame_free(&in); |
305 | return AVERROR(ENOMEM); |
306 | } |
307 | |
308 | av_frame_copy_props(out, in); |
309 | } |
310 | |
311 | for (c = 0; c < 3; c++) { |
312 | denoise(s, in->data[c], out->data[c], |
313 | s->line, &s->frame_prev[c], |
314 | AV_CEIL_RSHIFT(in->width, (!!c * s->hsub)), |
315 | AV_CEIL_RSHIFT(in->height, (!!c * s->vsub)), |
316 | in->linesize[c], out->linesize[c], |
317 | s->coefs[c ? CHROMA_SPATIAL : LUMA_SPATIAL], |
318 | s->coefs[c ? CHROMA_TMP : LUMA_TMP]); |
319 | } |
320 | |
321 | if (ctx->is_disabled) { |
322 | av_frame_free(&out); |
323 | return ff_filter_frame(outlink, in); |
324 | } |
325 | |
326 | if (!direct) |
327 | av_frame_free(&in); |
328 | |
329 | return ff_filter_frame(outlink, out); |
330 | } |
331 | |
332 | #define OFFSET(x) offsetof(HQDN3DContext, x) |
333 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
334 | static const AVOption hqdn3d_options[] = { |
335 | { "luma_spatial", "spatial luma strength", OFFSET(strength[LUMA_SPATIAL]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, |
336 | { "chroma_spatial", "spatial chroma strength", OFFSET(strength[CHROMA_SPATIAL]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, |
337 | { "luma_tmp", "temporal luma strength", OFFSET(strength[LUMA_TMP]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, |
338 | { "chroma_tmp", "temporal chroma strength", OFFSET(strength[CHROMA_TMP]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, |
339 | { NULL } |
340 | }; |
341 | |
342 | AVFILTER_DEFINE_CLASS(hqdn3d); |
343 | |
344 | static const AVFilterPad avfilter_vf_hqdn3d_inputs[] = { |
345 | { |
346 | .name = "default", |
347 | .type = AVMEDIA_TYPE_VIDEO, |
348 | .config_props = config_input, |
349 | .filter_frame = filter_frame, |
350 | }, |
351 | { NULL } |
352 | }; |
353 | |
354 | |
355 | static const AVFilterPad avfilter_vf_hqdn3d_outputs[] = { |
356 | { |
357 | .name = "default", |
358 | .type = AVMEDIA_TYPE_VIDEO |
359 | }, |
360 | { NULL } |
361 | }; |
362 | |
363 | AVFilter ff_vf_hqdn3d = { |
364 | .name = "hqdn3d", |
365 | .description = NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."), |
366 | .priv_size = sizeof(HQDN3DContext), |
367 | .priv_class = &hqdn3d_class, |
368 | .init = init, |
369 | .uninit = uninit, |
370 | .query_formats = query_formats, |
371 | .inputs = avfilter_vf_hqdn3d_inputs, |
372 | .outputs = avfilter_vf_hqdn3d_outputs, |
373 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, |
374 | }; |
375 |