/** * Generate a scaled down image with half width, height, and intensity. * * This function not only scales down an image, but halves the value * in each pixel too. The purpose of this is to produce a chroma * filter image out of a luma filter image. The pixel values store the * distance to the edge of the logo and halving the dimensions halves * the distance. This function rounds up, because a downwards rounding * error could cause the filter to fail, but an upwards rounding error * will only cause a minor amount of excess blur in the chroma planes. */ static void generate_half_size_image(const uint8_t *src_data, int src_linesize, uint8_t *dst_data, int dst_linesize, int src_w, int src_h, int *max_mask_size) { int x, y; /* Copy over the image data, using the average of 4 pixels for to * calculate each downsampled pixel. */ for (y = 0; y < src_h/2; y++) { for (x = 0; x < src_w/2; x++) { /* Set the pixel if there exists a non-zero value in the * source pixels, else clear it. */ dst_data[(y * dst_linesize) + x] = src_data[((y << 1) * src_linesize) + (x << 1)] || src_data[((y << 1) * src_linesize) + (x << 1) + 1] || src_data[(((y << 1) + 1) * src_linesize) + (x << 1)] || src_data[(((y << 1) + 1) * src_linesize) + (x << 1) + 1]; dst_data[(y * dst_linesize) + x] = FFMIN(1, dst_data[(y * dst_linesize) + x]); } } convert_mask_to_strength_mask(dst_data, dst_linesize, src_w/2, src_h/2, 0, max_mask_size); }
/** * \brief Initializes our filter. * * \param args The arguments passed in from the command line go here. This * filter expects only a single argument telling it where the PGM * or PPM file that describes the logo region is. * * This sets up our instance variables and parses the arguments to the filter. */ static int vf_open(vf_instance_t *vf, char *args) { vf->priv = safe_malloc(sizeof(vf_priv_s)); vf->uninit = uninit; /* Load our filter image. */ if (args) vf->priv->filter = load_pgm(args); else { mp_msg(MSGT_VFILTER, MSGL_ERR, "[vf]remove_logo usage: remove_logo=/path/to/filter_image_file.pgm\n"); free(vf->priv); return 0; } if (vf->priv->filter == NULL) { /* Error message was displayed by load_pgm(). */ free(vf->priv); return 0; } /* Create the scaled down filter image for the chroma planes. */ convert_mask_to_strength_mask(vf, vf->priv->filter); vf->priv->half_size_filter = generate_half_size_image(vf, vf->priv->filter); /* Now that we know how many masks we need (the info is in vf), we can generate the masks. */ initialize_masks(vf); /* Calculate our bounding rectangles, which determine in what region the logo resides for faster processing. */ calculate_bounding_rectangle(&vf->priv->bounding_rectangle_posx1, &vf->priv->bounding_rectangle_posy1, &vf->priv->bounding_rectangle_posx2, &vf->priv->bounding_rectangle_posy2, vf->priv->filter); calculate_bounding_rectangle(&vf->priv->bounding_rectangle_half_size_posx1, &vf->priv->bounding_rectangle_half_size_posy1, &vf->priv->bounding_rectangle_half_size_posx2, &vf->priv->bounding_rectangle_half_size_posy2, vf->priv->half_size_filter); vf->config=config; vf->put_image=put_image; vf->query_format=query_format; return 1; }
static av_cold int init(AVFilterContext *ctx, const char *args) { RemovelogoContext *removelogo = ctx->priv; int ***mask; int ret = 0; int a, b, c, w, h; int full_max_mask_size, half_max_mask_size; if (!args) { av_log(ctx, AV_LOG_ERROR, "An image file must be specified as argument\n"); return AVERROR(EINVAL); } /* Load our mask image. */ if ((ret = load_mask(&removelogo->full_mask_data, &w, &h, args, ctx)) < 0) return ret; removelogo->mask_w = w; removelogo->mask_h = h; convert_mask_to_strength_mask(removelogo->full_mask_data, w, w, h, 16, &full_max_mask_size); /* Create the scaled down mask image for the chroma planes. */ if (!(removelogo->half_mask_data = av_mallocz(w/2 * h/2))) return AVERROR(ENOMEM); generate_half_size_image(removelogo->full_mask_data, w, removelogo->half_mask_data, w/2, w, h, &half_max_mask_size); removelogo->max_mask_size = FFMAX(full_max_mask_size, half_max_mask_size); /* Create a circular mask for each size up to max_mask_size. When the filter is applied, the mask size is determined on a pixel by pixel basis, with pixels nearer the edge of the logo getting smaller mask sizes. */ mask = (int ***)av_malloc(sizeof(int **) * (removelogo->max_mask_size + 1)); if (!mask) return AVERROR(ENOMEM); for (a = 0; a <= removelogo->max_mask_size; a++) { mask[a] = (int **)av_malloc(sizeof(int *) * ((a * 2) + 1)); if (!mask[a]) return AVERROR(ENOMEM); for (b = -a; b <= a; b++) { mask[a][b + a] = (int *)av_malloc(sizeof(int) * ((a * 2) + 1)); if (!mask[a][b + a]) return AVERROR(ENOMEM); for (c = -a; c <= a; c++) { if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */ mask[a][b + a][c + a] = 1; else mask[a][b + a][c + a] = 0; } } } removelogo->mask = mask; /* Calculate our bounding rectangles, which determine in what * region the logo resides for faster processing. */ ff_calculate_bounding_box(&removelogo->full_mask_bbox, removelogo->full_mask_data, w, w, h, 0); ff_calculate_bounding_box(&removelogo->half_mask_bbox, removelogo->half_mask_data, w/2, w/2, h/2, 0); #define SHOW_LOGO_INFO(mask_type) \ av_log(ctx, AV_LOG_VERBOSE, #mask_type " x1:%d x2:%d y1:%d y2:%d max_mask_size:%d\n", \ removelogo->mask_type##_mask_bbox.x1, removelogo->mask_type##_mask_bbox.x2, \ removelogo->mask_type##_mask_bbox.y1, removelogo->mask_type##_mask_bbox.y2, \ mask_type##_max_mask_size); SHOW_LOGO_INFO(full); SHOW_LOGO_INFO(half); return 0; }