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/emms.h"
35 #include "libavutil/mem.h"
36 #include "libavutil/pixdesc.h"
37 #include "libavutil/intreadwrite.h"
38 #include "libavutil/opt.h"
43 #include "vf_hqdn3d.h"
45 #define LUT_BITS (depth==16 ? 8 : 4)
46 #define LOAD(x) (((depth == 8 ? src[x] : AV_RN16A(src + (x) * 2)) << (16 - depth))\
47 + (((1 << (16 - depth)) - 1) >> 1))
48 #define STORE(x,val) (depth == 8 ? dst[x] = (val) >> (16 - depth) : \
49 AV_WN16A(dst + (x) * 2, (val) >> (16 - depth)))
52 static uint32_t lowpass(int prev
, int cur
, int16_t *coef
, int depth
)
54 int d
= (prev
- cur
) >> (8 - LUT_BITS
);
59 static void denoise_temporal(uint8_t *src
, uint8_t *dst
,
61 int w
, int h
, int sstride
, int dstride
,
62 int16_t *temporal
, int depth
)
67 temporal
+= 256 << LUT_BITS
;
69 for (y
= 0; y
< h
; y
++) {
70 for (x
= 0; x
< w
; x
++) {
71 frame_ant
[x
] = tmp
= lowpass(frame_ant
[x
], LOAD(x
), temporal
, depth
);
81 static void denoise_spatial(HQDN3DContext
*s
,
82 uint8_t *src
, uint8_t *dst
,
83 uint16_t *line_ant
, uint16_t *frame_ant
,
84 int w
, int h
, int sstride
, int dstride
,
85 int16_t *spatial
, int16_t *temporal
, int depth
)
91 spatial
+= 256 << LUT_BITS
;
92 temporal
+= 256 << LUT_BITS
;
94 /* First line has no top neighbor. Only left one for each tmp and
97 for (x
= 0; x
< w
; x
++) {
98 line_ant
[x
] = tmp
= pixel_ant
= lowpass(pixel_ant
, LOAD(x
), spatial
, depth
);
99 frame_ant
[x
] = tmp
= lowpass(frame_ant
[x
], tmp
, temporal
, depth
);
103 for (y
= 1; y
< h
; y
++) {
107 if (s
->denoise_row
[depth
]) {
108 s
->denoise_row
[depth
](src
, dst
, line_ant
, frame_ant
, w
, spatial
, temporal
);
112 for (x
= 0; x
< w
-1; x
++) {
113 line_ant
[x
] = tmp
= lowpass(line_ant
[x
], pixel_ant
, spatial
, depth
);
114 pixel_ant
= lowpass(pixel_ant
, LOAD(x
+1), spatial
, depth
);
115 frame_ant
[x
] = tmp
= lowpass(frame_ant
[x
], tmp
, temporal
, depth
);
118 line_ant
[x
] = tmp
= lowpass(line_ant
[x
], pixel_ant
, spatial
, depth
);
119 frame_ant
[x
] = tmp
= lowpass(frame_ant
[x
], tmp
, temporal
, depth
);
125 static int denoise_depth(HQDN3DContext
*s
,
126 uint8_t *src
, uint8_t *dst
,
127 uint16_t *line_ant
, uint16_t **frame_ant_ptr
,
128 int w
, int h
, int sstride
, int dstride
,
129 int16_t *spatial
, int16_t *temporal
, int depth
)
131 // FIXME: For 16-bit depth, frame_ant could be a pointer to the previous
132 // filtered frame rather than a separate buffer.
134 uint16_t *frame_ant
= *frame_ant_ptr
;
136 uint8_t *frame_src
= src
;
137 *frame_ant_ptr
= frame_ant
= av_malloc_array(w
, h
*sizeof(uint16_t));
139 return AVERROR(ENOMEM
);
140 for (y
= 0; y
< h
; y
++, src
+= sstride
, frame_ant
+= w
)
141 for (x
= 0; x
< w
; x
++)
142 frame_ant
[x
] = LOAD(x
);
144 frame_ant
= *frame_ant_ptr
;
148 denoise_spatial(s
, src
, dst
, line_ant
, frame_ant
,
149 w
, h
, sstride
, dstride
, spatial
, temporal
, depth
);
151 denoise_temporal(src
, dst
, frame_ant
,
152 w
, h
, sstride
, dstride
, temporal
, depth
);
157 #define denoise(...) \
159 int ret = AVERROR_BUG; \
160 switch (s->depth) { \
161 case 8: ret = denoise_depth(__VA_ARGS__, 8); break; \
162 case 9: ret = denoise_depth(__VA_ARGS__, 9); break; \
163 case 10: ret = denoise_depth(__VA_ARGS__, 10); break; \
164 case 12: ret = denoise_depth(__VA_ARGS__, 12); break; \
165 case 14: ret = denoise_depth(__VA_ARGS__, 14); break; \
166 case 16: ret = denoise_depth(__VA_ARGS__, 16); break; \
169 av_frame_free(&out); \
171 av_frame_free(&in); \
176 static void precalc_coefs(double dist25
, int depth
, int16_t *ct
)
179 double gamma
, simil
, C
;
181 gamma
= log(0.25) / log(1.0 - FFMIN(dist25
,252.0)/255.0 - 0.00001);
183 for (i
= -(256<<LUT_BITS
); i
< 256<<LUT_BITS
; i
++) {
184 double f
= (i
* (1 << (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
);
193 #define PARAM1_DEFAULT 4.0
194 #define PARAM2_DEFAULT 3.0
195 #define PARAM3_DEFAULT 6.0
197 static av_cold
int init(AVFilterContext
*ctx
)
199 HQDN3DContext
*s
= ctx
->priv
;
201 if (!s
->strength
[LUMA_SPATIAL
])
202 s
->strength
[LUMA_SPATIAL
] = PARAM1_DEFAULT
;
203 if (!s
->strength
[CHROMA_SPATIAL
])
204 s
->strength
[CHROMA_SPATIAL
] = PARAM2_DEFAULT
* s
->strength
[LUMA_SPATIAL
] / PARAM1_DEFAULT
;
205 if (!s
->strength
[LUMA_TMP
])
206 s
->strength
[LUMA_TMP
] = PARAM3_DEFAULT
* s
->strength
[LUMA_SPATIAL
] / PARAM1_DEFAULT
;
207 if (!s
->strength
[CHROMA_TMP
])
208 s
->strength
[CHROMA_TMP
] = s
->strength
[LUMA_TMP
] * s
->strength
[CHROMA_SPATIAL
] / s
->strength
[LUMA_SPATIAL
];
210 av_log(ctx
, AV_LOG_VERBOSE
, "ls:%f cs:%f lt:%f ct:%f\n",
211 s
->strength
[LUMA_SPATIAL
], s
->strength
[CHROMA_SPATIAL
],
212 s
->strength
[LUMA_TMP
], s
->strength
[CHROMA_TMP
]);
217 static av_cold
void uninit(AVFilterContext
*ctx
)
219 HQDN3DContext
*s
= ctx
->priv
;
221 av_freep(&s
->coefs
[0]);
222 av_freep(&s
->coefs
[1]);
223 av_freep(&s
->coefs
[2]);
224 av_freep(&s
->coefs
[3]);
225 av_freep(&s
->line
[0]);
226 av_freep(&s
->line
[1]);
227 av_freep(&s
->line
[2]);
228 av_freep(&s
->frame_prev
[0]);
229 av_freep(&s
->frame_prev
[1]);
230 av_freep(&s
->frame_prev
[2]);
233 static const enum AVPixelFormat pix_fmts
[] = {
234 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV444P
,
235 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV440P
,
236 AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
237 AV_PIX_FMT_YUV420P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV444P9
,
238 AV_PIX_FMT_YUV420P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV444P10
,
239 AV_PIX_FMT_YUV440P10
,
240 AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV420P12
,
241 AV_PIX_FMT_YUV440P12
,
242 AV_PIX_FMT_YUV444P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV420P14
,
243 AV_PIX_FMT_YUV420P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV444P16
,
247 static void calc_coefs(AVFilterContext
*ctx
)
249 HQDN3DContext
*s
= ctx
->priv
;
251 for (int i
= 0; i
< 4; i
++)
252 precalc_coefs(s
->strength
[i
], s
->depth
, s
->coefs
[i
]);
255 static int config_input(AVFilterLink
*inlink
)
257 AVFilterContext
*ctx
= inlink
->dst
;
258 HQDN3DContext
*s
= inlink
->dst
->priv
;
259 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
264 s
->hsub
= desc
->log2_chroma_w
;
265 s
->vsub
= desc
->log2_chroma_h
;
266 s
->depth
= depth
= desc
->comp
[0].depth
;
268 for (i
= 0; i
< 3; i
++) {
269 s
->line
[i
] = av_malloc_array(inlink
->w
, sizeof(*s
->line
[i
]));
271 return AVERROR(ENOMEM
);
274 for (i
= 0; i
< 4; i
++) {
275 s
->coefs
[i
] = av_malloc((512<<LUT_BITS
) * sizeof(int16_t));
277 return AVERROR(ENOMEM
);
283 ff_hqdn3d_init_x86(s
);
289 typedef struct ThreadData
{
294 static int do_denoise(AVFilterContext
*ctx
, void *data
, int job_nr
, int n_jobs
)
296 HQDN3DContext
*s
= ctx
->priv
;
297 const ThreadData
*td
= data
;
298 AVFrame
*out
= td
->out
;
299 AVFrame
*in
= td
->in
;
300 int direct
= td
->direct
;
302 denoise(s
, in
->data
[job_nr
], out
->data
[job_nr
],
303 s
->line
[job_nr
], &s
->frame_prev
[job_nr
],
304 AV_CEIL_RSHIFT(in
->width
, (!!job_nr
* s
->hsub
)),
305 AV_CEIL_RSHIFT(in
->height
, (!!job_nr
* s
->vsub
)),
306 in
->linesize
[job_nr
], out
->linesize
[job_nr
],
307 s
->coefs
[job_nr
? CHROMA_SPATIAL
: LUMA_SPATIAL
],
308 s
->coefs
[job_nr
? CHROMA_TMP
: LUMA_TMP
]);
313 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
315 AVFilterContext
*ctx
= inlink
->dst
;
316 AVFilterLink
*outlink
= ctx
->outputs
[0];
319 int direct
= av_frame_is_writable(in
) && !ctx
->is_disabled
;
325 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
328 return AVERROR(ENOMEM
);
331 av_frame_copy_props(out
, in
);
337 /* one thread per plane */
338 ff_filter_execute(ctx
, do_denoise
, &td
, NULL
, 3);
340 if (ctx
->is_disabled
) {
342 return ff_filter_frame(outlink
, in
);
348 return ff_filter_frame(outlink
, out
);
351 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
352 char *res
, int res_len
, int flags
)
356 ret
= ff_filter_process_command(ctx
, cmd
, args
, res
, res_len
, flags
);
365 #define OFFSET(x) offsetof(HQDN3DContext, x)
366 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM
367 static const AVOption hqdn3d_options
[] = {
368 { "luma_spatial", "spatial luma strength", OFFSET(strength
[LUMA_SPATIAL
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
369 { "chroma_spatial", "spatial chroma strength", OFFSET(strength
[CHROMA_SPATIAL
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
370 { "luma_tmp", "temporal luma strength", OFFSET(strength
[LUMA_TMP
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
371 { "chroma_tmp", "temporal chroma strength", OFFSET(strength
[CHROMA_TMP
]), AV_OPT_TYPE_DOUBLE
, { .dbl
= 0.0 }, 0, DBL_MAX
, FLAGS
},
375 AVFILTER_DEFINE_CLASS(hqdn3d
);
377 static const AVFilterPad avfilter_vf_hqdn3d_inputs
[] = {
380 .type
= AVMEDIA_TYPE_VIDEO
,
381 .config_props
= config_input
,
382 .filter_frame
= filter_frame
,
387 const FFFilter ff_vf_hqdn3d
= {
389 .p
.description
= NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."),
390 .p
.priv_class
= &hqdn3d_class
,
391 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
| AVFILTER_FLAG_SLICE_THREADS
,
392 .priv_size
= sizeof(HQDN3DContext
),
395 FILTER_INPUTS(avfilter_vf_hqdn3d_inputs
),
396 FILTER_OUTPUTS(ff_video_default_filterpad
),
397 FILTER_PIXFMTS_ARRAY(pix_fmts
),
398 .process_command
= process_command
,