Browse code

avfilter: Add cover_rect filter

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Michael Niedermayer authored on 2015/05/03 04:00:38
Showing 6 changed files
... ...
@@ -2635,6 +2635,7 @@ blackframe_filter_deps="gpl"
2635 2635
 boxblur_filter_deps="gpl"
2636 2636
 bs2b_filter_deps="libbs2b"
2637 2637
 colormatrix_filter_deps="gpl"
2638
+cover_rect_filter_deps="gpl"
2638 2639
 cropdetect_filter_deps="gpl"
2639 2640
 delogo_filter_deps="gpl"
2640 2641
 deshake_filter_select="pixelutils"
... ...
@@ -5193,6 +5193,40 @@ ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.m
5193 5193
 @end example
5194 5194
 @end itemize
5195 5195
 
5196
+@section cover_rect
5197
+
5198
+Cover a rectangular object
5199
+
5200
+It accepts the following options:
5201
+
5202
+@table @option
5203
+@item cover
5204
+Filepath of the optional cover image, needs to be in yuv420.
5205
+
5206
+@item mode
5207
+Set covering mode.
5208
+
5209
+It accepts the following values:
5210
+@table @samp
5211
+@item cover
5212
+cover it by the supplied image
5213
+@item blur
5214
+cover it by interpolating the surrounding pixels
5215
+@end table
5216
+
5217
+Default value is @var{blur}.
5218
+@end table
5219
+
5220
+@subsection Examples
5221
+
5222
+@itemize
5223
+@item
5224
+Generate a representative palette of a given video using @command{ffmpeg}:
5225
+@example
5226
+ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.mkv
5227
+@end example
5228
+@end itemize
5229
+
5196 5230
 @anchor{format}
5197 5231
 @section format
5198 5232
 
... ...
@@ -105,6 +105,7 @@ OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
105 105
 OBJS-$(CONFIG_COLORLEVELS_FILTER)            += vf_colorlevels.o
106 106
 OBJS-$(CONFIG_COLORMATRIX_FILTER)            += vf_colormatrix.o
107 107
 OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
108
+OBJS-$(CONFIG_COVER_RECT_FILTER)             += vf_cover_rect.o
108 109
 OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
109 110
 OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
110 111
 OBJS-$(CONFIG_CURVES_FILTER)                 += vf_curves.o
... ...
@@ -121,6 +121,7 @@ void avfilter_register_all(void)
121 121
     REGISTER_FILTER(COLORLEVELS,    colorlevels,    vf);
122 122
     REGISTER_FILTER(COLORMATRIX,    colormatrix,    vf);
123 123
     REGISTER_FILTER(COPY,           copy,           vf);
124
+    REGISTER_FILTER(COVER_RECT,     cover_rect,     vf);
124 125
     REGISTER_FILTER(CROP,           crop,           vf);
125 126
     REGISTER_FILTER(CROPDETECT,     cropdetect,     vf);
