2 * Copyright (c) 2002 Jindrich Makovicka <makovick@gmail.com>
3 * Copyright (c) 2011 Stefano Sabatini
5 * This file is part of Libav.
7 * Libav is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * Libav is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with Libav; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * A very simple tv station logo remover
25 * Ported from MPlayer libmpcodecs/vf_delogo.c.
28 #include "libavutil/common.h"
29 #include "libavutil/imgutils.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/pixdesc.h"
38 * Apply a simple delogo algorithm to the image in dst and put the
41 * The algorithm is only applied to the region specified by the logo
44 * @param w width of the input image
45 * @param h height of the input image
46 * @param logo_x x coordinate of the top left corner of the logo region
47 * @param logo_y y coordinate of the top left corner of the logo region
48 * @param logo_w width of the logo
49 * @param logo_h height of the logo
50 * @param band the size of the band around the processed area
51 * @param show show a rectangle around the processed area, useful for
53 * @param direct if non-zero perform in-place processing
55 static void apply_delogo(uint8_t *dst
, int dst_linesize
,
56 uint8_t *src
, int src_linesize
,
58 int logo_x
, int logo_y
, int logo_w
, int logo_h
,
59 int band
, int show
, int direct
)
65 uint8_t *topleft
, *botleft
, *topright
;
66 int xclipl
, xclipr
, yclipt
, yclipb
;
67 int logo_x1
, logo_x2
, logo_y1
, logo_y2
;
69 xclipl
= FFMAX(-logo_x
, 0);
70 xclipr
= FFMAX(logo_x
+logo_w
-w
, 0);
71 yclipt
= FFMAX(-logo_y
, 0);
72 yclipb
= FFMAX(logo_y
+logo_h
-h
, 0);
74 logo_x1
= logo_x
+ xclipl
;
75 logo_x2
= logo_x
+ logo_w
- xclipr
;
76 logo_y1
= logo_y
+ yclipt
;
77 logo_y2
= logo_y
+ logo_h
- yclipb
;
79 topleft
= src
+logo_y1
* src_linesize
+logo_x1
;
80 topright
= src
+logo_y1
* src_linesize
+logo_x2
-1;
81 botleft
= src
+(logo_y2
-1) * src_linesize
+logo_x1
;
84 av_image_copy_plane(dst
, dst_linesize
, src
, src_linesize
, w
, h
);
86 dst
+= (logo_y1
+ 1) * dst_linesize
;
87 src
+= (logo_y1
+ 1) * src_linesize
;
89 for (y
= logo_y1
+1; y
< logo_y2
-1; y
++) {
92 xsrc
= src
+logo_x1
+1; x
< logo_x2
-1; x
++, xdst
++, xsrc
++) {
93 interp
= (topleft
[src_linesize
*(y
-logo_y
-yclipt
)] +
94 topleft
[src_linesize
*(y
-logo_y
-1-yclipt
)] +
95 topleft
[src_linesize
*(y
-logo_y
+1-yclipt
)]) * (logo_w
-(x
-logo_x
))/logo_w
96 + (topright
[src_linesize
*(y
-logo_y
-yclipt
)] +
97 topright
[src_linesize
*(y
-logo_y
-1-yclipt
)] +
98 topright
[src_linesize
*(y
-logo_y
+1-yclipt
)]) * (x
-logo_x
)/logo_w
99 + (topleft
[x
-logo_x
-xclipl
] +
100 topleft
[x
-logo_x
-1-xclipl
] +
101 topleft
[x
-logo_x
+1-xclipl
]) * (logo_h
-(y
-logo_y
))/logo_h
102 + (botleft
[x
-logo_x
-xclipl
] +
103 botleft
[x
-logo_x
-1-xclipl
] +
104 botleft
[x
-logo_x
+1-xclipl
]) * (y
-logo_y
)/logo_h
;
107 if (y
>= logo_y
+band
&& y
< logo_y
+logo_h
-band
&&
108 x
>= logo_x
+band
&& x
< logo_x
+logo_w
-band
) {
113 dist
= FFMAX(dist
, logo_x
-x
+band
);
114 else if (x
>= logo_x
+logo_w
-band
)
115 dist
= FFMAX(dist
, x
-(logo_x
+logo_w
-1-band
));
118 dist
= FFMAX(dist
, logo_y
-y
+band
);
119 else if (y
>= logo_y
+logo_h
-band
)
120 dist
= FFMAX(dist
, y
-(logo_y
+logo_h
-1-band
));
122 *xdst
= (*xsrc
*dist
+ interp
*(band
-dist
))/band
;
123 if (show
&& (dist
== band
-1))
134 const AVClass
*class;
135 int x
, y
, w
, h
, band
, show
;
138 #define OFFSET(x) offsetof(DelogoContext, x)
140 static const AVOption delogo_options
[]= {
141 {"x", "set logo x position", OFFSET(x
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, INT_MAX
},
142 {"y", "set logo y position", OFFSET(y
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, INT_MAX
},
143 {"w", "set logo width", OFFSET(w
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, INT_MAX
},
144 {"h", "set logo height", OFFSET(h
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, INT_MAX
},
145 {"band", "set delogo area band size", OFFSET(band
), AV_OPT_TYPE_INT
, {.i64
= 4}, -1, INT_MAX
},
146 {"t", "set delogo area band size", OFFSET(band
), AV_OPT_TYPE_INT
, {.i64
= 4}, -1, INT_MAX
},
147 {"show", "show delogo area", OFFSET(show
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1 },
151 static const char *delogo_get_name(void *ctx
)
156 static const AVClass delogo_class
= {
157 .class_name
= "DelogoContext",
158 .item_name
= delogo_get_name
,
159 .option
= delogo_options
,
162 static int query_formats(AVFilterContext
*ctx
)
164 enum AVPixelFormat pix_fmts
[] = {
165 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
166 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
167 AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_GRAY8
,
171 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
175 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
177 DelogoContext
*delogo
= ctx
->priv
;
180 delogo
->class = &delogo_class
;
181 av_opt_set_defaults(delogo
);
184 ret
= sscanf(args
, "%d:%d:%d:%d:%d",
185 &delogo
->x
, &delogo
->y
, &delogo
->w
, &delogo
->h
, &delogo
->band
);
187 if (delogo
->band
< 0)
189 } else if ((ret
= (av_set_options_string(delogo
, args
, "=", ":"))) < 0) {
190 av_log(ctx
, AV_LOG_ERROR
, "Error parsing options string: '%s'\n", args
);
194 #define CHECK_UNSET_OPT(opt) \
195 if (delogo->opt == -1) { \
196 av_log(delogo, AV_LOG_ERROR, "Option %s was not set.\n", #opt); \
197 return AVERROR(EINVAL); \
207 av_log(ctx
, AV_LOG_DEBUG
, "x:%d y:%d, w:%d h:%d band:%d show:%d\n",
208 delogo
->x
, delogo
->y
, delogo
->w
, delogo
->h
, delogo
->band
, delogo
->show
);
210 delogo
->w
+= delogo
->band
*2;
211 delogo
->h
+= delogo
->band
*2;
212 delogo
->x
-= delogo
->band
;
213 delogo
->y
-= delogo
->band
;
218 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
220 DelogoContext
*delogo
= inlink
->dst
->priv
;
221 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
222 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
224 int hsub0
= desc
->log2_chroma_w
;
225 int vsub0
= desc
->log2_chroma_h
;
229 if (av_frame_is_writable(in
)) {
233 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
236 return AVERROR(ENOMEM
);
239 av_frame_copy_props(out
, in
);
240 out
->width
= outlink
->w
;
241 out
->height
= outlink
->h
;
244 for (plane
= 0; plane
< 4 && in
->data
[plane
]; plane
++) {
245 int hsub
= plane
== 1 || plane
== 2 ? hsub0
: 0;
246 int vsub
= plane
== 1 || plane
== 2 ? vsub0
: 0;
248 apply_delogo(out
->data
[plane
], out
->linesize
[plane
],
249 in
->data
[plane
], in
->linesize
[plane
],
250 inlink
->w
>>hsub
, inlink
->h
>>vsub
,
251 delogo
->x
>>hsub
, delogo
->y
>>vsub
,
252 delogo
->w
>>hsub
, delogo
->h
>>vsub
,
253 delogo
->band
>>FFMIN(hsub
, vsub
),
254 delogo
->show
, direct
);
260 return ff_filter_frame(outlink
, out
);
263 static const AVFilterPad avfilter_vf_delogo_inputs
[] = {
266 .type
= AVMEDIA_TYPE_VIDEO
,
267 .get_video_buffer
= ff_null_get_video_buffer
,
268 .filter_frame
= filter_frame
,
273 static const AVFilterPad avfilter_vf_delogo_outputs
[] = {
276 .type
= AVMEDIA_TYPE_VIDEO
,
281 AVFilter avfilter_vf_delogo
= {
283 .description
= NULL_IF_CONFIG_SMALL("Remove logo from input video."),
284 .priv_size
= sizeof(DelogoContext
),
286 .query_formats
= query_formats
,
288 .inputs
= avfilter_vf_delogo_inputs
,
289 .outputs
= avfilter_vf_delogo_outputs
,