Browse code

lavfi: add smartblur filter

This is a port of the MPlayer smartblur filter (libmpcodecs/vf_smartblur.c)
by Michael Niedermayer.

Signed-off-by: Stefano Sabatini <stefasab@gmail.com>

Jérémy Tran authored on 2012/08/29 01:41:06
Showing 7 changed files
... ...
@@ -54,6 +54,7 @@ version next:
54 54
 - Ut Video encoder
55 55
 - Matroska demuxer now identifies SRT subtitles as AV_CODEC_ID_SUBRIP
56 56
   instead of AV_CODEC_ID_TEXT
57
+- smartblur filter ported from MPlayer
57 58
 
58 59
 
59 60
 version 0.11:
... ...
@@ -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, &lthreshold,
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
+};