LCOV - code coverage report
Current view: top level - libavfilter - vf_delogo.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 102 120 85.0 %
Date: 2017-12-17 11:58:42 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2002 Jindrich Makovicka <makovick@gmail.com>
       3             :  * Copyright (c) 2011 Stefano Sabatini
       4             :  * Copyright (c) 2013, 2015 Jean Delvare <jdelvare@suse.com>
       5             :  *
       6             :  * This file is part of FFmpeg.
       7             :  *
       8             :  * FFmpeg is free software; you can redistribute it and/or modify
       9             :  * it under the terms of the GNU General Public License as published by
      10             :  * the Free Software Foundation; either version 2 of the License, or
      11             :  * (at your option) any later version.
      12             :  *
      13             :  * FFmpeg is distributed in the hope that it will be useful,
      14             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :  * GNU General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU General Public License along
      19             :  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
      20             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      21             :  */
      22             : 
      23             : /**
      24             :  * @file
      25             :  * A very simple tv station logo remover
      26             :  * Originally imported from MPlayer libmpcodecs/vf_delogo.c,
      27             :  * the algorithm was later improved.
      28             :  */
      29             : 
      30             : #include "libavutil/common.h"
      31             : #include "libavutil/imgutils.h"
      32             : #include "libavutil/opt.h"
      33             : #include "libavutil/pixdesc.h"
      34             : #include "avfilter.h"
      35             : #include "formats.h"
      36             : #include "internal.h"
      37             : #include "video.h"
      38             : 
      39             : /**
      40             :  * Apply a simple delogo algorithm to the image in src and put the
      41             :  * result in dst.
      42             :  *
      43             :  * The algorithm is only applied to the region specified by the logo
      44             :  * parameters.
      45             :  *
      46             :  * @param w      width of the input image
      47             :  * @param h      height of the input image
      48             :  * @param logo_x x coordinate of the top left corner of the logo region
      49             :  * @param logo_y y coordinate of the top left corner of the logo region
      50             :  * @param logo_w width of the logo
      51             :  * @param logo_h height of the logo
      52             :  * @param band   the size of the band around the processed area
      53             :  * @param show   show a rectangle around the processed area, useful for
      54             :  *               parameters tweaking
      55             :  * @param direct if non-zero perform in-place processing
      56             :  */
      57         327 : static void apply_delogo(uint8_t *dst, int dst_linesize,
      58             :                          uint8_t *src, int src_linesize,
      59             :                          int w, int h, AVRational sar,
      60             :                          int logo_x, int logo_y, int logo_w, int logo_h,
      61             :                          unsigned int band, int show, int direct)
      62             : {
      63             :     int x, y;
      64             :     uint64_t interp, weightl, weightr, weightt, weightb, weight;
      65             :     uint8_t *xdst, *xsrc;
      66             : 
      67             :     uint8_t *topleft, *botleft, *topright;
      68             :     unsigned int left_sample, right_sample;
      69             :     int xclipl, xclipr, yclipt, yclipb;
      70             :     int logo_x1, logo_x2, logo_y1, logo_y2;
      71             : 
      72         327 :     xclipl = FFMAX(-logo_x, 0);
      73         327 :     xclipr = FFMAX(logo_x+logo_w-w, 0);
      74         327 :     yclipt = FFMAX(-logo_y, 0);
      75         327 :     yclipb = FFMAX(logo_y+logo_h-h, 0);
      76             : 
      77         327 :     logo_x1 = logo_x + xclipl;
      78         327 :     logo_x2 = logo_x + logo_w - xclipr - 1;
      79         327 :     logo_y1 = logo_y + yclipt;
      80         327 :     logo_y2 = logo_y + logo_h - yclipb - 1;
      81             : 
      82         327 :     topleft  = src+logo_y1 * src_linesize+logo_x1;
      83         327 :     topright = src+logo_y1 * src_linesize+logo_x2;
      84         327 :     botleft  = src+logo_y2 * src_linesize+logo_x1;
      85             : 
      86         327 :     if (!direct)
      87         150 :         av_image_copy_plane(dst, dst_linesize, src, src_linesize, w, h);
      88             : 
      89         327 :     dst += (logo_y1 + 1) * dst_linesize;
      90         327 :     src += (logo_y1 + 1) * src_linesize;
      91             : 
      92        3597 :     for (y = logo_y1+1; y < logo_y2; y++) {
      93        9810 :         left_sample = topleft[src_linesize*(y-logo_y1)]   +
      94        6540 :                       topleft[src_linesize*(y-logo_y1-1)] +
      95        3270 :                       topleft[src_linesize*(y-logo_y1+1)];
      96        9810 :         right_sample = topright[src_linesize*(y-logo_y1)]   +
      97        6540 :                        topright[src_linesize*(y-logo_y1-1)] +
      98        3270 :                        topright[src_linesize*(y-logo_y1+1)];
      99             : 
     100       74992 :         for (x = logo_x1+1,
     101        3270 :              xdst = dst+logo_x1+1,
     102       68452 :              xsrc = src+logo_x1+1; x < logo_x2; x++, xdst++, xsrc++) {
     103             : 
     104       65182 :             if (show && (y == logo_y1+1 || y == logo_y2-1 ||
     105           0 :                          x == logo_x1+1 || x == logo_x2-1)) {
     106           0 :                 *xdst = 0;
     107           0 :                 continue;
     108             :             }
     109             : 
     110             :             /* Weighted interpolation based on relative distances, taking SAR into account */
     111       65182 :             weightl = (uint64_t)              (logo_x2-x) * (y-logo_y1) * (logo_y2-y) * sar.den;
     112       65182 :             weightr = (uint64_t)(x-logo_x1)               * (y-logo_y1) * (logo_y2-y) * sar.den;
     113       65182 :             weightt = (uint64_t)(x-logo_x1) * (logo_x2-x)               * (logo_y2-y) * sar.num;
     114       65182 :             weightb = (uint64_t)(x-logo_x1) * (logo_x2-x) * (y-logo_y1)               * sar.num;
     115             : 
     116       65182 :             interp =
     117       65182 :                 left_sample * weightl
     118       65182 :                 +
     119       65182 :                 right_sample * weightr
     120             :                 +
     121      130364 :                 (topleft[x-logo_x1]    +
     122      130364 :                  topleft[x-logo_x1-1]  +
     123      130364 :                  topleft[x-logo_x1+1]) * weightt
     124       65182 :                 +
     125      130364 :                 (botleft[x-logo_x1]    +
     126      130364 :                  botleft[x-logo_x1-1]  +
     127      130364 :                  botleft[x-logo_x1+1]) * weightb;
     128       65182 :             weight = (weightl + weightr + weightt + weightb) * 3U;
     129       65182 :             interp = ROUNDED_DIV(interp, weight);
     130             : 
     131      130364 :             if (y >= logo_y+band && y < logo_y+logo_h-band &&
     132      130364 :                 x >= logo_x+band && x < logo_x+logo_w-band) {
     133       65182 :                 *xdst = interp;
     134             :             } else {
     135           0 :                 unsigned dist = 0;
     136             : 
     137           0 :                 if      (x < logo_x+band)
     138           0 :                     dist = FFMAX(dist, logo_x-x+band);
     139           0 :                 else if (x >= logo_x+logo_w-band)
     140           0 :                     dist = FFMAX(dist, x-(logo_x+logo_w-1-band));
     141             : 
     142           0 :                 if      (y < logo_y+band)
     143           0 :                     dist = FFMAX(dist, logo_y-y+band);
     144           0 :                 else if (y >= logo_y+logo_h-band)
     145           0 :                     dist = FFMAX(dist, y-(logo_y+logo_h-1-band));
     146             : 
     147           0 :                 *xdst = (*xsrc*dist + interp*(band-dist))/band;
     148             :             }
     149             :         }
     150             : 
     151        3270 :         dst += dst_linesize;
     152        3270 :         src += src_linesize;
     153             :     }
     154         327 : }
     155             : 
     156             : typedef struct DelogoContext {
     157             :     const AVClass *class;
     158             :     int x, y, w, h, band, show;
     159             : }  DelogoContext;
     160             : 
     161             : #define OFFSET(x) offsetof(DelogoContext, x)
     162             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
     163             : 
     164             : static const AVOption delogo_options[]= {
     165             :     { "x",    "set logo x position",       OFFSET(x),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
     166             :     { "y",    "set logo y position",       OFFSET(y),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
     167             :     { "w",    "set logo width",            OFFSET(w),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
     168             :     { "h",    "set logo height",           OFFSET(h),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
     169             : #if LIBAVFILTER_VERSION_MAJOR < 7
     170             :     /* Actual default value for band/t is 1, set in init */
     171             :     { "band", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 =  0 },  0, INT_MAX, FLAGS },
     172             :     { "t",    "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 =  0 },  0, INT_MAX, FLAGS },
     173             : #endif
     174             :     { "show", "show delogo area",          OFFSET(show), AV_OPT_TYPE_BOOL,{ .i64 =  0 },  0, 1,       FLAGS },
     175             :     { NULL }
     176             : };
     177             : 
     178             : AVFILTER_DEFINE_CLASS(delogo);
     179             : 
     180           1 : static int query_formats(AVFilterContext *ctx)
     181             : {
     182             :     static const enum AVPixelFormat pix_fmts[] = {
     183             :         AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
     184             :         AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUV440P,
     185             :         AV_PIX_FMT_YUVA420P, AV_PIX_FMT_GRAY8,
     186             :         AV_PIX_FMT_NONE
     187             :     };
     188           1 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
     189           1 :     if (!fmts_list)
     190           0 :         return AVERROR(ENOMEM);
     191           1 :     return ff_set_common_formats(ctx, fmts_list);
     192             : }
     193             : 
     194           1 : static av_cold int init(AVFilterContext *ctx)
     195             : {
     196           1 :     DelogoContext *s = ctx->priv;
     197             : 
     198             : #define CHECK_UNSET_OPT(opt)                                            \
     199             :     if (s->opt == -1) {                                            \
     200             :         av_log(s, AV_LOG_ERROR, "Option %s was not set.\n", #opt); \
     201             :         return AVERROR(EINVAL);                                         \
     202             :     }
     203           1 :     CHECK_UNSET_OPT(x);
     204           1 :     CHECK_UNSET_OPT(y);
     205           1 :     CHECK_UNSET_OPT(w);
     206           1 :     CHECK_UNSET_OPT(h);
     207             : 
     208             : #if LIBAVFILTER_VERSION_MAJOR < 7
     209             :     if (s->band == 0) { /* Unset, use default */
     210             :         av_log(ctx, AV_LOG_WARNING, "Note: default band value was changed from 4 to 1.\n");
     211             :         s->band = 1;
     212             :     } else if (s->band != 1) {
     213             :         av_log(ctx, AV_LOG_WARNING, "Option band is deprecated.\n");
     214             :     }
     215             : #else
     216           1 :     s->band = 1;
     217             : #endif
     218           1 :     av_log(ctx, AV_LOG_VERBOSE, "x:%d y:%d, w:%d h:%d band:%d show:%d\n",
     219             :            s->x, s->y, s->w, s->h, s->band, s->show);
     220             : 
     221           1 :     s->w += s->band*2;
     222           1 :     s->h += s->band*2;
     223           1 :     s->x -= s->band;
     224           1 :     s->y -= s->band;
     225             : 
     226           1 :     return 0;
     227             : }
     228             : 
     229           1 : static int config_input(AVFilterLink *inlink)
     230             : {
     231           1 :     DelogoContext *s = inlink->dst->priv;
     232             : 
     233             :     /* Check whether the logo area fits in the frame */
     234           2 :     if (s->x + (s->band - 1) < 0 || s->x + s->w - (s->band*2 - 2) > inlink->w ||
     235           2 :         s->y + (s->band - 1) < 0 || s->y + s->h - (s->band*2 - 2) > inlink->h) {
     236           0 :         av_log(s, AV_LOG_ERROR, "Logo area is outside of the frame.\n");
     237           0 :         return AVERROR(EINVAL);
     238             :     }
     239             : 
     240           1 :     return 0;
     241             : }
     242             : 
     243         109 : static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     244             : {
     245         109 :     DelogoContext *s = inlink->dst->priv;
     246         109 :     AVFilterLink *outlink = inlink->dst->outputs[0];
     247         109 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     248             :     AVFrame *out;
     249         109 :     int hsub0 = desc->log2_chroma_w;
     250         109 :     int vsub0 = desc->log2_chroma_h;
     251         109 :     int direct = 0;
     252             :     int plane;
     253             :     AVRational sar;
     254             : 
     255         109 :     if (av_frame_is_writable(in)) {
     256          59 :         direct = 1;
     257          59 :         out = in;
     258             :     } else {
     259          50 :         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     260          50 :         if (!out) {
     261           0 :             av_frame_free(&in);
     262           0 :             return AVERROR(ENOMEM);
     263             :         }
     264             : 
     265          50 :         av_frame_copy_props(out, in);
     266             :     }
     267             : 
     268         109 :     sar = in->sample_aspect_ratio;
     269             :     /* Assume square pixels if SAR is unknown */
     270         109 :     if (!sar.num)
     271         109 :         sar.num = sar.den = 1;
     272             : 
     273         436 :     for (plane = 0; plane < desc->nb_components; plane++) {
     274         327 :         int hsub = plane == 1 || plane == 2 ? hsub0 : 0;
     275         327 :         int vsub = plane == 1 || plane == 2 ? vsub0 : 0;
     276             : 
     277        3270 :         apply_delogo(out->data[plane], out->linesize[plane],
     278         654 :                      in ->data[plane], in ->linesize[plane],
     279         327 :                      AV_CEIL_RSHIFT(inlink->w, hsub),
     280         327 :                      AV_CEIL_RSHIFT(inlink->h, vsub),
     281         654 :                      sar, s->x>>hsub, s->y>>vsub,
     282             :                      /* Up and left borders were rounded down, inject lost bits
     283             :                       * into width and height to avoid error accumulation */
     284         327 :                      AV_CEIL_RSHIFT(s->w + (s->x & ((1<<hsub)-1)), hsub),
     285         327 :                      AV_CEIL_RSHIFT(s->h + (s->y & ((1<<vsub)-1)), vsub),
     286         327 :                      s->band>>FFMIN(hsub, vsub),
     287             :                      s->show, direct);
     288             :     }
     289             : 
     290         109 :     if (!direct)
     291          50 :         av_frame_free(&in);
     292             : 
     293         109 :     return ff_filter_frame(outlink, out);
     294             : }
     295             : 
     296             : static const AVFilterPad avfilter_vf_delogo_inputs[] = {
     297             :     {
     298             :         .name         = "default",
     299             :         .type         = AVMEDIA_TYPE_VIDEO,
     300             :         .filter_frame = filter_frame,
     301             :         .config_props = config_input,
     302             :     },
     303             :     { NULL }
     304             : };
     305             : 
     306             : static const AVFilterPad avfilter_vf_delogo_outputs[] = {
     307             :     {
     308             :         .name = "default",
     309             :         .type = AVMEDIA_TYPE_VIDEO,
     310             :     },
     311             :     { NULL }
     312             : };
     313             : 
     314             : AVFilter ff_vf_delogo = {
     315             :     .name          = "delogo",
     316             :     .description   = NULL_IF_CONFIG_SMALL("Remove logo from input video."),
     317             :     .priv_size     = sizeof(DelogoContext),
     318             :     .priv_class    = &delogo_class,
     319             :     .init          = init,
     320             :     .query_formats = query_formats,
     321             :     .inputs        = avfilter_vf_delogo_inputs,
     322             :     .outputs       = avfilter_vf_delogo_outputs,
     323             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     324             : };

Generated by: LCOV version 1.13