This is a port of the MPlayer smartblur filter (libmpcodecs/vf_smartblur.c)
by Michael Niedermayer.
Signed-off-by: Stefano Sabatini <stefasab@gmail.com>
... | ... |
@@ -1867,6 +1867,7 @@ ocv_filter_deps="libopencv" |
1867 | 1867 |
pan_filter_deps="swresample" |
1868 | 1868 |
removelogo_filter_deps="avcodec avformat swscale" |
1869 | 1869 |
scale_filter_deps="swscale" |
1870 |
+smartblur_filter_deps="gpl swscale" |
|
1870 | 1871 |
select_filter_deps="avcodec" |
1871 | 1872 |
showspectrum_filter_deps="avcodec" |
1872 | 1873 |
super2xsai_filter_deps="gpl" |
... | ... |
@@ -3253,6 +3253,35 @@ not specified it will use the default value of 16. |
3253 | 3253 |
Adding this in the beginning of filter chains should make filtering |
3254 | 3254 |
faster due to better use of the memory cache. |
3255 | 3255 |
|
3256 |
+@section smartblur |
|
3257 |
+ |
|
3258 |
+Blur the input video without impacting the outlines. |
|
3259 |
+ |
|
3260 |
+The filter accepts the following parameters: |
|
3261 |
+@var{luma_radius}:@var{luma_strength}:@var{luma_threshold}[:@var{chroma_radius}:@var{chroma_strength}:@var{chroma_threshold}] |
|
3262 |
+ |
|
3263 |
+Parameters prefixed by @var{luma} indicate that they work on the |
|
3264 |
+luminance of the pixels whereas parameters prefixed by @var{chroma} |
|
3265 |
+refer to the chrominance of the pixels. |
|
3266 |
+ |
|
3267 |
+If the chroma parameters are not set, the luma parameters are used for |
|
3268 |
+either the luminance and the chrominance of the pixels. |
|
3269 |
+ |
|
3270 |
+@var{luma_radius} or @var{chroma_radius} must be a float number in the |
|
3271 |
+range [0.1,5.0] that specifies the variance of the gaussian filter |
|
3272 |
+used to blur the image (slower if larger). |
|
3273 |
+ |
|
3274 |
+@var{luma_strength} or @var{chroma_strength} must be a float number in |
|
3275 |
+the range [-1.0,1.0] that configures the blurring. A value included in |
|
3276 |
+[0.0,1.0] will blur the image whereas a value included in [-1.0,0.0] |
|
3277 |
+will sharpen the image. |
|
3278 |
+ |
|
3279 |
+@var{luma_threshold} or @var{chroma_threshold} must be an integer in |
|
3280 |
+the range [-30,30] that is used as a coefficient to determine whether |
|
3281 |
+a pixel should be blurred or not. A value of 0 will filter all the |
|
3282 |
+image, a value included in [0,30] will filter flat areas and a value |
|
3283 |
+included in [-30,0] will filter edges. |
|
3284 |
+ |
|
3256 | 3285 |
@section split |
3257 | 3286 |
|
3258 | 3287 |
Split input video into several identical outputs. |
... | ... |
@@ -14,6 +14,7 @@ FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec |
14 | 14 |
FFLIBS-$(CONFIG_PAN_FILTER) += swresample |
15 | 15 |
FFLIBS-$(CONFIG_REMOVELOGO_FILTER) += avformat avcodec |
16 | 16 |
FFLIBS-$(CONFIG_MP_FILTER) += avcodec postproc |
17 |
+FFLIBS-$(CONFIG_SMARTBLUR_FILTER) += swscale |
|
17 | 18 |
|
18 | 19 |
HEADERS = asrc_abuffer.h \ |
19 | 20 |
avcodec.h \ |
... | ... |
@@ -125,6 +126,7 @@ OBJS-$(CONFIG_SETSAR_FILTER) += vf_aspect.o |
125 | 125 |
OBJS-$(CONFIG_SETTB_FILTER) += f_settb.o |
126 | 126 |
OBJS-$(CONFIG_SHOWINFO_FILTER) += vf_showinfo.o |
127 | 127 |
OBJS-$(CONFIG_SLICIFY_FILTER) += vf_slicify.o |
128 |
+OBJS-$(CONFIG_SMARTBLUR_FILTER) += vf_smartblur.o |
|
128 | 129 |
OBJS-$(CONFIG_SPLIT_FILTER) += split.o |
129 | 130 |
OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o |
130 | 131 |
OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o |
... | ... |
@@ -116,6 +116,7 @@ void avfilter_register_all(void) |
116 | 116 |
REGISTER_FILTER (SETTB, settb, vf); |
117 | 117 |
REGISTER_FILTER (SHOWINFO, showinfo, vf); |
118 | 118 |
REGISTER_FILTER (SLICIFY, slicify, vf); |
119 |
+ REGISTER_FILTER (SMARTBLUR, smartblur, vf); |
|
119 | 120 |
REGISTER_FILTER (SPLIT, split, vf); |
120 | 121 |
REGISTER_FILTER (SUPER2XSAI, super2xsai, vf); |
121 | 122 |
REGISTER_FILTER (SWAPUV, swapuv, vf); |
... | ... |
@@ -29,8 +29,8 @@ |
29 | 29 |
#include "libavutil/avutil.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVFILTER_VERSION_MAJOR 3 |
32 |
-#define LIBAVFILTER_VERSION_MINOR 13 |
|
33 |
-#define LIBAVFILTER_VERSION_MICRO 101 |
|
32 |
+#define LIBAVFILTER_VERSION_MINOR 14 |
|
33 |
+#define LIBAVFILTER_VERSION_MICRO 100 |
|
34 | 34 |
|
35 | 35 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
36 | 36 |
LIBAVFILTER_VERSION_MINOR, \ |
37 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,303 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at> |
|
2 |
+ * Copyright (c) 2012 Jeremy Tran |
|
3 |
+ * |
|
4 |
+ * This file is part of FFmpeg. |
|
5 |
+ * |
|
6 |
+ * FFmpeg is free software; you can redistribute it and/or modify |
|
7 |
+ * it under the terms of the GNU General Public License as published by |
|
8 |
+ * the Free Software Foundation; either version 2 of the License, or |
|
9 |
+ * (at your option) any later version. |
|
10 |
+ * |
|
11 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License along |
|
17 |
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc., |
|
18 |
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
19 |
+ */ |
|
20 |
+ |
|
21 |
+/** |
|
22 |
+ * @file |
|
23 |
+ * Apply a smartblur filter to the input video |
|
24 |
+ * Ported from MPlayer libmpcodecs/vf_smartblur.c by Michael Niedermayer. |
|
25 |
+ */ |
|
26 |
+ |
|
27 |
+#include "libavutil/pixdesc.h" |
|
28 |
+#include "libswscale/swscale.h" |
|
29 |
+ |
|
30 |
+#include "avfilter.h" |
|
31 |
+#include "formats.h" |
|
32 |
+#include "internal.h" |
|
33 |
+ |
|
34 |
+#define RADIUS_MIN 0.1 |
|
35 |
+#define RADIUS_MAX 5.0 |
|
36 |
+ |
|
37 |
+#define STRENGTH_MIN -1.0 |
|
38 |
+#define STRENGTH_MAX 1.0 |
|
39 |
+ |
|
40 |
+#define THRESHOLD_MIN -30 |
|
41 |
+#define THRESHOLD_MAX 30 |
|
42 |
+ |
|
43 |
+typedef struct { |
|
44 |
+ float radius; |
|
45 |
+ float strength; |
|
46 |
+ int threshold; |
|
47 |
+ float quality; |
|
48 |
+ struct SwsContext *filter_context; |
|
49 |
+} FilterParam; |
|
50 |
+ |
|
51 |
+typedef struct { |
|
52 |
+ FilterParam luma; |
|
53 |
+ FilterParam chroma; |
|
54 |
+ int hsub; |
|
55 |
+ int vsub; |
|
56 |
+ unsigned int sws_flags; |
|
57 |
+} SmartblurContext; |
|
58 |
+ |
|
59 |
+#define CHECK_PARAM(param, name, min, max, format, ret) \ |
|
60 |
+ if (param < min || param > max) { \ |
|
61 |
+ av_log(ctx, AV_LOG_ERROR, \ |
|
62 |
+ "Invalid " #name " value " #format ": " \ |
|
63 |
+ "must be included between range " #format " and " #format "\n",\ |
|
64 |
+ param, min, max); \ |
|
65 |
+ ret = AVERROR(EINVAL); \ |
|
66 |
+ } |
|
67 |
+ |
|
68 |
+static av_cold int init(AVFilterContext *ctx, const char *args) |
|
69 |
+{ |
|
70 |
+ SmartblurContext *sblur = ctx->priv; |
|
71 |
+ int n = 0, ret = 0; |
|
72 |
+ float lradius, lstrength, cradius, cstrength; |
|
73 |
+ int lthreshold, cthreshold; |
|
74 |
+ |
|
75 |
+ if (args) |
|
76 |
+ n = sscanf(args, "%f:%f:%d:%f:%f:%d", |
|
77 |
+ &lradius, &lstrength, <hreshold, |
|
78 |
+ &cradius, &cstrength, &cthreshold); |
|
79 |
+ |
|
80 |
+ if (n != 3 && n != 6) { |
|
81 |
+ av_log(ctx, AV_LOG_ERROR, |
|
82 |
+ "Incorrect number of parameters or invalid syntax: " |
|
83 |
+ "must be luma_radius:luma_strength:luma_threshold" |
|
84 |
+ "[:chroma_radius:chroma_strength:chroma_threshold]\n"); |
|
85 |
+ return AVERROR(EINVAL); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ sblur->luma.radius = lradius; |
|
89 |
+ sblur->luma.strength = lstrength; |
|
90 |
+ sblur->luma.threshold = lthreshold; |
|
91 |
+ |
|
92 |
+ if (n == 3) { |
|
93 |
+ sblur->chroma.radius = sblur->luma.radius; |
|
94 |
+ sblur->chroma.strength = sblur->luma.strength; |
|
95 |
+ sblur->chroma.threshold = sblur->luma.threshold; |
|
96 |
+ } else { |
|
97 |
+ sblur->chroma.radius = cradius; |
|
98 |
+ sblur->chroma.strength = cstrength; |
|
99 |
+ sblur->chroma.threshold = cthreshold; |
|
100 |
+ } |
|
101 |
+ |
|
102 |
+ sblur->luma.quality = sblur->chroma.quality = 3.0; |
|
103 |
+ sblur->sws_flags = SWS_BICUBIC; |
|
104 |
+ |
|
105 |
+ CHECK_PARAM(lradius, luma radius, RADIUS_MIN, RADIUS_MAX, %0.1f, ret) |
|
106 |
+ CHECK_PARAM(lstrength, luma strength, STRENGTH_MIN, STRENGTH_MAX, %0.1f, ret) |
|
107 |
+ CHECK_PARAM(lthreshold, luma threshold, THRESHOLD_MIN, THRESHOLD_MAX, %d, ret) |
|
108 |
+ |
|
109 |
+ if (n != 3) { |
|
110 |
+ CHECK_PARAM(sblur->chroma.radius, chroma radius, RADIUS_MIN, RADIUS_MAX, %0.1f, ret) |
|
111 |
+ CHECK_PARAM(sblur->chroma.strength, chroma strength, STRENGTH_MIN, STRENGTH_MAX, %0.1f, ret) |
|
112 |
+ CHECK_PARAM(sblur->chroma.threshold, chroma threshold, THRESHOLD_MIN,THRESHOLD_MAX, %d, ret) |
|
113 |
+ } |
|
114 |
+ |
|
115 |
+ return ret; |
|
116 |
+} |
|
117 |
+ |
|
118 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
119 |
+{ |
|
120 |
+ SmartblurContext *sblur = ctx->priv; |
|
121 |
+ |
|
122 |
+ sws_freeContext(sblur->luma.filter_context); |
|
123 |
+ sws_freeContext(sblur->chroma.filter_context); |
|
124 |
+} |
|
125 |
+ |
|
126 |
+static int query_formats(AVFilterContext *ctx) |
|
127 |
+{ |
|
128 |
+ static const enum PixelFormat pix_fmts[] = { |
|
129 |
+ PIX_FMT_YUV444P, PIX_FMT_YUV422P, |
|
130 |
+ PIX_FMT_YUV420P, PIX_FMT_YUV411P, |
|
131 |
+ PIX_FMT_YUV410P, PIX_FMT_YUV440P, |
|
132 |
+ PIX_FMT_GRAY8, |
|
133 |
+ PIX_FMT_NONE |
|
134 |
+ }; |
|
135 |
+ |
|
136 |
+ ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
|
137 |
+ |
|
138 |
+ return 0; |
|
139 |
+} |
|
140 |
+ |
|
141 |
+static int alloc_sws_context(FilterParam *f, int width, int height, unsigned int flags) |
|
142 |
+{ |
|
143 |
+ SwsVector *vec; |
|
144 |
+ SwsFilter sws_filter; |
|
145 |
+ |
|
146 |
+ vec = sws_getGaussianVec(f->radius, f->quality); |
|
147 |
+ |
|
148 |
+ if (!vec) |
|
149 |
+ return AVERROR(EINVAL); |
|
150 |
+ |
|
151 |
+ sws_scaleVec(vec, f->strength); |
|
152 |
+ vec->coeff[vec->length / 2] += 1.0 - f->strength; |
|
153 |
+ sws_filter.lumH = sws_filter.lumV = vec; |
|
154 |
+ sws_filter.chrH = sws_filter.chrV = NULL; |
|
155 |
+ f->filter_context = sws_getCachedContext(NULL, |
|
156 |
+ width, height, PIX_FMT_GRAY8, |
|
157 |
+ width, height, PIX_FMT_GRAY8, |
|
158 |
+ flags, &sws_filter, NULL, NULL); |
|
159 |
+ |
|
160 |
+ sws_freeVec(vec); |
|
161 |
+ |
|
162 |
+ if (!f->filter_context) |
|
163 |
+ return AVERROR(EINVAL); |
|
164 |
+ |
|
165 |
+ return 0; |
|
166 |
+} |
|
167 |
+ |
|
168 |
+static int config_props(AVFilterLink *inlink) |
|
169 |
+{ |
|
170 |
+ SmartblurContext *sblur = inlink->dst->priv; |
|
171 |
+ const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format]; |
|
172 |
+ |
|
173 |
+ sblur->hsub = desc->log2_chroma_w; |
|
174 |
+ sblur->vsub = desc->log2_chroma_h; |
|
175 |
+ |
|
176 |
+ alloc_sws_context(&sblur->luma, inlink->w, inlink->h, sblur->sws_flags); |
|
177 |
+ alloc_sws_context(&sblur->chroma, |
|
178 |
+ inlink->w >> sblur->hsub, inlink->h >> sblur->vsub, |
|
179 |
+ sblur->sws_flags); |
|
180 |
+ |
|
181 |
+ return 0; |
|
182 |
+} |
|
183 |
+ |
|
184 |
+static void blur(uint8_t *dst, const int dst_linesize, |
|
185 |
+ const uint8_t *src, const int src_linesize, |
|
186 |
+ const int w, const int h, const int threshold, |
|
187 |
+ struct SwsContext *filter_context) |
|
188 |
+{ |
|
189 |
+ int x, y; |
|
190 |
+ int orig, filtered; |
|
191 |
+ int diff; |
|
192 |
+ /* Declare arrays of 4 to get aligned data */ |
|
193 |
+ const uint8_t* const src_array[4] = {src}; |
|
194 |
+ uint8_t *dst_array[4] = {dst}; |
|
195 |
+ int src_linesize_array[4] = {src_linesize}; |
|
196 |
+ int dst_linesize_array[4] = {dst_linesize}; |
|
197 |
+ |
|
198 |
+ sws_scale(filter_context, src_array, src_linesize_array, |
|
199 |
+ 0, h, dst_array, dst_linesize_array); |
|
200 |
+ |
|
201 |
+ if (threshold > 0) { |
|
202 |
+ for (y = 0; y < h; ++y) { |
|
203 |
+ for (x = 0; x < w; ++x) { |
|
204 |
+ orig = src[x + y * src_linesize]; |
|
205 |
+ filtered = dst[x + y * dst_linesize]; |
|
206 |
+ diff = orig - filtered; |
|
207 |
+ |
|
208 |
+ if (diff > 0) { |
|
209 |
+ if (diff > 2 * threshold) |
|
210 |
+ dst[x + y * dst_linesize] = orig; |
|
211 |
+ else if (diff > threshold) |
|
212 |
+ /* add 'diff' and substract 'threshold' from 'filtered' */ |
|
213 |
+ dst[x + y * dst_linesize] = orig - threshold; |
|
214 |
+ } else { |
|
215 |
+ if (-diff > 2 * threshold) |
|
216 |
+ dst[x + y * dst_linesize] = orig; |
|
217 |
+ else if (-diff > threshold) |
|
218 |
+ /* add 'diff' and 'threshold' to 'filtered' */ |
|
219 |
+ dst[x + y * dst_linesize] = orig + threshold; |
|
220 |
+ } |
|
221 |
+ } |
|
222 |
+ } |
|
223 |
+ } else if (threshold < 0) { |
|
224 |
+ for (y = 0; y < h; ++y) { |
|
225 |
+ for (x = 0; x < w; ++x) { |
|
226 |
+ orig = src[x + y * src_linesize]; |
|
227 |
+ filtered = dst[x + y * dst_linesize]; |
|
228 |
+ diff = orig - filtered; |
|
229 |
+ |
|
230 |
+ if (diff > 0) { |
|
231 |
+ if (diff <= -threshold) |
|
232 |
+ dst[x + y * dst_linesize] = orig; |
|
233 |
+ else if (diff <= -2 * threshold) |
|
234 |
+ /* substract 'diff' and 'threshold' from 'orig' */ |
|
235 |
+ dst[x + y * dst_linesize] = filtered - threshold; |
|
236 |
+ } else { |
|
237 |
+ if (diff >= threshold) |
|
238 |
+ dst[x + y * dst_linesize] = orig; |
|
239 |
+ else if (diff >= 2 * threshold) |
|
240 |
+ /* add 'threshold' and substract 'diff' from 'orig' */ |
|
241 |
+ dst[x + y * dst_linesize] = filtered + threshold; |
|
242 |
+ } |
|
243 |
+ } |
|
244 |
+ } |
|
245 |
+ } |
|
246 |
+} |
|
247 |
+ |
|
248 |
+static int end_frame(AVFilterLink *inlink) |
|
249 |
+{ |
|
250 |
+ SmartblurContext *sblur = inlink->dst->priv; |
|
251 |
+ AVFilterBufferRef *inpic = inlink->cur_buf; |
|
252 |
+ AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf; |
|
253 |
+ int cw = inlink->w >> sblur->hsub; |
|
254 |
+ int ch = inlink->h >> sblur->vsub; |
|
255 |
+ |
|
256 |
+ blur(outpic->data[0], outpic->linesize[0], |
|
257 |
+ inpic->data[0], inpic->linesize[0], |
|
258 |
+ inlink->w, inlink->h, sblur->luma.threshold, |
|
259 |
+ sblur->luma.filter_context); |
|
260 |
+ |
|
261 |
+ if (inpic->data[2]) { |
|
262 |
+ blur(outpic->data[1], outpic->linesize[1], |
|
263 |
+ inpic->data[1], inpic->linesize[1], |
|
264 |
+ cw, ch, sblur->chroma.threshold, |
|
265 |
+ sblur->chroma.filter_context); |
|
266 |
+ blur(outpic->data[2], outpic->linesize[2], |
|
267 |
+ inpic->data[2], inpic->linesize[2], |
|
268 |
+ cw, ch, sblur->chroma.threshold, |
|
269 |
+ sblur->chroma.filter_context); |
|
270 |
+ } |
|
271 |
+ |
|
272 |
+ return ff_end_frame(inlink->dst->outputs[0]); |
|
273 |
+} |
|
274 |
+ |
|
275 |
+AVFilter avfilter_vf_smartblur = { |
|
276 |
+ .name = "smartblur", |
|
277 |
+ .description = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."), |
|
278 |
+ |
|
279 |
+ .priv_size = sizeof(SmartblurContext), |
|
280 |
+ |
|
281 |
+ .init = init, |
|
282 |
+ .uninit = uninit, |
|
283 |
+ .query_formats = query_formats, |
|
284 |
+ |
|
285 |
+ .inputs = (const AVFilterPad[]) { |
|
286 |
+ { |
|
287 |
+ .name = "default", |
|
288 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
289 |
+ .end_frame = end_frame, |
|
290 |
+ .config_props = config_props, |
|
291 |
+ .min_perms = AV_PERM_READ, |
|
292 |
+ }, |
|
293 |
+ { .name = NULL } |
|
294 |
+ }, |
|
295 |
+ .outputs = (const AVFilterPad[]) { |
|
296 |
+ { |
|
297 |
+ .name = "default", |
|
298 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
299 |
+ }, |
|
300 |
+ { .name = NULL } |
|
301 |
+ } |
|
302 |
+}; |