Browse code

lavfi: port libmpcodecs delogo filter

The ported filter supports named option parsing and more YUV formats.

Stefano Sabatini authored on 2011/08/11 01:58:49
Showing 6 changed files
... ...
@@ -1504,6 +1504,7 @@ udp_protocol_deps="network"
1504 1504
 blackframe_filter_deps="gpl"
1505 1505
 boxblur_filter_deps="gpl"
1506 1506
 cropdetect_filter_deps="gpl"
1507
+delogo_filter_deps="gpl"
1507 1508
 drawtext_filter_deps="libfreetype"
1508 1509
 frei0r_filter_deps="frei0r dlopen strtok_r"
1509 1510
 frei0r_src_filter_deps="frei0r dlopen strtok_r"
... ...
@@ -426,6 +426,58 @@ indicates never reset and return the largest area encountered during
426 426
 playback.
427 427
 @end table
428 428
 
429
+@section delogo
430
+
431
+Suppress a TV station logo by a simple interpolation of the surrounding
432
+pixels. Just set a rectangle covering the logo and watch it disappear
433
+(and sometimes something even uglier appear - your mileage may vary).
434
+
435
+The filter accepts parameters as a string of the form
436
+"@var{x}:@var{y}:@var{w}:@var{h}:@var{band}", or as a list of
437
+@var{key}=@var{value} pairs, separated by ":".
438
+
439
+The description of the accepted parameters follows.
440
+
441
+@table @option
442
+
443
+@item x, y
444
+Specify the top left corner coordinates of the logo. They must be
445
+specified.
446
+
447
+@item w, h
448
+Specify the width and height of the logo to clear. They must be
449
+specified.
450
+
451
+@item band, t
452
+Specify the thickness of the fuzzy edge of the rectangle (added to
453
+@var{w} and @var{h}). The default value is 4.
454
+
455
+@item show
456
+When set to 1, a green rectangle is drawn on the screen to simplify
457
+finding the right @var{x}, @var{y}, @var{w}, @var{h} parameters, and
458
+@var{band} is set to 4. The default value is 0.
459
+
460
+@end table
461
+
462
+Some examples follow.
463
+
464
+@itemize
465
+
466
+@item
467
+Set a rectangle covering the area with top left corner coordinates 0,0
468
+and size 100x77, setting a band of size 10:
469
+@example
470
+delogo=0:0:100:77:10
471
+@end example
472
+
473
+@item
474
+As the previous example, but use named options:
475
+@example
476
+delogo=x=0:y=0:w=100:h=77:band=10
477
+@end example
478
+
479
+@end itemize
480
+
429 481
 @section drawbox
430 482
 
431 483
 Draw a colored box on the input image.
... ...
@@ -31,6 +31,7 @@ OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o
31 31
 OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
32 32
 OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
33 33
 OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
34
+OBJS-$(CONFIG_DELOGO_FILTER)                 += vf_delogo.o
34 35
 OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
35 36
 OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
36 37
 OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
... ...
@@ -47,6 +47,7 @@ void avfilter_register_all(void)
47 47
     REGISTER_FILTER (COPY,        copy,        vf);
48 48
     REGISTER_FILTER (CROP,        crop,        vf);
49 49
     REGISTER_FILTER (CROPDETECT,  cropdetect,  vf);
50
+    REGISTER_FILTER (DELOGO,      delogo,      vf);
50 51
     REGISTER_FILTER (DRAWBOX,     drawbox,     vf);
51 52
     REGISTER_FILTER (DRAWTEXT,    drawtext,    vf);
