... | ... |
@@ -3075,6 +3075,7 @@ tinterlace_filter_deps="gpl" |
3075 | 3075 |
tinterlace_merge_test_deps="tinterlace_filter" |
3076 | 3076 |
tinterlace_pad_test_deps="tinterlace_filter" |
3077 | 3077 |
uspp_filter_deps="gpl avcodec" |
3078 |
+vaguedenoiser_filter_deps="gpl" |
|
3078 | 3079 |
vidstabdetect_filter_deps="libvidstab" |
3079 | 3080 |
vidstabtransform_filter_deps="libvidstab" |
3080 | 3081 |
zmq_filter_deps="libzmq" |
... | ... |
@@ -13379,6 +13379,53 @@ Force a constant quantization parameter. If not set, the filter will use the QP |
13379 | 13379 |
from the video stream (if available). |
13380 | 13380 |
@end table |
13381 | 13381 |
|
13382 |
+@section vaguedenoiser |
|
13383 |
+ |
|
13384 |
+Apply a wavelet based denoiser. |
|
13385 |
+ |
|
13386 |
+It transforms each frame from the video input into the wavelet domain, |
|
13387 |
+using Cohen-Daubechies-Feauveau 9/7. Then it applies some filtering to |
|
13388 |
+the obtained coefficients. It does an inverse wavelet transform after. |
|
13389 |
+Due to wavelet properties, it should give a nice smoothed result, and |
|
13390 |
+reduced noise, without blurring picture features. |
|
13391 |
+ |
|
13392 |
+This filter accepts the following options: |
|
13393 |
+ |
|
13394 |
+@table @option |
|
13395 |
+@item threshold |
|
13396 |
+The filtering strength. The higher, the more filtered the video will be. |
|
13397 |
+Hard thresholding can use a higher threshold than soft thresholding |
|
13398 |
+before the video looks overfiltered. |
|
13399 |
+ |
|
13400 |
+@item method |
|
13401 |
+The filtering method the filter will use. |
|
13402 |
+ |
|
13403 |
+It accepts the following values: |
|
13404 |
+@table @samp |
|
13405 |
+@item hard |
|
13406 |
+All values under the threshold will be zeroed. |
|
13407 |
+ |
|
13408 |
+@item soft |
|
13409 |
+All values under the threshold will be zeroed. All values above will be |
|
13410 |
+reduced by the threshold. |
|
13411 |
+ |
|
13412 |
+@item garrote |
|
13413 |
+Scales or nullifies coefficients - intermediary between (more) soft and |
|
13414 |
+(less) hard thresholding. |
|
13415 |
+@end table |
|
13416 |
+ |
|
13417 |
+@item nsteps |
|
13418 |
+Number of times, the wavelet will decompose the picture. Picture can't |
|
13419 |
+be decomposed beyond a particular point (typically, 8 for a 640x480 |
|
13420 |
+frame - as 2^9 = 512 > 480) |
|
13421 |
+ |
|
13422 |
+@item percent |
|
13423 |
+Partial of full denoising (limited coefficients shrinking), from 0 to 100. |
|
13424 |
+ |
|
13425 |
+@item planes |
|
13426 |
+A list of the planes to process. By default all planes are processed. |
|
13427 |
+@end table |
|
13428 |
+ |
|
13382 | 13429 |
@section vectorscope |
13383 | 13430 |
|
13384 | 13431 |
Display 2 color component values in the two dimensional graph (which is called |
... | ... |
@@ -286,6 +286,7 @@ OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o |
286 | 286 |
OBJS-$(CONFIG_TRIM_FILTER) += trim.o |
287 | 287 |
OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o |
288 | 288 |
OBJS-$(CONFIG_USPP_FILTER) += vf_uspp.o |
289 |
+OBJS-$(CONFIG_VAGUEDENOISER_FILTER) += vf_vaguedenoiser.o |
|
289 | 290 |
OBJS-$(CONFIG_VECTORSCOPE_FILTER) += vf_vectorscope.o |
290 | 291 |
OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o |
291 | 292 |
OBJS-$(CONFIG_VIDSTABDETECT_FILTER) += vidstabutils.o vf_vidstabdetect.o |
... | ... |
@@ -302,6 +302,7 @@ void avfilter_register_all(void) |
302 | 302 |
REGISTER_FILTER(TRIM, trim, vf); |
303 | 303 |
REGISTER_FILTER(UNSHARP, unsharp, vf); |
304 | 304 |
REGISTER_FILTER(USPP, uspp, vf); |
305 |
+ REGISTER_FILTER(VAGUEDENOISER, vaguedenoiser, vf); |
|
305 | 306 |
REGISTER_FILTER(VECTORSCOPE, vectorscope, vf); |
306 | 307 |
REGISTER_FILTER(VFLIP, vflip, vf); |
307 | 308 |
REGISTER_FILTER(VIDSTABDETECT, vidstabdetect, vf); |
... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/version.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 6 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 56 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 57 |
|
34 | 34 |
#define LIBAVFILTER_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
37 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,562 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2003 LeFunGus, lefungus@altern.org |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or modify |
|
6 |
+ * it under the terms of the GNU General Public License as published by |
|
7 |
+ * the Free Software Foundation; either version 2 of the License, or |
|
8 |
+ * (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 |
+ * GNU General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU General Public License along |
|
16 |
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc., |
|
17 |
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+#include <float.h> |
|
21 |
+ |
|
22 |
+#include "libavutil/imgutils.h" |
|
23 |
+#include "libavutil/attributes.h" |
|
24 |
+#include "libavutil/common.h" |
|
25 |
+#include "libavutil/pixdesc.h" |
|
26 |
+#include "libavutil/intreadwrite.h" |
|
27 |
+#include "libavutil/opt.h" |
|
28 |
+ |
|
29 |
+#include "avfilter.h" |
|
30 |
+#include "formats.h" |
|
31 |
+#include "internal.h" |
|
32 |
+#include "video.h" |
|
33 |
+ |
|
34 |
+typedef struct VagueDenoiserContext { |
|
35 |
+ const AVClass *class; |
|
36 |
+ |
|
37 |
+ float threshold; |
|
38 |
+ float percent; |
|
39 |
+ int method; |
|
40 |
+ int nsteps; |
|
41 |
+ int planes; |
|
42 |
+ |
|
43 |
+ int depth; |
|
44 |
+ int peak; |
|
45 |
+ int nb_planes; |
|
46 |
+ int planeheight[4]; |
|
47 |
+ int planewidth[4]; |
|
48 |
+ |
|
49 |
+ float *block; |
|
50 |
+ float *in; |
|
51 |
+ float *out; |
|
52 |
+ float *tmp; |
|
53 |
+ |
|
54 |
+ int hlowsize[32]; |
|
55 |
+ int hhighsize[32]; |
|
56 |
+ int vlowsize[32]; |
|
57 |
+ int vhighsize[32]; |
|
58 |
+} VagueDenoiserContext; |
|
59 |
+ |
|
60 |
+#define OFFSET(x) offsetof(VagueDenoiserContext, x) |
|
61 |
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
|
62 |
+static const AVOption vaguedenoiser_options[] = { |
|
63 |
+ { "threshold", "set filtering strength", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=2.}, 0,DBL_MAX, FLAGS }, |
|
64 |
+ { "method", "set filtering method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=2 }, 0, 2, FLAGS, "method" }, |
|
65 |
+ { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "method" }, |
|
66 |
+ { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "method" }, |
|
67 |
+ { "garrote", "garotte thresholding", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "method" }, |
|
68 |
+ { "nsteps", "set number of steps", OFFSET(nsteps), AV_OPT_TYPE_INT, {.i64=6 }, 1, 32, FLAGS }, |
|
69 |
+ { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_FLOAT, {.dbl=85}, 0,100, FLAGS }, |
|
70 |
+ { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15 }, 0, 15, FLAGS }, |
|
71 |
+ { NULL } |
|
72 |
+}; |
|
73 |
+ |
|
74 |
+AVFILTER_DEFINE_CLASS(vaguedenoiser); |
|
75 |
+ |
|
76 |
+#define NPAD 10 |
|
77 |
+ |
|
78 |
+static const float analysis_low[9] = { |
|
79 |
+ 0.037828455506995f, -0.023849465019380f, -0.110624404418423f, 0.377402855612654f, |
|
80 |
+ 0.852698679009403f, 0.377402855612654f, -0.110624404418423f, -0.023849465019380f, 0.037828455506995f |
|
81 |
+}; |
|
82 |
+ |
|
83 |
+static const float analysis_high[7] = { |
|
84 |
+ -0.064538882628938f, 0.040689417609558f, 0.418092273222212f, -0.788485616405664f, |
|
85 |
+ 0.418092273222212f, 0.040689417609558f, -0.064538882628938f |
|
86 |
+}; |
|
87 |
+ |
|
88 |
+static const float synthesis_low[7] = { |
|
89 |
+ -0.064538882628938f, -0.040689417609558f, 0.418092273222212f, 0.788485616405664f, |
|
90 |
+ 0.418092273222212f, -0.040689417609558f, -0.064538882628938f |
|
91 |
+}; |
|
92 |
+ |
|
93 |
+static const float synthesis_high[9] = { |
|
94 |
+ -0.037828455506995f, -0.023849465019380f, 0.110624404418423f, 0.377402855612654f, |
|
95 |
+ -0.852698679009403f, 0.377402855612654f, 0.110624404418423f, -0.023849465019380f, -0.037828455506995f |
|
96 |
+}; |
|
97 |
+ |
|
98 |
+static int query_formats(AVFilterContext *ctx) |
|
99 |
+{ |
|
100 |
+ static const enum AVPixelFormat pix_fmts[] = { |
|
101 |
+ AV_PIX_FMT_GRAY8, |
|
102 |
+ AV_PIX_FMT_GRAY16, |
|
103 |
+ AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, |
|
104 |
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, |
|
105 |
+ AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, |
|
106 |
+ AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, |
|
107 |
+ AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P, |
|
108 |
+ AV_PIX_FMT_YUVJ411P, |
|
109 |
+ AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, |
|
110 |
+ AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, |
|
111 |
+ AV_PIX_FMT_YUV440P10, |
|
112 |
+ AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, |
|
113 |
+ AV_PIX_FMT_YUV440P12, |
|
114 |
+ AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14, |
|
115 |
+ AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, |
|
116 |
+ AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, |
|
117 |
+ AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, |
|
118 |
+ AV_PIX_FMT_NONE |
|
119 |
+ }; |
|
120 |
+ AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); |
|
121 |
+ if (!fmts_list) |
|
122 |
+ return AVERROR(ENOMEM); |
|
123 |
+ return ff_set_common_formats(ctx, fmts_list); |
|
124 |
+} |
|
125 |
+ |
|
126 |
+static int config_input(AVFilterLink *inlink) |
|
127 |
+{ |
|
128 |
+ VagueDenoiserContext *s = inlink->dst->priv; |
|
129 |
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
|
130 |
+ int nsteps_width, nsteps_height, nsteps_max; |
|
131 |
+ |
|
132 |
+ s->depth = desc->comp[0].depth; |
|
133 |
+ s->nb_planes = desc->nb_components; |
|
134 |
+ |
|
135 |
+ s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); |
|
136 |
+ s->planeheight[0] = s->planeheight[3] = inlink->h; |
|
137 |
+ s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); |
|
138 |
+ s->planewidth[0] = s->planewidth[3] = inlink->w; |
|
139 |
+ |
|
140 |
+ s->block = av_malloc_array(inlink->w * inlink->h, sizeof(*s->block)); |
|
141 |
+ s->in = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->in)); |
|
142 |
+ s->out = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->out)); |
|
143 |
+ s->tmp = av_malloc_array(32 + FFMAX(inlink->w, inlink->h), sizeof(*s->tmp)); |
|
144 |
+ |
|
145 |
+ if (!s->block || !s->in || !s->out || !s->tmp) |
|
146 |
+ return AVERROR(ENOMEM); |
|
147 |
+ |
|
148 |
+ s->threshold *= 1 << (s->depth - 8); |
|
149 |
+ s->peak = (1 << s->depth) - 1; |
|
150 |
+ |
|
151 |
+ nsteps_width = ((s->planes & 2 || s->planes & 4) && s->nb_planes > 1) ? s->planewidth[1] : s->planewidth[0]; |
|
152 |
+ nsteps_height = ((s->planes & 2 || s->planes & 4) && s->nb_planes > 1) ? s->planeheight[1] : s->planeheight[0]; |
|
153 |
+ |
|
154 |
+ for (nsteps_max = 1; nsteps_max < 15; nsteps_max++) { |
|
155 |
+ if (pow(2, nsteps_max) >= nsteps_width || pow(2, nsteps_max) >= nsteps_height) |
|
156 |
+ break; |
|
157 |
+ } |
|
158 |
+ |
|
159 |
+ s->nsteps = FFMIN(s->nsteps, nsteps_max - 2); |
|
160 |
+ |
|
161 |
+ return 0; |
|
162 |
+} |
|
163 |
+ |
|
164 |
+static inline void copy(const float *p1, float *p2, const int length) |
|
165 |
+{ |
|
166 |
+ memcpy(p2, p1, length * sizeof(float)); |
|
167 |
+} |
|
168 |
+ |
|
169 |
+static inline void copyv(const float *p1, const int stride1, float *p2, const int length) |
|
170 |
+{ |
|
171 |
+ int i; |
|
172 |
+ |
|
173 |
+ for (i = 0; i < length; i++) { |
|
174 |
+ p2[i] = *p1; |
|
175 |
+ p1 += stride1; |
|
176 |
+ } |
|
177 |
+} |
|
178 |
+ |
|
179 |
+static inline void copyh(const float *p1, float *p2, const int stride2, const int length) |
|
180 |
+{ |
|
181 |
+ int i; |
|
182 |
+ |
|
183 |
+ for (i = 0; i < length; i++) { |
|
184 |
+ *p2 = p1[i]; |
|
185 |
+ p2 += stride2; |
|
186 |
+ } |
|
187 |
+} |
|
188 |
+ |
|
189 |
+// Do symmetric extension of data using prescribed symmetries |
|
190 |
+// Original values are in output[npad] through output[npad+size-1] |
|
191 |
+// New values will be placed in output[0] through output[npad] and in output[npad+size] through output[2*npad+size-1] (note: end values may not be filled in) |
|
192 |
+// extension at left bdry is ... 3 2 1 0 | 0 1 2 3 ... |
|
193 |
+// same for right boundary |
|
194 |
+// if right_ext=1 then ... 3 2 1 0 | 1 2 3 |
|
195 |
+static void symmetric_extension(float *output, const int size, const int left_ext, const int right_ext) |
|
196 |
+{ |
|
197 |
+ int first = NPAD; |
|
198 |
+ int last = NPAD - 1 + size; |
|
199 |
+ const int originalLast = last; |
|
200 |
+ int i, nextend, idx; |
|
201 |
+ |
|
202 |
+ if (left_ext == 2) |
|
203 |
+ output[--first] = output[NPAD]; |
|
204 |
+ if (right_ext == 2) |
|
205 |
+ output[++last] = output[originalLast]; |
|
206 |
+ |
|
207 |
+ // extend left end |
|
208 |
+ nextend = first; |
|
209 |
+ for (i = 0; i < nextend; i++) |
|
210 |
+ output[--first] = output[NPAD + 1 + i]; |
|
211 |
+ |
|
212 |
+ idx = NPAD + NPAD - 1 + size; |
|
213 |
+ |
|
214 |
+ // extend right end |
|
215 |
+ nextend = idx - last; |
|
216 |
+ for (i = 0; i < nextend; i++) |
|
217 |
+ output[++last] = output[originalLast - 1 - i]; |
|
218 |
+} |
|
219 |
+ |
|
220 |
+static void transform_step(float *input, float *output, const int size, const int low_size, VagueDenoiserContext *s) |
|
221 |
+{ |
|
222 |
+ int i; |
|
223 |
+ |
|
224 |
+ symmetric_extension(input, size, 1, 1); |
|
225 |
+ |
|
226 |
+ for (i = NPAD; i < NPAD + low_size; i++) { |
|
227 |
+ const float a = input[2 * i - 14] * analysis_low[0]; |
|
228 |
+ const float b = input[2 * i - 13] * analysis_low[1]; |
|
229 |
+ const float c = input[2 * i - 12] * analysis_low[2]; |
|
230 |
+ const float d = input[2 * i - 11] * analysis_low[3]; |
|
231 |
+ const float e = input[2 * i - 10] * analysis_low[4]; |
|
232 |
+ const float f = input[2 * i - 9] * analysis_low[3]; |
|
233 |
+ const float g = input[2 * i - 8] * analysis_low[2]; |
|
234 |
+ const float h = input[2 * i - 7] * analysis_low[1]; |
|
235 |
+ const float k = input[2 * i - 6] * analysis_low[0]; |
|
236 |
+ |
|
237 |
+ output[i] = a + b + c + d + e + f + g + h + k; |
|
238 |
+ } |
|
239 |
+ |
|
240 |
+ for (i = NPAD; i < NPAD + low_size; i++) { |
|
241 |
+ const float a = input[2 * i - 12] * analysis_high[0]; |
|
242 |
+ const float b = input[2 * i - 11] * analysis_high[1]; |
|
243 |
+ const float c = input[2 * i - 10] * analysis_high[2]; |
|
244 |
+ const float d = input[2 * i - 9] * analysis_high[3]; |
|
245 |
+ const float e = input[2 * i - 8] * analysis_high[2]; |
|
246 |
+ const float f = input[2 * i - 7] * analysis_high[1]; |
|
247 |
+ const float g = input[2 * i - 6] * analysis_high[0]; |
|
248 |
+ |
|
249 |
+ output[i + low_size] = a + b + c + d + e + f + g; |
|
250 |
+ } |
|
251 |
+} |
|
252 |
+ |
|
253 |
+static void invert_step(const float *input, float *output, float *temp, const int size, VagueDenoiserContext *s) |
|
254 |
+{ |
|
255 |
+ const int low_size = (size + 1) >> 1; |
|
256 |
+ const int high_size = size >> 1; |
|
257 |
+ int left_ext = 1, right_ext, i; |
|
258 |
+ int findex; |
|
259 |
+ |
|
260 |
+ memcpy(temp + NPAD, input + NPAD, low_size * sizeof(float)); |
|
261 |
+ |
|
262 |
+ right_ext = (size % 2 == 0) ? 2 : 1; |
|
263 |
+ symmetric_extension(temp, low_size, left_ext, right_ext); |
|
264 |
+ |
|
265 |
+ memset(output, 0, (NPAD + NPAD + size) * sizeof(float)); |
|
266 |
+ findex = (size + 2) >> 1; |
|
267 |
+ |
|
268 |
+ for (i = 9; i < findex + 11; i++) { |
|
269 |
+ const float a = temp[i] * synthesis_low[0]; |
|
270 |
+ const float b = temp[i] * synthesis_low[1]; |
|
271 |
+ const float c = temp[i] * synthesis_low[2]; |
|
272 |
+ const float d = temp[i] * synthesis_low[3]; |
|
273 |
+ |
|
274 |
+ output[2 * i - 13] += a; |
|
275 |
+ output[2 * i - 12] += b; |
|
276 |
+ output[2 * i - 11] += c; |
|
277 |
+ output[2 * i - 10] += d; |
|
278 |
+ output[2 * i - 9] += c; |
|
279 |
+ output[2 * i - 8] += b; |
|
280 |
+ output[2 * i - 7] += a; |
|
281 |
+ } |
|
282 |
+ |
|
283 |
+ memcpy(temp + NPAD, input + NPAD + low_size, high_size * sizeof(float)); |
|
284 |
+ |
|
285 |
+ left_ext = 2; |
|
286 |
+ right_ext = (size % 2 == 0) ? 1 : 2; |
|
287 |
+ symmetric_extension(temp, high_size, left_ext, right_ext); |
|
288 |
+ |
|
289 |
+ for (i = 8; i < findex + 11; i++) { |
|
290 |
+ const float a = temp[i] * synthesis_high[0]; |
|
291 |
+ const float b = temp[i] * synthesis_high[1]; |
|
292 |
+ const float c = temp[i] * synthesis_high[2]; |
|
293 |
+ const float d = temp[i] * synthesis_high[3]; |
|
294 |
+ const float e = temp[i] * synthesis_high[4]; |
|
295 |
+ |
|
296 |
+ output[2 * i - 13] += a; |
|
297 |
+ output[2 * i - 12] += b; |
|
298 |
+ output[2 * i - 11] += c; |
|
299 |
+ output[2 * i - 10] += d; |
|
300 |
+ output[2 * i - 9] += e; |
|
301 |
+ output[2 * i - 8] += d; |
|
302 |
+ output[2 * i - 7] += c; |
|
303 |
+ output[2 * i - 6] += b; |
|
304 |
+ output[2 * i - 5] += a; |
|
305 |
+ } |
|
306 |
+} |
|
307 |
+ |
|
308 |
+static void hard_thresholding(float *block, const int width, const int height, |
|
309 |
+ const int stride, const float threshold, |
|
310 |
+ const float percent) |
|
311 |
+{ |
|
312 |
+ const float frac = 1.f - percent * 0.01f; |
|
313 |
+ int y, x; |
|
314 |
+ |
|
315 |
+ for (y = 0; y < height; y++) { |
|
316 |
+ for (x = 0; x < width; x++) { |
|
317 |
+ if (FFABS(block[x]) <= threshold) |
|
318 |
+ block[x] *= frac; |
|
319 |
+ } |
|
320 |
+ block += stride; |
|
321 |
+ } |
|
322 |
+} |
|
323 |
+ |
|
324 |
+static void soft_thresholding(float *block, const int width, const int height, const int stride, |
|
325 |
+ const float threshold, const float percent, const int nsteps) |
|
326 |
+{ |
|
327 |
+ const float frac = 1.f - percent * 0.01f; |
|
328 |
+ const float shift = threshold * 0.01f * percent; |
|
329 |
+ int w = width; |
|
330 |
+ int h = height; |
|
331 |
+ int y, x, l; |
|
332 |
+ |
|
333 |
+ for (l = 0; l < nsteps; l++) { |
|
334 |
+ w = (w + 1) >> 1; |
|
335 |
+ h = (h + 1) >> 1; |
|
336 |
+ } |
|
337 |
+ |
|
338 |
+ for (y = 0; y < height; y++) { |
|
339 |
+ const int x0 = (y < h) ? w : 0; |
|
340 |
+ for (x = x0; x < width; x++) { |
|
341 |
+ const float temp = FFABS(block[x]); |
|
342 |
+ if (temp <= threshold) |
|
343 |
+ block[x] *= frac; |
|
344 |
+ else |
|
345 |
+ block[x] = (block[x] < 0.f ? -1.f : (block[x] > 0.f ? 1.f : 0.f)) * (temp - shift); |
|
346 |
+ } |
|
347 |
+ block += stride; |
|
348 |
+ } |
|
349 |
+} |
|
350 |
+ |
|
351 |
+static void qian_thresholding(float *block, const int width, const int height, |
|
352 |
+ const int stride, const float threshold, |
|
353 |
+ const float percent) |
|
354 |
+{ |
|
355 |
+ const float percent01 = percent * 0.01f; |
|
356 |
+ const float tr2 = threshold * threshold * percent01; |
|
357 |
+ const float frac = 1.f - percent01; |
|
358 |
+ int y, x; |
|
359 |
+ |
|
360 |
+ for (y = 0; y < height; y++) { |
|
361 |
+ for (x = 0; x < width; x++) { |
|
362 |
+ const float temp = FFABS(block[x]); |
|
363 |
+ if (temp <= threshold) { |
|
364 |
+ block[x] *= frac; |
|
365 |
+ } else { |
|
366 |
+ const float tp2 = temp * temp; |
|
367 |
+ block[x] *= (tp2 - tr2) / tp2; |
|
368 |
+ } |
|
369 |
+ } |
|
370 |
+ block += stride; |
|
371 |
+ } |
|
372 |
+} |
|
373 |
+ |
|
374 |
+static void filter(VagueDenoiserContext *s, AVFrame *in, AVFrame *out) |
|
375 |
+{ |
|
376 |
+ int p, y, x, i, j; |
|
377 |
+ |
|
378 |
+ for (p = 0; p < s->nb_planes; p++) { |
|
379 |
+ const int height = s->planeheight[p]; |
|
380 |
+ const int width = s->planewidth[p]; |
|
381 |
+ const uint8_t *srcp8 = in->data[p]; |
|
382 |
+ const uint16_t *srcp16 = (const uint16_t *)in->data[p]; |
|
383 |
+ uint8_t *dstp8 = out->data[p]; |
|
384 |
+ uint16_t *dstp16 = (uint16_t *)out->data[p]; |
|
385 |
+ float *output = s->block; |
|
386 |
+ int h_low_size0 = width; |
|
387 |
+ int v_low_size0 = height; |
|
388 |
+ int nsteps_transform = s->nsteps; |
|
389 |
+ int nsteps_invert = s->nsteps; |
|
390 |
+ const float *input = s->block; |
|
391 |
+ |
|
392 |
+ if (!((1 << p) & s->planes)) { |
|
393 |
+ av_image_copy_plane(out->data[p], out->linesize[p], in->data[p], in->linesize[p], |
|
394 |
+ s->planewidth[p], s->planeheight[p]); |
|
395 |
+ continue; |
|
396 |
+ } |
|
397 |
+ |
|
398 |
+ if (s->depth <= 8) { |
|
399 |
+ for (y = 0; y < height; y++) { |
|
400 |
+ for (x = 0; x < width; x++) |
|
401 |
+ output[x] = srcp8[x]; |
|
402 |
+ srcp8 += in->linesize[p]; |
|
403 |
+ output += width; |
|
404 |
+ } |
|
405 |
+ } else { |
|
406 |
+ for (y = 0; y < height; y++) { |
|
407 |
+ for (x = 0; x < width; x++) |
|
408 |
+ output[x] = srcp16[x]; |
|
409 |
+ srcp16 += in->linesize[p] / 2; |
|
410 |
+ output += width; |
|
411 |
+ } |
|
412 |
+ } |
|
413 |
+ |
|
414 |
+ while (nsteps_transform--) { |
|
415 |
+ int low_size = (h_low_size0 + 1) >> 1; |
|
416 |
+ float *input = s->block; |
|
417 |
+ for (j = 0; j < v_low_size0; j++) { |
|
418 |
+ copy(input, s->in + NPAD, h_low_size0); |
|
419 |
+ transform_step(s->in, s->out, h_low_size0, low_size, s); |
|
420 |
+ copy(s->out + NPAD, input, h_low_size0); |
|
421 |
+ input += width; |
|
422 |
+ } |
|
423 |
+ |
|
424 |
+ low_size = (v_low_size0 + 1) >> 1; |
|
425 |
+ input = s->block; |
|
426 |
+ for (j = 0; j < h_low_size0; j++) { |
|
427 |
+ copyv(input, width, s->in + NPAD, v_low_size0); |
|
428 |
+ transform_step(s->in, s->out, v_low_size0, low_size, s); |
|
429 |
+ copyh(s->out + NPAD, input, width, v_low_size0); |
|
430 |
+ input++; |
|
431 |
+ } |
|
432 |
+ |
|
433 |
+ h_low_size0 = (h_low_size0 + 1) >> 1; |
|
434 |
+ v_low_size0 = (v_low_size0 + 1) >> 1; |
|
435 |
+ } |
|
436 |
+ |
|
437 |
+ if (s->method == 0) |
|
438 |
+ hard_thresholding(s->block, width, height, width, s->threshold, s->percent); |
|
439 |
+ else if (s->method == 1) |
|
440 |
+ soft_thresholding(s->block, width, height, width, s->threshold, s->percent, s->nsteps); |
|
441 |
+ else |
|
442 |
+ qian_thresholding(s->block, width, height, width, s->threshold, s->percent); |
|
443 |
+ |
|
444 |
+ s->hlowsize[0] = (width + 1) >> 1; |
|
445 |
+ s->hhighsize[0] = width >> 1; |
|
446 |
+ s->vlowsize[0] = (height + 1) >> 1; |
|
447 |
+ s->vhighsize[0] = height >> 1; |
|
448 |
+ |
|
449 |
+ for (i = 1; i < s->nsteps; i++) { |
|
450 |
+ s->hlowsize[i] = (s->hlowsize[i - 1] + 1) >> 1; |
|
451 |
+ s->hhighsize[i] = s->hlowsize[i - 1] >> 1; |
|
452 |
+ s->vlowsize[i] = (s->vlowsize[i - 1] + 1) >> 1; |
|
453 |
+ s->vhighsize[i] = s->vlowsize[i - 1] >> 1; |
|
454 |
+ } |
|
455 |
+ |
|
456 |
+ while (nsteps_invert--) { |
|
457 |
+ const int idx = s->vlowsize[nsteps_invert] + s->vhighsize[nsteps_invert]; |
|
458 |
+ const int idx2 = s->hlowsize[nsteps_invert] + s->hhighsize[nsteps_invert]; |
|
459 |
+ float * idx3 = s->block; |
|
460 |
+ for (i = 0; i < idx2; i++) { |
|
461 |
+ copyv(idx3, width, s->in + NPAD, idx); |
|
462 |
+ invert_step(s->in, s->out, s->tmp, idx, s); |
|
463 |
+ copyh(s->out + NPAD, idx3, width, idx); |
|
464 |
+ idx3++; |
|
465 |
+ } |
|
466 |
+ |
|
467 |
+ idx3 = s->block; |
|
468 |
+ for (i = 0; i < idx; i++) { |
|
469 |
+ copy(idx3, s->in + NPAD, idx2); |
|
470 |
+ invert_step(s->in, s->out, s->tmp, idx2, s); |
|
471 |
+ copy(s->out + NPAD, idx3, idx2); |
|
472 |
+ idx3 += width; |
|
473 |
+ } |
|
474 |
+ } |
|
475 |
+ |
|
476 |
+ if (s->depth <= 8) { |
|
477 |
+ for (y = 0; y < height; y++) { |
|
478 |
+ for (x = 0; x < width; x++) |
|
479 |
+ dstp8[x] = av_clip_uint8(input[x] + 0.5f); |
|
480 |
+ input += width; |
|
481 |
+ dstp8 += out->linesize[p]; |
|
482 |
+ } |
|
483 |
+ } else { |
|
484 |
+ for (y = 0; y < height; y++) { |
|
485 |
+ for (x = 0; x < width; x++) |
|
486 |
+ dstp16[x] = av_clip(input[x] + 0.5f, 0, s->peak); |
|
487 |
+ input += width; |
|
488 |
+ dstp16 += out->linesize[p] / 2; |
|
489 |
+ } |
|
490 |
+ } |
|
491 |
+ } |
|
492 |
+} |
|
493 |
+ |
|
494 |
+static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
495 |
+{ |
|
496 |
+ AVFilterContext *ctx = inlink->dst; |
|
497 |
+ VagueDenoiserContext *s = ctx->priv; |
|
498 |
+ AVFilterLink *outlink = ctx->outputs[0]; |
|
499 |
+ AVFrame *out; |
|
500 |
+ int direct = av_frame_is_writable(in); |
|
501 |
+ |
|
502 |
+ if (direct) { |
|
503 |
+ out = in; |
|
504 |
+ } else { |
|
505 |
+ out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
|
506 |
+ if (!out) { |
|
507 |
+ av_frame_free(&in); |
|
508 |
+ return AVERROR(ENOMEM); |
|
509 |
+ } |
|
510 |
+ |
|
511 |
+ av_frame_copy_props(out, in); |
|
512 |
+ } |
|
513 |
+ |
|
514 |
+ filter(s, in, out); |
|
515 |
+ |
|
516 |
+ if (!direct) |
|
517 |
+ av_frame_free(&in); |
|
518 |
+ |
|
519 |
+ return ff_filter_frame(outlink, out); |
|
520 |
+} |
|
521 |
+ |
|
522 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
523 |
+{ |
|
524 |
+ VagueDenoiserContext *s = ctx->priv; |
|
525 |
+ |
|
526 |
+ av_freep(&s->block); |
|
527 |
+ av_freep(&s->in); |
|
528 |
+ av_freep(&s->out); |
|
529 |
+ av_freep(&s->tmp); |
|
530 |
+} |
|
531 |
+ |
|
532 |
+static const AVFilterPad vaguedenoiser_inputs[] = { |
|
533 |
+ { |
|
534 |
+ .name = "default", |
|
535 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
536 |
+ .config_props = config_input, |
|
537 |
+ .filter_frame = filter_frame, |
|
538 |
+ }, |
|
539 |
+ { NULL } |
|
540 |
+}; |
|
541 |
+ |
|
542 |
+ |
|
543 |
+static const AVFilterPad vaguedenoiser_outputs[] = { |
|
544 |
+ { |
|
545 |
+ .name = "default", |
|
546 |
+ .type = AVMEDIA_TYPE_VIDEO |
|
547 |
+ }, |
|
548 |
+ { NULL } |
|
549 |
+}; |
|
550 |
+ |
|
551 |
+AVFilter ff_vf_vaguedenoiser = { |
|
552 |
+ .name = "vaguedenoiser", |
|
553 |
+ .description = NULL_IF_CONFIG_SMALL("Apply a Wavelet based Denoiser."), |
|
554 |
+ .priv_size = sizeof(VagueDenoiserContext), |
|
555 |
+ .priv_class = &vaguedenoiser_class, |
|
556 |
+ .uninit = uninit, |
|
557 |
+ .query_formats = query_formats, |
|
558 |
+ .inputs = vaguedenoiser_inputs, |
|
559 |
+ .outputs = vaguedenoiser_outputs, |
|
560 |
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
|
561 |
+}; |