ffmpeg / libavfilter / vf_hqdn3d.c @ a4dc7aa5
History | View | Annotate | Download (11.5 KB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2003 Daniel Moreno <comac AT comac DOT darktech DOT org>
|
3 |
* Copyright (c) 2010 Baptiste Coudurier
|
4 |
*
|
5 |
* This file is part of FFmpeg, ported from MPlayer.
|
6 |
*
|
7 |
* FFmpeg is free software; you can redistribute it and/or modify
|
8 |
* it under the terms of the GNU General Public License as published by
|
9 |
* the Free Software Foundation; either version 2 of the License, or
|
10 |
* (at your option) any later version.
|
11 |
*
|
12 |
* FFmpeg is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
* GNU General Public License for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU General Public License along
|
18 |
* with FFmpeg; if not, write to the Free Software Foundation, Inc.,
|
19 |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
20 |
*/
|
21 |
|
22 |
/**
|
23 |
* @file
|
24 |
* high quality 3d video denoiser, ported from MPlayer
|
25 |
* libmpcodecs/vf_hqdn3d.c.
|
26 |
*/
|
27 |
|
28 |
#include "libavutil/pixdesc.h" |
29 |
#include "avfilter.h" |
30 |
|
31 |
typedef struct { |
32 |
int Coefs[4][512*16]; |
33 |
unsigned int *Line; |
34 |
unsigned short *Frame[3]; |
35 |
int hsub, vsub;
|
36 |
} HQDN3DContext; |
37 |
|
38 |
static inline unsigned int LowPassMul(unsigned int PrevMul, unsigned int CurrMul, int *Coef) |
39 |
{ |
40 |
// int dMul= (PrevMul&0xFFFFFF)-(CurrMul&0xFFFFFF);
|
41 |
int dMul= PrevMul-CurrMul;
|
42 |
unsigned int d=((dMul+0x10007FF)>>12); |
43 |
return CurrMul + Coef[d];
|
44 |
} |
45 |
|
46 |
static void deNoiseTemporal(unsigned char *FrameSrc, |
47 |
unsigned char *FrameDest, |
48 |
unsigned short *FrameAnt, |
49 |
int W, int H, int sStride, int dStride, |
50 |
int *Temporal)
|
51 |
{ |
52 |
long X, Y;
|
53 |
unsigned int PixelDst; |
54 |
|
55 |
for (Y = 0; Y < H; Y++) { |
56 |
for (X = 0; X < W; X++) { |
57 |
PixelDst = LowPassMul(FrameAnt[X]<<8, FrameSrc[X]<<16, Temporal); |
58 |
FrameAnt[X] = ((PixelDst+0x1000007F)>>8); |
59 |
FrameDest[X]= ((PixelDst+0x10007FFF)>>16); |
60 |
} |
61 |
FrameSrc += sStride; |
62 |
FrameDest += dStride; |
63 |
FrameAnt += W; |
64 |
} |
65 |
} |
66 |
|
67 |
static void deNoiseSpacial(unsigned char *Frame, |
68 |
unsigned char *FrameDest, |
69 |
unsigned int *LineAnt, |
70 |
int W, int H, int sStride, int dStride, |
71 |
int *Horizontal, int *Vertical) |
72 |
{ |
73 |
long X, Y;
|
74 |
long sLineOffs = 0, dLineOffs = 0; |
75 |
unsigned int PixelAnt; |
76 |
unsigned int PixelDst; |
77 |
|
78 |
/* First pixel has no left nor top neighbor. */
|
79 |
PixelDst = LineAnt[0] = PixelAnt = Frame[0]<<16; |
80 |
FrameDest[0]= ((PixelDst+0x10007FFF)>>16); |
81 |
|
82 |
/* First line has no top neighbor, only left. */
|
83 |
for (X = 1; X < W; X++) { |
84 |
PixelDst = LineAnt[X] = LowPassMul(PixelAnt, Frame[X]<<16, Horizontal);
|
85 |
FrameDest[X]= ((PixelDst+0x10007FFF)>>16); |
86 |
} |
87 |
|
88 |
for (Y = 1; Y < H; Y++) { |
89 |
unsigned int PixelAnt; |
90 |
sLineOffs += sStride, dLineOffs += dStride; |
91 |
/* First pixel on each line doesn't have previous pixel */
|
92 |
PixelAnt = Frame[sLineOffs]<<16;
|
93 |
PixelDst = LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical); |
94 |
FrameDest[dLineOffs]= ((PixelDst+0x10007FFF)>>16); |
95 |
|
96 |
for (X = 1; X < W; X++) { |
97 |
unsigned int PixelDst; |
98 |
/* The rest are normal */
|
99 |
PixelAnt = LowPassMul(PixelAnt, Frame[sLineOffs+X]<<16, Horizontal);
|
100 |
PixelDst = LineAnt[X] = LowPassMul(LineAnt[X], PixelAnt, Vertical); |
101 |
FrameDest[dLineOffs+X]= ((PixelDst+0x10007FFF)>>16); |
102 |
} |
103 |
} |
104 |
} |
105 |
|
106 |
static void deNoise(unsigned char *Frame, |
107 |
unsigned char *FrameDest, |
108 |
unsigned int *LineAnt, |
109 |
unsigned short **FrameAntPtr, |
110 |
int W, int H, int sStride, int dStride, |
111 |
int *Horizontal, int *Vertical, int *Temporal) |
112 |
{ |
113 |
long X, Y;
|
114 |
long sLineOffs = 0, dLineOffs = 0; |
115 |
unsigned int PixelAnt; |
116 |
unsigned int PixelDst; |
117 |
unsigned short* FrameAnt=(*FrameAntPtr); |
118 |
|
119 |
if (!FrameAnt) {
|
120 |
(*FrameAntPtr) = FrameAnt = av_malloc(W*H*sizeof(unsigned short)); |
121 |
for (Y = 0; Y < H; Y++) { |
122 |
unsigned short* dst=&FrameAnt[Y*W]; |
123 |
unsigned char* src=Frame+Y*sStride; |
124 |
for (X = 0; X < W; X++) dst[X]=src[X]<<8; |
125 |
} |
126 |
} |
127 |
|
128 |
if (!Horizontal[0] && !Vertical[0]) { |
129 |
deNoiseTemporal(Frame, FrameDest, FrameAnt, |
130 |
W, H, sStride, dStride, Temporal); |
131 |
return;
|
132 |
} |
133 |
if (!Temporal[0]) { |
134 |
deNoiseSpacial(Frame, FrameDest, LineAnt, |
135 |
W, H, sStride, dStride, Horizontal, Vertical); |
136 |
return;
|
137 |
} |
138 |
|
139 |
/* First pixel has no left nor top neighbor. Only previous frame */
|
140 |
LineAnt[0] = PixelAnt = Frame[0]<<16; |
141 |
PixelDst = LowPassMul(FrameAnt[0]<<8, PixelAnt, Temporal); |
142 |
FrameAnt[0] = ((PixelDst+0x1000007F)>>8); |
143 |
FrameDest[0]= ((PixelDst+0x10007FFF)>>16); |
144 |
|
145 |
/* First line has no top neighbor. Only left one for each pixel and
|
146 |
* last frame */
|
147 |
for (X = 1; X < W; X++) { |
148 |
LineAnt[X] = PixelAnt = LowPassMul(PixelAnt, Frame[X]<<16, Horizontal);
|
149 |
PixelDst = LowPassMul(FrameAnt[X]<<8, PixelAnt, Temporal);
|
150 |
FrameAnt[X] = ((PixelDst+0x1000007F)>>8); |
151 |
FrameDest[X]= ((PixelDst+0x10007FFF)>>16); |
152 |
} |
153 |
|
154 |
for (Y = 1; Y < H; Y++) { |
155 |
unsigned int PixelAnt; |
156 |
unsigned short* LinePrev=&FrameAnt[Y*W]; |
157 |
sLineOffs += sStride, dLineOffs += dStride; |
158 |
/* First pixel on each line doesn't have previous pixel */
|
159 |
PixelAnt = Frame[sLineOffs]<<16;
|
160 |
LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical); |
161 |
PixelDst = LowPassMul(LinePrev[0]<<8, LineAnt[0], Temporal); |
162 |
LinePrev[0] = ((PixelDst+0x1000007F)>>8); |
163 |
FrameDest[dLineOffs]= ((PixelDst+0x10007FFF)>>16); |
164 |
|
165 |
for (X = 1; X < W; X++) { |
166 |
unsigned int PixelDst; |
167 |
/* The rest are normal */
|
168 |
PixelAnt = LowPassMul(PixelAnt, Frame[sLineOffs+X]<<16, Horizontal);
|
169 |
LineAnt[X] = LowPassMul(LineAnt[X], PixelAnt, Vertical); |
170 |
PixelDst = LowPassMul(LinePrev[X]<<8, LineAnt[X], Temporal);
|
171 |
LinePrev[X] = ((PixelDst+0x1000007F)>>8); |
172 |
FrameDest[dLineOffs+X]= ((PixelDst+0x10007FFF)>>16); |
173 |
} |
174 |
} |
175 |
} |
176 |
|
177 |
static void PrecalcCoefs(int *Ct, double Dist25) |
178 |
{ |
179 |
int i;
|
180 |
double Gamma, Simil, C;
|
181 |
|
182 |
Gamma = log(0.25) / log(1.0 - Dist25/255.0 - 0.00001); |
183 |
|
184 |
for (i = -255*16; i <= 255*16; i++) { |
185 |
Simil = 1.0 - FFABS(i) / (16*255.0); |
186 |
C = pow(Simil, Gamma) * 65536.0 * i / 16.0; |
187 |
Ct[16*256+i] = lrint(C); |
188 |
} |
189 |
|
190 |
Ct[0] = !!Dist25;
|
191 |
} |
192 |
|
193 |
#define PARAM1_DEFAULT 4.0 |
194 |
#define PARAM2_DEFAULT 3.0 |
195 |
#define PARAM3_DEFAULT 6.0 |
196 |
|
197 |
static int init(AVFilterContext *ctx, const char *args, void *opaque) |
198 |
{ |
199 |
HQDN3DContext *hqdn3d = ctx->priv; |
200 |
double LumSpac, LumTmp, ChromSpac, ChromTmp;
|
201 |
double Param1, Param2, Param3, Param4;
|
202 |
|
203 |
LumSpac = PARAM1_DEFAULT; |
204 |
ChromSpac = PARAM2_DEFAULT; |
205 |
LumTmp = PARAM3_DEFAULT; |
206 |
ChromTmp = LumTmp * ChromSpac / LumSpac; |
207 |
|
208 |
if (args) {
|
209 |
switch (sscanf(args, "%lf:%lf:%lf:%lf", |
210 |
&Param1, &Param2, &Param3, &Param4)) { |
211 |
case 1: |
212 |
LumSpac = Param1; |
213 |
ChromSpac = PARAM2_DEFAULT * Param1 / PARAM1_DEFAULT; |
214 |
LumTmp = PARAM3_DEFAULT * Param1 / PARAM1_DEFAULT; |
215 |
ChromTmp = LumTmp * ChromSpac / LumSpac; |
216 |
break;
|
217 |
case 2: |
218 |
LumSpac = Param1; |
219 |
ChromSpac = Param2; |
220 |
LumTmp = PARAM3_DEFAULT * Param1 / PARAM1_DEFAULT; |
221 |
ChromTmp = LumTmp * ChromSpac / LumSpac; |
222 |
break;
|
223 |
case 3: |
224 |
LumSpac = Param1; |
225 |
ChromSpac = Param2; |
226 |
LumTmp = Param3; |
227 |
ChromTmp = LumTmp * ChromSpac / LumSpac; |
228 |
break;
|
229 |
case 4: |
230 |
LumSpac = Param1; |
231 |
ChromSpac = Param2; |
232 |
LumTmp = Param3; |
233 |
ChromTmp = Param4; |
234 |
break;
|
235 |
} |
236 |
} |
237 |
|
238 |
av_log(ctx, AV_LOG_INFO, "ls:%lf cs:%lf lt:%lf ct:%lf\n",
|
239 |
LumSpac, ChromSpac, LumTmp, ChromTmp); |
240 |
if (LumSpac < 0 || ChromSpac < 0 || isnan(ChromTmp)) { |
241 |
av_log(ctx, AV_LOG_ERROR, |
242 |
"Invalid negative value for luma or chroma spatial strength, "
|
243 |
"or resulting value for chroma temporal strength is nan.\n");
|
244 |
return AVERROR(EINVAL);
|
245 |
} |
246 |
|
247 |
PrecalcCoefs(hqdn3d->Coefs[0], LumSpac);
|
248 |
PrecalcCoefs(hqdn3d->Coefs[1], LumTmp);
|
249 |
PrecalcCoefs(hqdn3d->Coefs[2], ChromSpac);
|
250 |
PrecalcCoefs(hqdn3d->Coefs[3], ChromTmp);
|
251 |
|
252 |
return 0; |
253 |
} |
254 |
|
255 |
static void uninit(AVFilterContext *ctx) |
256 |
{ |
257 |
HQDN3DContext *hqdn3d = ctx->priv; |
258 |
|
259 |
av_freep(&hqdn3d->Line); |
260 |
av_freep(&hqdn3d->Frame[0]);
|
261 |
av_freep(&hqdn3d->Frame[1]);
|
262 |
av_freep(&hqdn3d->Frame[2]);
|
263 |
} |
264 |
|
265 |
static int query_formats(AVFilterContext *ctx) |
266 |
{ |
267 |
static const enum PixelFormat pix_fmts[] = { |
268 |
PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV411P, PIX_FMT_NONE |
269 |
}; |
270 |
|
271 |
avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); |
272 |
|
273 |
return 0; |
274 |
} |
275 |
|
276 |
static int config_input(AVFilterLink *inlink) |
277 |
{ |
278 |
HQDN3DContext *hqdn3d = inlink->dst->priv; |
279 |
|
280 |
hqdn3d->hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w; |
281 |
hqdn3d->vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h; |
282 |
|
283 |
hqdn3d->Line = av_malloc(inlink->w * sizeof(*hqdn3d->Line));
|
284 |
if (!hqdn3d->Line)
|
285 |
return AVERROR(ENOMEM);
|
286 |
|
287 |
return 0; |
288 |
} |
289 |
|
290 |
static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } |
291 |
|
292 |
static void end_frame(AVFilterLink *inlink) |
293 |
{ |
294 |
HQDN3DContext *hqdn3d = inlink->dst->priv; |
295 |
AVFilterLink *outlink = inlink->dst->outputs[0];
|
296 |
AVFilterBufferRef *inpic = inlink ->cur_buf; |
297 |
AVFilterBufferRef *outpic = outlink->out_buf; |
298 |
int cw = inpic->video->w >> hqdn3d->hsub;
|
299 |
int ch = inpic->video->h >> hqdn3d->vsub;
|
300 |
|
301 |
deNoise(inpic->data[0], outpic->data[0], |
302 |
hqdn3d->Line, &hqdn3d->Frame[0], inpic->video->w, inpic->video->h,
|
303 |
inpic->linesize[0], outpic->linesize[0], |
304 |
hqdn3d->Coefs[0],
|
305 |
hqdn3d->Coefs[0],
|
306 |
hqdn3d->Coefs[1]);
|
307 |
deNoise(inpic->data[1], outpic->data[1], |
308 |
hqdn3d->Line, &hqdn3d->Frame[1], cw, ch,
|
309 |
inpic->linesize[1], outpic->linesize[1], |
310 |
hqdn3d->Coefs[2],
|
311 |
hqdn3d->Coefs[2],
|
312 |
hqdn3d->Coefs[3]);
|
313 |
deNoise(inpic->data[2], outpic->data[2], |
314 |
hqdn3d->Line, &hqdn3d->Frame[2], cw, ch,
|
315 |
inpic->linesize[2], outpic->linesize[2], |
316 |
hqdn3d->Coefs[2],
|
317 |
hqdn3d->Coefs[2],
|
318 |
hqdn3d->Coefs[3]);
|
319 |
|
320 |
avfilter_draw_slice(outlink, 0, inpic->video->h, 1); |
321 |
avfilter_end_frame(outlink); |
322 |
avfilter_unref_buffer(inpic); |
323 |
avfilter_unref_buffer(outpic); |
324 |
} |
325 |
|
326 |
AVFilter avfilter_vf_hqdn3d = { |
327 |
.name = "hqdn3d",
|
328 |
.description = NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."),
|
329 |
|
330 |
.priv_size = sizeof(HQDN3DContext),
|
331 |
.init = init, |
332 |
.uninit = uninit, |
333 |
.query_formats = query_formats, |
334 |
|
335 |
.inputs = (AVFilterPad[]) {{ .name = "default",
|
336 |
.type = AVMEDIA_TYPE_VIDEO, |
337 |
.draw_slice = null_draw_slice, |
338 |
.config_props = config_input, |
339 |
.end_frame = end_frame }, |
340 |
{ .name = NULL}},
|
341 |
|
342 |
.outputs = (AVFilterPad[]) {{ .name = "default",
|
343 |
.type = AVMEDIA_TYPE_VIDEO }, |
344 |
{ .name = NULL}},
|
345 |
}; |