52 53
     REGISTER_FILTER (FADE,        fade,        vf);
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/rational.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  2
32
-#define LIBAVFILTER_VERSION_MINOR 30
32
+#define LIBAVFILTER_VERSION_MINOR 31
33 33
 #define LIBAVFILTER_VERSION_MICRO  0
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
new file mode 100644
... ...
@@ -0,0 +1,288 @@
0
+/*
1
+ * Copyright (c) 2002 Jindrich Makovicka <makovick@gmail.com>
2
+ * Copyright (c) 2011 Stefano Sabatini
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
+ * A very simple tv station logo remover
24
+ * Ported from MPlayer libmpcodecs/vf_delogo.c.
25
+ */
26
+
27
+#include "libavutil/imgutils.h"
28
+#include "libavutil/opt.h"
29
+#include "libavutil/pixdesc.h"
30
+#include "avfilter.h"
31
+
32
+/**
33
+ * Apply a simple delogo algorithm to the image in dst and put the
34
+ * result in src.
35
+ *
36
+ * The algorithm is only applied to the region specified by the logo
37
+ * parameters.
38
+ *
39
+ * @param w      width of the input image
40
+ * @param h      height of the input image
41
+ * @param logo_x x coordinate of the top left corner of the logo region
42
+ * @param logo_y y coordinate of the top left corner of the logo region
43
+ * @param logo_w width of the logo
44
+ * @param logo_h height of the logo
45
+ * @param band   the size of the band around the processed area
46
+ * @param show   show a rectangle around the processed area, useful for
47
+ *               parameters tweaking
48
+ * @param direct if non-zero perform in-place processing
49
+ */
50
+static void apply_delogo(uint8_t *dst, int dst_linesize,
51
+                         uint8_t *src, int src_linesize,
52
+                         int w, int h,
53
+                         int logo_x, int logo_y, int logo_w, int logo_h,
54
+                         int band, int show, int direct)
55
+{
56
+    int x, y;
57
+    int interp, dist;
58
+    uint8_t *xdst, *xsrc;
59
+
60
+    uint8_t *topleft, *botleft, *topright;
61
+    int xclipl, xclipr, yclipt, yclipb;
62
+    int logo_x1, logo_x2, logo_y1, logo_y2;
63
+
64
+    xclipl = FFMAX(-logo_x, 0);
65
+    xclipr = FFMAX(logo_x+logo_w-w, 0);
66
+    yclipt = FFMAX(-logo_y, 0);
67
+    yclipb = FFMAX(logo_y+logo_h-h, 0);
68
+
69
+    logo_x1 = logo_x + xclipl;
70
+    logo_x2 = logo_x + logo_w - xclipr;
71
+    logo_y1 = logo_y + yclipt;
72
+    logo_y2 = logo_y + logo_h - yclipb;
73
+
74
+    topleft  = src+logo_y1     * src_linesize+logo_x1;
75
+    topright = src+logo_y1     * src_linesize+logo_x2-1;
76
+    botleft  = src+(logo_y2-1) * src_linesize+logo_x1;
77
+
78
+    dst += (logo_y1+1)*dst_linesize;
79
+    src += (logo_y1+1)*src_linesize;
80
+
81
+    if (!direct)
82
+        av_image_copy_plane(dst, dst_linesize, src, src_linesize, w, h);
83
+
84
+    for (y = logo_y1+1; y < logo_y2-1; y++) {
85
+        for (x = logo_x1+1,
86
+             xdst = dst+logo_x1+1,
87
+             xsrc = src+logo_x1+1; x < logo_x2-1; x++, xdst++, xsrc++) {
88
+            interp =
89
+                (topleft[src_linesize*(y-logo_y  -yclipt)]   +
90
+                 topleft[src_linesize*(y-logo_y-1-yclipt)]   +
91
+                 topleft[src_linesize*(y-logo_y+1-yclipt)])  * (logo_w-(x-logo_x))/logo_w
92
+                +
93
+                (topright[src_linesize*(y-logo_y-yclipt)]    +
94
+                 topright[src_linesize*(y-logo_y-1-yclipt)]  +
95
+                 topright[src_linesize*(y-logo_y+1-yclipt)]) * (x-logo_x)/logo_w
96
+                +
97
+                (topleft[x-logo_x-xclipl]    +
98
+                 topleft[x-logo_x-1-xclipl]  +
99
+                 topleft[x-logo_x+1-xclipl]) * (logo_h-(y-logo_y))/logo_h
100
+                +
101
+                (botleft[x-logo_x-xclipl]    +
102
+                 botleft[x-logo_x-1-xclipl]  +
103
+                 botleft[x-logo_x+1-xclipl]) * (y-logo_y)/logo_h;
104
+            interp /= 6;
105
+
106
+            if (y >= logo_y+band && y < logo_y+logo_h-band &&
107
+                x >= logo_x+band && x < logo_x+logo_w-band) {
108
+                *xdst = interp;
109
+            } else {
110
+                dist = 0;
111
+                if      (x < logo_x+band)
112
+                    dist = FFMAX(dist, logo_x-x+band);
113
+                else if (x >= logo_x+logo_w-band)
114
+                    dist = FFMAX(dist, x-(logo_x+logo_w-1-band));
115
+
116
+                if      (y < logo_y+band)
117
+                    dist = FFMAX(dist, logo_y-y+band);
118
+                else if (y >= logo_y+logo_h-band)
119
+                    dist = FFMAX(dist, y-(logo_y+logo_h-1-band));
120
+
121
+                *xdst = (*xsrc*dist + interp*(band-dist))/band;
122
+                if (show && (dist == band-1))
123
+                    *xdst = 0;
124
+            }
125
+        }
126
+
127
+        dst += dst_linesize;
128
+        src += src_linesize;
129
+    }
130
+}
131
+
132
+typedef struct {
133
+    const AVClass *class;
134
+    int x, y, w, h, band, show;
135
+}  DelogoContext;
136
+
137
+#define OFFSET(x) offsetof(DelogoContext, x)
138
+
139
+static const AVOption delogo_options[]= {
140
+    {"x",    "set logo x position",       OFFSET(x),    FF_OPT_TYPE_INT, {.dbl=-1}, -1, INT_MAX },
141
+    {"y",    "set logo y position",       OFFSET(y),    FF_OPT_TYPE_INT, {.dbl=-1}, -1, INT_MAX },
142
+    {"w",    "set logo width",            OFFSET(w),    FF_OPT_TYPE_INT, {.dbl=-1}, -1, INT_MAX },
143
+    {"h",    "set logo height",           OFFSET(h),    FF_OPT_TYPE_INT, {.dbl=-1}, -1, INT_MAX },
144
+    {"band", "set delogo area band size", OFFSET(band), FF_OPT_TYPE_INT, {.dbl= 4}, -1, INT_MAX },
145
+    {"t",    "set delogo area band size", OFFSET(band), FF_OPT_TYPE_INT, {.dbl= 4}, -1, INT_MAX },
146
+    {"show", "show delogo area",          OFFSET(show), FF_OPT_TYPE_INT, {.dbl= 0},  0, 1       },
147
+    {NULL},
148
+};
149
+
150
+static const char *delogo_get_name(void *ctx)
151
+{
152
+    return "delogo";
153
+}
154
+
155
+static const AVClass delogo_class = {
156
+    "DelogoContext",
157
+    delogo_get_name,
158
+    delogo_options
159
+};
160
+
161
+static int query_formats(AVFilterContext *ctx)
162
+{
163
+    enum PixelFormat pix_fmts[] = {
164
+        PIX_FMT_YUV444P,  PIX_FMT_YUV422P,  PIX_FMT_YUV420P,
165
+        PIX_FMT_YUV411P,  PIX_FMT_YUV410P,  PIX_FMT_YUV440P,
166
+        PIX_FMT_YUVA420P, PIX_FMT_GRAY8,
167
+        PIX_FMT_NONE
168
+    };
169
+
170
+    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
171
+    return 0;
172
+}
173
+
174
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
175
+{
176
+    DelogoContext *delogo = ctx->priv;
177
+    int ret = 0;
178
+
179
+    delogo->class = &delogo_class;
180
+    av_opt_set_defaults2(delogo, 0, 0);
181
+
182
+    if (args)
183
+        ret = sscanf(args, "%d:%d:%d:%d:%d",
184
+                     &delogo->x, &delogo->y, &delogo->w, &delogo->h, &delogo->band);
185
+    if (ret == 5) {
186
+        if (delogo->band < 0)
187
+            delogo->show = 1;
188
+    } else if ((ret = (av_set_options_string(delogo, args, "=", ":"))) < 0) {
189
+        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
190
+        return ret;
191
+    }
192
+
193
+#define CHECK_UNSET_OPT(opt)                                            \
194
+    if (delogo->opt == -1) {                                            \
195
+        av_log(delogo, AV_LOG_ERROR, "Option %s was not set.\n", #opt); \
196
+        return AVERROR(EINVAL);                                         \
197
+    }
198
+    CHECK_UNSET_OPT(x);
199
+    CHECK_UNSET_OPT(y);
200
+    CHECK_UNSET_OPT(w);
201
+    CHECK_UNSET_OPT(h);
202
+
203
+    if (delogo->show)
204
+        delogo->band = 4;
205
+
206
+    av_log(ctx, AV_LOG_INFO, "x:%d y:%d, w:%d h:%d band:%d show:%d\n",
207
+           delogo->x, delogo->y, delogo->w, delogo->h, delogo->band, delogo->show);
208
+
209
+    delogo->w += delogo->band*2;
210
+    delogo->h += delogo->band*2;
211
+    delogo->x -= delogo->band;
212
+    delogo->y -= delogo->band;
213
+
214
+    return 0;
215
+}
216
+
217
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
218
+{
219
+    AVFilterLink *outlink = inlink->dst->outputs[0];
220
+    AVFilterBufferRef *outpicref;
221
+
222
+    if (inpicref->perms & AV_PERM_PRESERVE) {
223
+        outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
224
+                                              outlink->w, outlink->h);
225
+        avfilter_copy_buffer_ref_props(outpicref, inpicref);
226
+        outpicref->video->w = outlink->w;
227
+        outpicref->video->h = outlink->h;
228
+    } else
229
+        outpicref = inpicref;
230
+
231
+    outlink->out_buf = outpicref;
232
+    avfilter_start_frame(outlink, avfilter_ref_buffer(outpicref, ~0));
233
+}
234
+
235
+static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
236
+
237
+static void end_frame(AVFilterLink *inlink)
238
+{
239
+    DelogoContext *delogo = inlink->dst->priv;
240
+    AVFilterLink *outlink = inlink->dst->outputs[0];
241
+    AVFilterBufferRef *inpicref  = inlink ->cur_buf;
242
+    AVFilterBufferRef *outpicref = outlink->out_buf;
243
+    int direct = inpicref == outpicref;
244
+    int hsub0 = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
245
+    int vsub0 = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
246
+    int plane;
247
+
248
+    for (plane = 0; plane < 4 && inpicref->data[plane]; plane++) {
249
+        int hsub = plane == 1 || plane == 2 ? hsub0 : 0;
250
+        int vsub = plane == 1 || plane == 2 ? vsub0 : 0;
251
+
252
+        apply_delogo(outpicref->data[plane], outpicref->linesize[plane],
253
+                     inpicref ->data[plane], inpicref ->linesize[plane],
254
+                     inlink->w>>hsub, inlink->h>>vsub,
255
+                     delogo->x>>hsub, delogo->y>>vsub,
256
+                     delogo->w>>hsub, delogo->h>>vsub,
257
+                     delogo->band>>FFMIN(hsub, vsub),
258
+                     delogo->show, direct);
259
+    }
260
+
261
+    avfilter_draw_slice(outlink, 0, inlink->h, 1);
262
+    avfilter_end_frame(outlink);
263
+    avfilter_unref_buffer(inpicref);
264
+    if (!direct)
265
+        avfilter_unref_buffer(outpicref);
266
+}
267
+
268
+AVFilter avfilter_vf_delogo = {
269
+    .name          = "delogo",
270
+    .description   = NULL_IF_CONFIG_SMALL("Remove logo from input video."),
271
+    .priv_size     = sizeof(DelogoContext),
272
+    .init          = init,
273
+    .query_formats = query_formats,
274
+
275
+    .inputs    = (AVFilterPad[]) {{ .name             = "default",
276
+                                    .type             = AVMEDIA_TYPE_VIDEO,
277
+                                    .get_video_buffer = avfilter_null_get_video_buffer,
278
+                                    .start_frame      = start_frame,
279
+                                    .draw_slice       = null_draw_slice,
280
+                                    .end_frame        = end_frame,
281
+                                    .min_perms        = AV_PERM_WRITE | AV_PERM_READ,
282
+                                    .rej_perms        = AV_PERM_PRESERVE },
283
+                                  { .name = NULL}},
284
+    .outputs   = (AVFilterPad[]) {{ .name             = "default",
285
+                                    .type             = AVMEDIA_TYPE_VIDEO, },
286
+                                  { .name = NULL}},
287
+};