vf_remove_logo.c
来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 910 行 · 第 1/3 页
C
910 行
/*Copyright 2005 Robert Edele.e-mail: yartrebo@earthlink.netThis program is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by the FreeSoftware Foundation; either version 2 of the License, or (at your option)any later version.This program is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITYor FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for moredetails.You should have reveived a copy of the GNU General Public Licensealong with this program; if not, write to theFree Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA02110-1301 USA__________________________________________________________________________| Robert Edele Fri. 4-Feb-2005 || This program loads a .pgm mask file showing where a logo is and uses || a blur transform to remove the logo. ||________________________________________________________________________|*//** * \file vf_remove_logo.c * * \brief Advanced blur-based logo removing filter. * Hello and welcome. This code implements a filter to remove annoying TV * logos and other annoying images placed onto a video stream. It works by filling * in the pixels that comprise the logo with neighboring pixels. The transform is * very loosely based on a gaussian blur, but it is different enough to merit its * own paragraph later on. It is a major improvement on the old delogo filter as * it both uses a better blurring algorithm and uses a bitmap to use an arbitrary * and generally much tighter fitting shape than a rectangle. * * The filter requires 1 argument and has no optional arguments. It requires * a filter bitmap, which must be in PGM or PPM format. A sample invocation would * be -vf remove_logo=/home/username/logo_bitmaps/xyz.pgm. Pixels with a value of * zero are not part of the logo, and non-zero pixels are part of the logo. If you * use white (255) for the logo and black (0) for the rest, you will be safe. For * making the filter bitmap, I recommend taking a screen capture of a black frame * with the logo visible, and then using The GIMP's threshold filter followed by * the erode filter once or twice. If needed, little splotches can be fixed * manually. Remember that if logo pixels are not covered, the filter quality will * be much reduced. Marking too many pixels as part of the logo doesn't hurt as * much, but it will increase the amount of blurring needed to cover over the * image and will destroy more information than necessary. Additionally, this blur * algorithm is O(n) = n^4, where n is the width and height of a hypothetical * square logo, so extra pixels will slow things down on a large lo * * The logo removal algorithm has two key points. The first is that it * distinguishes between pixels in the logo and those not in the logo by using the * passed-in bitmap. Pixels not in the logo are copied over directly without being * modified and they also serve as source pixels for the logo fill-in. Pixels * inside the logo have the mask applied. * * At init-time the bitmap is reprocessed internally, and the distance to the * nearest edge of the logo (Manhattan distance), along with a little extra to * remove rough edges, is stored in each pixel. This is done using an in-place * erosion algorithm, and incrementing each pixel that survives any given erosion. * Once every pixel is eroded, the maximum value is recorded, and a set of masks * from size 0 to this size are generaged. The masks are circular binary masks, * where each pixel within a radius N (where N is the size of the mask) is a 1, * and all other pixels are a 0. Although a gaussian mask would be more * mathematically accurate, a binary mask works better in practice because we * generally do not use the central pixels in the mask (because they are in the * logo region), and thus a gaussian mask will cause too little blur and thus a * very unstable image. * * The mask is applied in a special way. Namely, only pixels in the mask that * line up to pixels outside the logo are used. The dynamic mask size means that * the mask is just big enough so that the edges touch pixels outside the logo, so * the blurring is kept to a minimum and at least the first boundary condition is * met (that the image function itself is continuous), even if the second boundary * condition (that the derivative of the image function is continuous) is not met. * A masking algorithm that does preserve the second boundary coundition * (perhaps something based on a highly-modified bi-cubic algorithm) should offer * even better results on paper, but the noise in a typical TV signal should make * anything based on derivatives hopelessly noisy. */#include <uclib.h>#include <uclib.h>#include <mplaylib.h>#include <ctype.h>#include <inttypes.h>#include "config.h"#include "mp_msg.h"#include "libvo/fastmemcpy.h"#include "img_format.h"#include "mp_image.h"#include "vf.h"//===========================================================================///** \brief Returns the larger of the two arguments. **/#define max(x,y) ((x)>(y)?(x):(y))/** \brief Returns the smaller of the two arguments. **/#define min(x,y) ((x)>(y)?(y):(x))/** * \brief Test if a pixel is part of the logo. */#define test_filter(image, x, y) ((unsigned char) (image->pixel[((y) * image->width) + (x)]))/** * \brief Chooses a slightly larger mask size to improve performance. * * This function maps the absolute minimum mask size needed to the mask size we'll * actually use. f(x) = x (the smallest that will work) will produce the sharpest * results, but will be quite jittery. f(x) = 1.25x (what I'm using) is a good * tradeoff in my opinion. This will calculate only at init-time, so you can put a * long expression here without effecting performance. */#define apply_mask_fudge_factor(x) (((x) >> 2) + x)/** * \brief Simple implementation of the PGM image format. * * This struct holds a bare-bones image loaded from a PGM or PPM file. Once * loaded and pre-processed, each pixel in this struct will contain how far from * the edge of the logo each pixel is, using the manhattan distance (|dx| + |dy|). * * pixels in char * pixel can be addressed using (y * width) + height. */typedef struct{ unsigned int width; unsigned int height; unsigned char * pixel;} pgm_structure;/** * \brief Stores persistant variables. * * Variables stored here are kept from frame to frame, and separate instances of * the filter will get their own separate copies. */typedef struct{ unsigned int fmt; /* Not exactly sure of the use for this. It came with the example filter I used as a basis for this, and it looks like a lot of stuff will break if I remove it. */ int max_mask_size; /* The largest possible mask size that will be needed with the given filter and corresponding half_size_filter. The half_size_filter can have a larger requirment in some rare (but not degenerate) cases. */ int * * * mask; /* Stores our collection of masks. The first * is for an array of masks, the second for the y axis, and the third for the x axis. */ pgm_structure * filter; /* Stores the full-size filter image. This is used to tell what pixels are in the logo or not in the luma plane. */ pgm_structure * half_size_filter; /* Stores a 50% width and 50% height filter image. This is used to tell what pixels are in the logo or not in the chroma planes. */ /* These 8 variables store the bounding rectangles that the logo resides in. */ int bounding_rectangle_posx1; int bounding_rectangle_posy1; int bounding_rectangle_posx2; int bounding_rectangle_posy2; int bounding_rectangle_half_size_posx1; int bounding_rectangle_half_size_posy1; int bounding_rectangle_half_size_posx2; int bounding_rectangle_half_size_posy2;} vf_priv_s;/** * \brief Mallocs memory and checks to make sure it succeeded. * * \param size How many bytes to allocate. * * \return A pointer to the freshly allocated memory block, or NULL on failutre. * * Mallocs memory, and checks to make sure it was successfully allocated. Because * of how MPlayer works, it cannot safely halt execution, but at least the user * will get an error message before the segfault happens. */static void * safe_malloc(int size){ void * answer = malloc(size); if (answer == NULL) mp_msg(MSGT_VFILTER, MSGL_ERR, "Unable to allocate memory in vf_remove_logo.c\n"); return answer;}/** * \brief Calculates the smallest rectangle that will encompass the logo region. * * \param filter This image contains the logo around which the rectangle will * will be fitted. * * The bounding rectangle is calculated by testing successive lines (from the four * sides of the rectangle) until no more can be removed without removing logo * pixels. The results are returned by reference to posx1, posy1, posx2, and * posy2. */static void calculate_bounding_rectangle(int * posx1, int * posy1, int * posx2, int * posy2, pgm_structure * filter){ int x; /* Temporary variables to run */ int y; /* through each row or column. */ int start_x; int start_y; int end_x = filter->width - 1; int end_y = filter->height - 1; int did_we_find_a_logo_pixel = 0; /* Let's find the top bound first. */ for (start_x = 0; start_x < filter->width && !did_we_find_a_logo_pixel; start_x++) { for (y = 0; y < filter->height; y++) { did_we_find_a_logo_pixel |= test_filter(filter, start_x, y); } } start_x--; /* Now the bottom bound. */ did_we_find_a_logo_pixel = 0; for (end_x = filter->width - 1; end_x > start_x && !did_we_find_a_logo_pixel; end_x--) { for (y = 0; y < filter->height; y++) { did_we_find_a_logo_pixel |= test_filter(filter, end_x, y); } } end_x++; /* Left bound. */ did_we_find_a_logo_pixel = 0; for (start_y = 0; start_y < filter->height && !did_we_find_a_logo_pixel; start_y++) { for (x = 0; x < filter->width; x++) { did_we_find_a_logo_pixel |= test_filter(filter, x, start_y); } } start_y--; /* Right bound. */ did_we_find_a_logo_pixel = 0; for (end_y = filter->height - 1; end_y > start_y && !did_we_find_a_logo_pixel; end_y--) { for (x = 0; x < filter->width; x++) { did_we_find_a_logo_pixel |= test_filter(filter, x, end_y); } } end_y++; *posx1 = start_x; *posy1 = start_y; *posx2 = end_x; *posy2 = end_y; return;}/** * \brief Free mask memory. * * \param vf Data structure which stores our persistant data, and is to be freed. * * We call this function when our filter is done. It will free the memory * allocated to the masks and leave the variables in a safe state. */static void destroy_masks(vf_instance_t * vf){ int a, b; /* Load values from the vf->priv struct for faster dereferencing. */ int * * * mask = ((vf_priv_s *)vf->priv)->mask; int max_mask_size = ((vf_priv_s *)vf->priv)->max_mask_size; if (mask == NULL) return; /* Nothing allocated, so return before we segfault. */ /* Free all allocated memory. */ for (a = 0; a <= max_mask_size; a++) /* Loop through each mask. */ { for (b = -a; b <= a; b++) /* Loop through each scanline in a mask. */ { free(mask[a][b + a]); /* Free a scanline. */ } free(mask[a]); /* Free a mask. */ } free(mask); /* Free the array of pointers pointing to the masks. */ /* Set the pointer to NULL, so that any duplicate calls to this function will not cause a crash. */ ((vf_priv_s *)vf->priv)->mask = NULL; return;}/** * \brief Set up our array of masks. * * \param vf Where our filter stores persistance data, like these masks. * * This creates an array of progressively larger masks and calculates their * values. The values will not change during program execution once this function * is done. */static void initialize_masks(vf_instance_t * vf){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?