126 127
     REGISTER_FILTER(CURVES,         curves,         vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR  5
33
-#define LIBAVFILTER_VERSION_MINOR  15
33
+#define LIBAVFILTER_VERSION_MINOR  16
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,255 @@
0
+/*
1
+ * Copyright (c) 2014-2015 Michael Niedermayer <michaelni@gmx.at>
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
+/**
21
+ * @todo switch to dualinput
22
+ */
23
+
24
+#include "libavutil/avassert.h"
25
+#include "libavutil/imgutils.h"
26
+#include "libavutil/opt.h"
27
+#include "internal.h"
28
+
29
+#include "lavfutils.h"
30
+
31
+enum mode {
32
+    MODE_COVER,
33
+    MODE_BLUR,
34
+    NB_MODES
35
+};
36
+
37
+typedef struct CoverContext {
38
+    AVClass *class;
39
+    int mode;
40
+    char *cover_filename;
41
+    AVFrame *cover_frame;
42
+    int width, height;
43
+} CoverContext;
44
+
45
+#define OFFSET(x) offsetof(CoverContext, x)
46
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
47
+static const AVOption cover_rect_options[] = {
48
+    { "cover",  "cover bitmap filename",  OFFSET(cover_filename),  AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
49
+    { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, "mode" },
50
+        { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, "mode" },
51
+        { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, "mode" },
52
+    { NULL }
53
+};
54
+
55
+static const AVClass cover_rect_class = {
56
+    .class_name       = "cover_rect",
57
+    .item_name        = av_default_item_name,
58
+    .option           = cover_rect_options,
59
+    .version          = LIBAVUTIL_VERSION_INT,
60
+    .category         = AV_CLASS_CATEGORY_FILTER,
61
+};
62
+
63
+static int query_formats(AVFilterContext *ctx)
64
+{
65
+    static const enum AVPixelFormat pix_fmts[] = {
66
+        AV_PIX_FMT_YUV420P,
67
+        AV_PIX_FMT_YUVJ420P,
68
+        AV_PIX_FMT_NONE
69
+    };
70
+
71
+    return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
72
+}
73
+
74
+static int config_input(AVFilterLink *inlink)
75
+{
76
+    return 0;
77
+}
78
+
79
+static void cover_rect(CoverContext *cover, AVFrame *in, int offx, int offy)
80
+{
81
+    int x, y, p;
82
+
83
+    for (p = 0; p < 3; p++) {
84
+        uint8_t *data = in->data[p] + (offx>>!!p) + (offy>>!!p) * in->linesize[p];
85
+        const uint8_t *src = cover->cover_frame->data[p];
86
+        int w = FF_CEIL_RSHIFT(cover->cover_frame->width , !!p);
87
+        int h = FF_CEIL_RSHIFT(cover->cover_frame->height, !!p);
88
+        for (y = 0; y < h; y++) {
89
+            for (x = 0; x < w; x++) {
90
+                data[x] = src[x];
91
+            }
92
+            data += in->linesize[p];
93
+            src += cover->cover_frame->linesize[p];
94
+        }
95
+    }
96
+}
97
+static void blur(CoverContext *cover, AVFrame *in, int offx, int offy)
98
+{
99
+    int x, y, p;
100
+
101
+    for (p=0; p<3; p++) {
102
+        int ox = offx>>!!p;
103
+        int oy = offy>>!!p;
104
+        int stride = in->linesize[p];
105
+        uint8_t *data = in->data[p] + ox + oy * stride;
106
+        int w = FF_CEIL_RSHIFT(cover->width , !!p);
107
+        int h = FF_CEIL_RSHIFT(cover->height, !!p);
108
+        int iw = FF_CEIL_RSHIFT(in->width , !!p);
109
+        int ih = FF_CEIL_RSHIFT(in->height, !!p);
110
+        for (y = 0; y < h; y++) {
111
+            for (x = 0; x < w; x++) {
112
+                int c = 0;
113
+                int s = 0;
114
+                if (ox) {
115
+                    int scale = 65536 / (x + 1);
116
+                    s += data[-1 + y*stride] * scale;
117
+                    c += scale;
118
+                }
119
+                if (oy) {
120
+                    int scale = 65536 / (y + 1);
121
+                    s += data[x - stride] * scale;
122
+                    c += scale;
123
+                }
124
+                if (ox + w < iw) {
125
+                    int scale = 65536 / (w - x);
126
+                    s += data[w + y*stride] * scale;
127
+                    c += scale;
128
+                }
129
+                if (oy + h < ih) {
130
+                    int scale = 65536 / (h - y);
131
+                    s += data[x + h*stride] * scale;
132
+                    c += scale;
133
+                }
134
+                data[x + y*stride] = (s + (c>>1)) / c;
135
+            }
136
+        }
137
+    }
138
+}
139
+
140
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
141
+{
142
+    AVFilterContext *ctx = inlink->dst;
143
+    CoverContext *cover = ctx->priv;
144
+    AVDictionaryEntry *ex, *ey, *ew, *eh;
145
+    int x = -1, y = -1, w = -1, h = -1;
146
+    char *xendptr = NULL, *yendptr = NULL, *wendptr = NULL, *hendptr = NULL;
147
+
148
+    ex = av_dict_get(in->metadata, "lavfi.rect.x", NULL, AV_DICT_MATCH_CASE);
149
+    ey = av_dict_get(in->metadata, "lavfi.rect.y", NULL, AV_DICT_MATCH_CASE);
150
+    ew = av_dict_get(in->metadata, "lavfi.rect.w", NULL, AV_DICT_MATCH_CASE);
151
+    eh = av_dict_get(in->metadata, "lavfi.rect.h", NULL, AV_DICT_MATCH_CASE);
152
+    if (ex && ey && ew && eh) {
153
+        x = strtol(ex->value, &xendptr, 10);
154
+        y = strtol(ey->value, &yendptr, 10);
155
+        w = strtol(ew->value, &wendptr, 10);
156
+        h = strtol(eh->value, &hendptr, 10);
157
+    }
158
+
159
+    if (!xendptr || *xendptr || !yendptr || *yendptr ||
160
+        !wendptr || *wendptr || !hendptr || !hendptr
161
+    ) {
162
+        return ff_filter_frame(ctx->outputs[0], in);
163
+    }
164
+
165
+    if (w > in->width || h > in->height || w <= 0 || h <= 0)
166
+        return AVERROR(EINVAL);
167
+
168
+    if (cover->cover_frame) {
169
+        if (w != cover->cover_frame->width || h != cover->cover_frame->height)
170
+            return AVERROR(EINVAL);
171
+    }
172
+
173
+    cover->width  = w;
174
+    cover->height = h;
175
+
176
+    x = av_clip(x, 0, in->width  - w);
177
+    y = av_clip(y, 0, in->height - h);
178
+
179
+    av_frame_make_writable(in);
180
+
181
+    if (cover->mode == MODE_BLUR) {
182
+        blur (cover, in, x, y);
183
+    } else {
184
+        cover_rect(cover, in, x, y);
185
+    }
186
+    return ff_filter_frame(ctx->outputs[0], in);
187
+}
188
+
189
+static av_cold void uninit(AVFilterContext *ctx)
190
+{
191
+    CoverContext *cover = ctx->priv;
192
+
193
+    if (cover->cover_frame)
194
+        av_freep(&cover->cover_frame->data[0]);
195
+}
196
+
197
+static av_cold int init(AVFilterContext *ctx)
198
+{
199
+    CoverContext *cover = ctx->priv;
200
+    int ret;
201
+
202
+    if (cover->mode == MODE_COVER) {
203
+        if (!cover->cover_filename) {
204
+            av_log(ctx, AV_LOG_ERROR, "cover filename not set\n");
205
+            return AVERROR(EINVAL);
206
+        }
207
+
208
+        cover->cover_frame = av_frame_alloc();
209
+        if (!cover->cover_frame)
210
+            return AVERROR(ENOMEM);
211
+
212
+        if ((ret = ff_load_image(cover->cover_frame->data, cover->cover_frame->linesize,
213
+                                &cover->cover_frame->width, &cover->cover_frame->height,
214
+                                &cover->cover_frame->format, cover->cover_filename, ctx)) < 0)
215
+            return ret;
216
+
217
+        if (cover->cover_frame->format != AV_PIX_FMT_YUV420P && cover->cover_frame->format != AV_PIX_FMT_YUVJ420P) {
218
+            av_log(ctx, AV_LOG_ERROR, "cover image is not a YUV420 image\n");
219
+            return AVERROR(EINVAL);
220
+        }
221
+    }
222
+
223
+    return 0;
224
+}
225
+
226
+static const AVFilterPad cover_rect_inputs[] = {
227
+    {
228
+        .name         = "default",
229
+        .type         = AVMEDIA_TYPE_VIDEO,
230
+        .config_props = config_input,
231
+        .filter_frame = filter_frame,
232
+    },
233
+    { NULL }
234
+};
235
+
236
+static const AVFilterPad cover_rect_outputs[] = {
237
+    {
238
+        .name = "default",
239
+        .type = AVMEDIA_TYPE_VIDEO,
240
+    },
241
+    { NULL }
242
+};
243
+
244
+AVFilter ff_vf_cover_rect = {
245
+    .name            = "cover_rect",
246
+    .description     = NULL_IF_CONFIG_SMALL("Find and cover a user specified object"),
247
+    .priv_size       = sizeof(CoverContext),
248
+    .init            = init,
249
+    .uninit          = uninit,
250
+    .query_formats   = query_formats,
251
+    .inputs          = cover_rect_inputs,
252
+    .outputs         = cover_rect_outputs,
253
+    .priv_class      = &cover_rect_class,
254
+};