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
6 * This file is part of FFmpeg, ported from MPlayer.
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.
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.
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.
25 * high quality 3d video denoiser, ported from MPlayer
26 * libmpcodecs/vf_hqdn3d.c.
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"
42 #include "vf_hqdn3d.h"
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)))
51 static uint32_t lowpass(int prev
, int cur
, int16_t *coef
, int depth
)
53 int d
= (prev
- cur
) >> (8 - LUT_BITS
);
58 static void denoise_temporal(uint8_t *src
, uint8_t *dst
,
60 int w
, int h
, int sstride
, int dstride
,
61 int16_t *temporal
, int depth
)
66 temporal
+= 256 << LUT_BITS
;
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
);
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
)
90 spatial
+= 256 << LUT_BITS
;
91 temporal
+= 256 << LUT_BITS
;
93 /* First line has no top neighbor. Only left one for each tmp and
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
);
102 for (y
= 1; y
< h
; y
++) {
106 if (s
->denoise_row
[depth
]) {
107 s
->denoise_row
[depth
](src
, dst
, line_ant
, frame_ant
, w
, spatial
, temporal
);
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
);
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
);
124 static void 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
)
130 // FIXME: For 16bit depth, frame_ant could be a pointer to the previous
131 // filtered frame rather than a separate buffer.
133 uint16_t *frame_ant
= *frame_ant_ptr
;
135 uint8_t *frame_src
= src
;
136 *frame_ant_ptr
= frame_ant
= av_malloc_array(w
, h
*sizeof(uint16_t));
137 for (y
= 0; y
< h
; y
++, src
+= sstride
, frame_ant
+= w
)
138 for (x
= 0; x
< w
; x
++)
139 frame_ant
[x
] = LOAD(x
);
141 frame_ant
= *frame_ant_ptr
;
145 denoise_spatial(s
, src
, dst
, line_ant
, frame_ant
,
146 w
, h
, sstride
, dstride
, spatial
, temporal
, depth
);
148 denoise_temporal(src
, dst
, frame_ant
,
149 w
, h
, sstride
, dstride
, temporal
, depth
);
153 #define denoise(...) \
155 case 8: denoise_depth(__VA_ARGS__, 8); break;\
156 case 9: denoise_depth(__VA_ARGS__, 9); break;\
157 case 10: denoise_depth(__VA_ARGS__, 10); break;\
158 case 16: denoise_depth(__VA_ARGS__, 16); break;\
161 static int16_t *precalc_coefs(double dist25
, int depth
)
164 double gamma
, simil
, C
;
165 int16_t *ct
= av_malloc((512<<LUT_BITS
)*sizeof(int16_t));
169 gamma
= log(0.25) / log(1.0 - FFMIN(dist25
,252.0)/255.0 - 0.00001);
171 for (i
= -255<<LUT_BITS
; i
<= 255<<LUT_BITS
; i
++) {
172 double f
= ((i
<<(9-LUT_BITS
)) + (1<<(8-LUT_BITS
)) - 1) / 512.0; // midpoint of the bin
173 simil
= 1.0 - FFABS(f
) / 255.0;
174 C
= pow(simil
, gamma
) * 256.0 * f
;
175 ct
[(256<<LUT_BITS
)+i
] = lrint(C
);
182 #define PARAM1_DEFAULT 4.0
183 #define PARAM2_DEFAULT 3.0
184 #define PARAM3_DEFAULT 6.0
186 static av_cold
int init(AVFilterContext
*ctx
)
188 HQDN3DContext
*s
= ctx
->priv
;
190 if (!s
->strength
[LUMA_SPATIAL
])
191 s
->strength
[LUMA_SPATIAL
] = PARAM1_DEFAULT
;
192 if (!s
->strength
[CHROMA_SPATIAL
])
193 s
->strength
[CHROMA_SPATIAL
] = PARAM2_DEFAULT
* s
->strength
[LUMA_SPATIAL
] / PARAM1_DEFAULT
;
194 if (!s
->strength
[LUMA_TMP
])
195 s
->strength
[LUMA_TMP
] = PARAM3_DEFAULT
* s
->strength
[LUMA_SPATIAL
] / PARAM1_DEFAULT
;
196 if (!s
->strength
[CHROMA_TMP
])
197 s
->strength
[CHROMA_TMP
] = s
->strength
[LUMA_TMP
] * s
->strength
[CHROMA_SPATIAL
] / s
->strength
[LUMA_SPATIAL
];
199 av_log(ctx
, AV_LOG_VERBOSE
, "ls:%f cs:%f lt:%f ct:%f\n",
200 s
->strength
[LUMA_SPATIAL
], s
->strength
[CHROMA_SPATIAL
],
201 s
->strength
[LUMA_TMP
], s
->strength
[CHROMA_TMP
]);
206 static av_cold
void uninit(AVFilterContext
*ctx
)
208 HQDN3DContext
*s
= ctx
->priv
;
210 av_freep(&s
->coefs
[0]);
211 av_freep(&s
->coefs
[1]);
212 av_freep(&s
->coefs
[2]);
213 av_freep(&s
->coefs
[3]);
215 av_freep(&s
->frame_prev
[0]);
216 av_freep(&s
->frame_prev
[1]);
217 av_freep(&s
->frame_prev
[2]);
220 static int query_formats(AVFilterContext
*ctx
)
222 static const enum AVPixelFormat pix_fmts
[] = {
236 AV_PIX_FMT_YUV420P10
,
237 AV_PIX_FMT_YUV422P10
,
238 AV_PIX_FMT_YUV444P10
,
239 AV_PIX_FMT_YUV420P16
,
240 AV_PIX_FMT_YUV422P16
,
241 AV_PIX_FMT_YUV444P16
,
245 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
250 static int config_input(AVFilterLink
*inlink
)
252 HQDN3DContext
*s
= inlink
->dst
->priv
;
253 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
258 s
->hsub
= desc
->log2_chroma_w
;
259 s
->vsub
= desc
->log2_chroma_h
;
260 s
->depth
= desc
->comp
[0].depth_minus1
+1;
262 s
->line
= av_malloc_array(inlink
->w
, sizeof(*s
->line
));
264 return AVERROR(ENOMEM
);
266 for (i
= 0; i
< 4; i
++) {
267 s
->coefs
[i
] = precalc_coefs(s
->strength
[i
], s
->depth
);
269 return AVERROR(ENOMEM
);
273 ff_hqdn3d_init_x86(s
);
278 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
280 AVFilterContext
*ctx
= inlink
->dst
;
281 HQDN3DContext
*s
= ctx
->priv
;
282 AVFilterLink
*outlink
= ctx
->outputs
[0];
287 if (av_frame_is_writable(in
) && !ctx
->is_disabled
) {
292 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
295 return AVERROR(ENOMEM
);
298 av_frame_copy_props(out
, in
);
301 for (c
= 0; c
< 3; c
++) {
302 denoise(s
, in
->data
[c
], out
->data
[c
],
303 s
->line
, &s
->frame_prev
[c
],
304 FF_CEIL_RSHIFT(in
->width
, (!!c
* s
->hsub
)),
305 FF_CEIL_RSHIFT(in
->height
, (!!c
* s
->vsub
)),
306 in
->linesize
[c
], out
->linesize
[c
],
307 s
->coefs
[c
? CHROMA_SPATIAL
: LUMA_SPATIAL
],
308 s
->coefs
[c
? CHROMA_TMP
: LUMA_TMP
]);
311 if (ctx
->is_disabled
) {
313 return ff_filter_frame(outlink
, in
);
319 return ff_filter_frame(outlink
, out
);
322 #define OFFSET(x) offsetof(HQDN3DContext, x)
323 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
324 static const AVOption hqdn3d_options
[] = {
325 { "luma_spatial", "spatial luma strength", OFFSET(strength
[LUMA_SPATIAL
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
326 { "chroma_spatial", "spatial chroma strength", OFFSET(strength
[CHROMA_SPATIAL
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
327 { "luma_tmp", "temporal luma strength", OFFSET(strength
[LUMA_TMP
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
328 { "chroma_tmp", "temporal chroma strength", OFFSET(strength
[CHROMA_TMP
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
332 AVFILTER_DEFINE_CLASS(hqdn3d
);
334 static const AVFilterPad avfilter_vf_hqdn3d_inputs
[] = {
337 .type
= AVMEDIA_TYPE_VIDEO
,
338 .config_props
= config_input
,
339 .filter_frame
= filter_frame
,
345 static const AVFilterPad avfilter_vf_hqdn3d_outputs
[] = {
348 .type
= AVMEDIA_TYPE_VIDEO
353 AVFilter ff_vf_hqdn3d
= {
355 .description
= NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."),
356 .priv_size
= sizeof(HQDN3DContext
),
357 .priv_class
= &hqdn3d_class
,
360 .query_formats
= query_formats
,
361 .inputs
= avfilter_vf_hqdn3d_inputs
,
362 .outputs
= avfilter_vf_hqdn3d_outputs
,
363 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
,