• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavfilter/libmpcodecs/vf_remove_logo.c

Go to the documentation of this file.
00001 /*
00002  * This filter loads a .pgm mask file showing where a logo is and uses
00003  * a blur transform to remove the logo.
00004  *
00005  * Copyright (C) 2005 Robert Edele <yartrebo@earthlink.net>
00006  *
00007  * This file is part of MPlayer.
00008  *
00009  * MPlayer is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * MPlayer is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License along
00020  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
00021  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00022  */
00023 
00083 #include <stdio.h>
00084 #include <stdlib.h>
00085 #include <string.h>
00086 #include <ctype.h>
00087 #include <inttypes.h>
00088 
00089 #include "config.h"
00090 #include "mp_msg.h"
00091 #include "libvo/fastmemcpy.h"
00092 
00093 #include "img_format.h"
00094 #include "mp_image.h"
00095 #include "vf.h"
00096 
00097 //===========================================================================//
00098 
00100 #define max(x,y) ((x)>(y)?(x):(y))
00101 
00102 #define min(x,y) ((x)>(y)?(y):(x))
00103 
00107 #define test_filter(image, x, y) ((unsigned char) (image->pixel[((y) * image->width) + (x)]))
00108 
00118 #define apply_mask_fudge_factor(x) (((x) >> 2) + x)
00119 
00129 typedef struct
00130 {
00131   unsigned int width;
00132   unsigned int height;
00133 
00134   unsigned char * pixel;
00135 
00136 } pgm_structure;
00137 
00144 struct vf_priv_s
00145 {
00146   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. */
00147   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. */
00148   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. */
00149   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. */
00150   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. */
00151   /* These 8 variables store the bounding rectangles that the logo resides in. */
00152   int bounding_rectangle_posx1;
00153   int bounding_rectangle_posy1;
00154   int bounding_rectangle_posx2;
00155   int bounding_rectangle_posy2;
00156   int bounding_rectangle_half_size_posx1;
00157   int bounding_rectangle_half_size_posy1;
00158   int bounding_rectangle_half_size_posx2;
00159   int bounding_rectangle_half_size_posy2;
00160 } vf_priv_s;
00161 
00173 static void * safe_malloc(int size)
00174 {
00175   void * answer = malloc(size);
00176   if (answer == NULL)
00177     mp_msg(MSGT_VFILTER, MSGL_ERR, "Unable to allocate memory in vf_remove_logo.c\n");
00178 
00179   return answer;
00180 }
00181 
00193 static void calculate_bounding_rectangle(int * posx1, int * posy1, int * posx2, int * posy2, pgm_structure * filter)
00194 {
00195   int x; /* Temporary variables to run  */
00196   int y; /* through each row or column. */
00197   int start_x;
00198   int start_y;
00199   int end_x = filter->width - 1;
00200   int end_y = filter->height - 1;
00201   int did_we_find_a_logo_pixel = 0;
00202 
00203   /* Let's find the top bound first. */
00204   for (start_x = 0; start_x < filter->width && !did_we_find_a_logo_pixel; start_x++)
00205   {
00206     for (y = 0; y < filter->height; y++)
00207     {
00208       did_we_find_a_logo_pixel |= test_filter(filter, start_x, y);
00209     }
00210   }
00211   start_x--;
00212 
00213   /* Now the bottom bound. */
00214   did_we_find_a_logo_pixel = 0;
00215   for (end_x = filter->width - 1; end_x > start_x && !did_we_find_a_logo_pixel; end_x--)
00216   {
00217     for (y = 0; y < filter->height; y++)
00218     {
00219       did_we_find_a_logo_pixel |= test_filter(filter, end_x, y);
00220     }
00221   }
00222   end_x++;
00223 
00224   /* Left bound. */
00225   did_we_find_a_logo_pixel = 0;
00226   for (start_y = 0; start_y < filter->height && !did_we_find_a_logo_pixel; start_y++)
00227   {
00228     for (x = 0; x < filter->width; x++)
00229     {
00230       did_we_find_a_logo_pixel |= test_filter(filter, x, start_y);
00231     }
00232   }
00233   start_y--;
00234 
00235   /* Right bound. */
00236   did_we_find_a_logo_pixel = 0;
00237   for (end_y = filter->height - 1; end_y > start_y && !did_we_find_a_logo_pixel; end_y--)
00238   {
00239     for (x = 0; x < filter->width; x++)
00240     {
00241       did_we_find_a_logo_pixel |= test_filter(filter, x, end_y);
00242     }
00243   }
00244   end_y++;
00245 
00246   *posx1 = start_x;
00247   *posy1 = start_y;
00248   *posx2 = end_x;
00249   *posy2 = end_y;
00250 
00251   return;
00252 }
00253 
00262 static void destroy_masks(vf_instance_t * vf)
00263 {
00264   int a, b;
00265 
00266   /* Load values from the vf->priv struct for faster dereferencing. */
00267   int * * * mask = vf->priv->mask;
00268   int max_mask_size = vf->priv->max_mask_size;
00269 
00270   if (mask == NULL)
00271     return; /* Nothing allocated, so return before we segfault. */
00272 
00273   /* Free all allocated memory. */
00274   for (a = 0; a <= max_mask_size; a++) /* Loop through each mask. */
00275   {
00276     for (b = -a; b <= a; b++) /* Loop through each scanline in a mask. */
00277     {
00278       free(mask[a][b + a]); /* Free a scanline. */
00279     }
00280     free(mask[a]); /* Free a mask. */
00281   }
00282   free(mask); /* Free the array of pointers pointing to the masks. */
00283 
00284   /* Set the pointer to NULL, so that any duplicate calls to this function will not cause a crash. */
00285   vf->priv->mask = NULL;
00286 
00287   return;
00288 }
00289 
00299 static void initialize_masks(vf_instance_t * vf)
00300 {
00301   int a, b, c;
00302 
00303   /* Load values from the vf->priv struct for faster dereferencing. */
00304   int * * * mask = vf->priv->mask;
00305   int max_mask_size = vf->priv->max_mask_size; /* This tells us how many masks we'll need to generate. */
00306 
00307   /* Create a circular mask for each size up to max_mask_size. When the filter is applied, the mask size is
00308      determined on a pixel by pixel basis, with pixels nearer the edge of the logo getting smaller mask sizes. */
00309   mask = (int * * *) safe_malloc(sizeof(int * *) * (max_mask_size + 1));
00310   for (a = 0; a <= max_mask_size; a++)
00311   {
00312     mask[a] = (int * *) safe_malloc(sizeof(int *) * ((a * 2) + 1));
00313     for (b = -a; b <= a; b++)
00314     {
00315       mask[a][b + a] = (int *) safe_malloc(sizeof(int) * ((a * 2) + 1));
00316       for (c = -a; c <= a; c++)
00317       {
00318         if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */
00319           mask[a][b + a][c + a] = 1;
00320         else
00321           mask[a][b + a][c + a] = 0;
00322       }
00323     }
00324   }
00325 
00326   /* Store values back to vf->priv so they aren't lost after the function returns. */
00327   vf->priv->mask = mask;
00328 
00329   return;
00330 }
00331 
00347 static void convert_mask_to_strength_mask(vf_instance_t * vf, pgm_structure * mask)
00348 {
00349   int x, y; /* Used by our for loops to go through every single pixel in the picture one at a time. */
00350   int has_anything_changed = 1; /* Used by the main while() loop to know if anything changed on the last erosion. */
00351   int current_pass = 0; /* How many times we've gone through the loop. Used in the in-place erosion algorithm
00352                            and to get us max_mask_size later on. */
00353   int max_mask_size; /* This will record how large a mask the pixel that is the furthest from the edge of the logo
00354                            (and thus the neediest) is. */
00355   char * current_pixel = mask->pixel; /* This stores the actual pixel data. */
00356 
00357   /* First pass, set all non-zero values to 1. After this loop finishes, the data should be considered numeric
00358      data for the filter, not color data. */
00359   for (x = 0; x < mask->height * mask->width; x++, current_pixel++)
00360     if(*current_pixel) *current_pixel = 1;
00361 
00362   /* Second pass and future passes. For each pass, if a pixel is itself the same value as the current pass,
00363      and its four neighbors are too, then it is incremented. If no pixels are incremented by the end of the pass,
00364      then we go again. Edge pixels are counted as always excluded (this should be true anyway for any sane mask,
00365      but if it isn't this will ensure that we eventually exit). */
00366   while (has_anything_changed)
00367   {
00368     current_pass++;
00369     current_pixel = mask->pixel;
00370 
00371     has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */
00372 
00373     for (y = 1; y < mask->height - 1; y++)
00374     {
00375       for (x = 1; x < mask->width - 1; x++)
00376       {
00377         /* Apply the in-place erosion transform. It is based on the following two premises: 1 - Any pixel that fails 1 erosion
00378            will fail all future erosions. 2 - Only pixels having survived all erosions up to the present will be >= to
00379            current_pass. It doesn't matter if it survived the current pass, failed it, or hasn't been tested yet. */
00380         if (*current_pixel >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */
00381             *(current_pixel + 1) >= current_pass &&
00382             *(current_pixel - 1) >= current_pass &&
00383             *(current_pixel + mask->width) >= current_pass &&
00384             *(current_pixel - mask->width) >= current_pass)
00385          {
00386            (*current_pixel)++; /* Increment the value since it still has not been eroded, as evidenced by the if statement
00387                                   that just evaluated to true. */
00388            has_anything_changed = 1;
00389          }
00390         current_pixel++;
00391       }
00392     }
00393   }
00394 
00395   /* Apply the fudge factor, which will increase the size of the mask a little to reduce jitter at the cost of more blur. */
00396   for (y = 1; y < mask->height - 1; y++)
00397   {
00398    for (x = 1; x < mask->width - 1; x++)
00399     {
00400       mask->pixel[(y * mask->width) + x] = apply_mask_fudge_factor(mask->pixel[(y * mask->width) + x]);
00401     }
00402   }
00403 
00404   max_mask_size = current_pass + 1; /* As a side-effect, we now know the maximum mask size, which we'll use to generate our masks. */
00405   max_mask_size = apply_mask_fudge_factor(max_mask_size); /* Apply the fudge factor to this number too, since we must
00406                                                              ensure that enough masks are generated. */
00407   vf->priv->max_mask_size = max_mask_size; /* Commit the newly calculated max_mask_size to the vf->priv struct. */
00408 
00409   return;
00410 }
00411 
00428 static void get_blur(const vf_instance_t * const vf, unsigned int * const value_out, const pgm_structure * const logo_mask,
00429               const mp_image_t * const image, const int x, const int y, const int plane)
00430 {
00431   int mask_size; /* Mask size tells how large a circle to use. The radius is about (slightly larger than) mask size. */
00432   /* Get values from vf->priv for faster dereferencing. */
00433   int * * * mask = vf->priv->mask;
00434 
00435   int start_posx, start_posy, end_posx, end_posy;
00436   int i, j;
00437   unsigned int accumulator = 0, divisor = 0;
00438   const unsigned char * mask_read_position; /* What pixel we are reading out of the circular blur mask. */
00439   const unsigned char * logo_mask_read_position; /* What pixel we are reading out of the filter image. */
00440 
00441   /* Prepare our bounding rectangle and clip it if need be. */
00442   mask_size = test_filter(logo_mask, x, y);
00443   start_posx = max(0, x - mask_size);
00444   start_posy = max(0, y - mask_size);
00445   end_posx = min(image->width - 1, x + mask_size);
00446   end_posy = min(image->height - 1, y + mask_size);
00447 
00448   mask_read_position = image->planes[plane] + (image->stride[plane] * start_posy) + start_posx;
00449   logo_mask_read_position = logo_mask->pixel + (start_posy * logo_mask->width) + start_posx;
00450 
00451   for (j = start_posy; j <= end_posy; j++)
00452   {
00453     for (i = start_posx; i <= end_posx; i++)
00454     {
00455       if (!(*logo_mask_read_position) && mask[mask_size][i - start_posx][j - start_posy])
00456       { /* Check to see if this pixel is in the logo or not. Only use the pixel if it is not. */
00457         accumulator += *mask_read_position;
00458         divisor++;
00459       }
00460 
00461       mask_read_position++;
00462       logo_mask_read_position++;
00463     }
00464 
00465     mask_read_position += (image->stride[plane] - ((end_posx + 1) - start_posx));
00466     logo_mask_read_position += (logo_mask->width - ((end_posx + 1) - start_posx));
00467   }
00468 
00469   if (divisor == 0) /* This means that not a single pixel is outside of the logo, so we have no data. */
00470   { /* We should put some eye catching value here, to indicate the flaw to the user. */
00471     *value_out = 255;
00472   }
00473   else /* Else we need to normalise the data using the divisor. */
00474   {
00475     *value_out = (accumulator + (divisor / 2)) / divisor; /* Divide, taking into account average rounding error. */
00476   }
00477 
00478   return;
00479 }
00480 
00484 static void destroy_pgm(pgm_structure * to_be_destroyed)
00485 {
00486   if (to_be_destroyed == NULL)
00487     return; /* Don't do anything if a NULL pointer was passed it. */
00488 
00489   /* Internally allocated memory. */
00490   if (to_be_destroyed->pixel != NULL)
00491   {
00492     free(to_be_destroyed->pixel);
00493     to_be_destroyed->pixel = NULL;
00494   }
00495 
00496   /* Free the actual struct instance. This is done here and not by the calling function. */
00497   free(to_be_destroyed);
00498 }
00499 
00501 static void load_pgm_skip(FILE *f) {
00502   int c, comment = 0;
00503   do {
00504     c = fgetc(f);
00505     if (c == '#')
00506       comment = 1;
00507     if (c == '\n')
00508       comment = 0;
00509   } while (c != EOF && (isspace(c) || comment));
00510   ungetc(c, f);
00511 }
00512 
00513 #define REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE(message) {mp_msg(MSGT_VFILTER, MSGL_ERR, message); return NULL;}
00514 
00531 static pgm_structure * load_pgm(const char * file_name)
00532 {
00533   int maximum_greyscale_value;
00534   FILE * input;
00535   int pnm_number;
00536   pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));
00537   char * write_position;
00538   char * end_position;
00539   int image_size; /* width * height */
00540 
00541   if((input = fopen(file_name, "rb")) == NULL) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Unable to open file. File not found or insufficient permissions.\n");
00542 
00543   /* Parse the PGM header. */
00544   if (fgetc(input) != 'P') REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: File is not a valid PGM or PPM file.\n");
00545   pnm_number = fgetc(input) - '0';
00546   if (pnm_number != 5 && pnm_number != 6) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PNM file. Only raw PGM (Portable Gray Map) and raw PPM (Portable Pixel Map) subtypes are allowed.\n");
00547   load_pgm_skip(input);
00548   if (fscanf(input, "%i", &(new_pgm->width)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
00549   load_pgm_skip(input);
00550   if (fscanf(input, "%i", &(new_pgm->height)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
00551   load_pgm_skip(input);
00552   if (fscanf(input, "%i", &maximum_greyscale_value) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
00553   if (maximum_greyscale_value >= 256) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove_logo: Only 1 byte per pixel (pgm) or 1 byte per color value (ppm) are supported.\n");
00554   load_pgm_skip(input);
00555 
00556   new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);
00557 
00558   /* Load the pixels. */
00559   /* Note: I am aware that fgetc(input) isn't the fastest way of doing things, but it is quite compact and the code only runs once when the filter is initialized.*/
00560   image_size = new_pgm->width * new_pgm->height;
00561   end_position = new_pgm->pixel + image_size;
00562   for (write_position = new_pgm->pixel; write_position < end_position; write_position++)
00563   {
00564     *write_position = fgetc(input);
00565     if (pnm_number == 6) /* This tests to see if the file is a PPM file. */
00566     { /* If it is, then consider the pixel set if any of the three color channels are set. Since we just care about == 0 or != 0, a bitwise or will do the trick. */
00567       *write_position |= fgetc(input);
00568       *write_position |= fgetc(input);
00569     }
00570   }
00571 
00572   return new_pgm;
00573 }
00574 
00592 static pgm_structure * generate_half_size_image(vf_instance_t * vf, pgm_structure * input_image)
00593 {
00594   int x, y;
00595   pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));
00596   int has_anything_changed = 1;
00597   int current_pass;
00598   int max_mask_size;
00599   char * current_pixel;
00600 
00601   new_pgm->width = input_image->width / 2;
00602   new_pgm->height = input_image->height / 2;
00603   new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);
00604 
00605   /* Copy over the image data, using the average of 4 pixels for to calculate each downsampled pixel. */
00606   for (y = 0; y < new_pgm->height; y++)
00607     for (x = 0; x < new_pgm->width; x++)
00608     {
00609       /* Set the pixel if there exists a non-zero value in the source pixels, else clear it. */
00610       new_pgm->pixel[(y * new_pgm->width) + x] = input_image->pixel[((y << 1) * input_image->width) + (x << 1)] ||
00611                                                  input_image->pixel[((y << 1) * input_image->width) + (x << 1) + 1] ||
00612                                                  input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1)] ||
00613                                                  input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1) + 1];
00614       new_pgm->pixel[(y * new_pgm->width) + x] = min(1, new_pgm->pixel[(y * new_pgm->width) + x]);
00615     }
00616 
00617   /* Now we need to recalculate the numbers for the smaller size. Just using the old_value / 2 can cause subtle
00618      and fairly rare, but very nasty, bugs. */
00619 
00620   current_pixel = new_pgm->pixel;
00621   /* First pass, set all non-zero values to 1. */
00622   for (x = 0; x < new_pgm->height * new_pgm->width; x++, current_pixel++)
00623     if(*current_pixel) *current_pixel = 1;
00624 
00625   /* Second pass and future passes. For each pass, if a pixel is itself the same value as the current pass,
00626      and its four neighbors are too, then it is incremented. If no pixels are incremented by the end of the pass,
00627      then we go again. Edge pixels are counted as always excluded (this should be true anyway for any sane mask,
00628      but if it isn't this will ensure that we eventually exit). */
00629   current_pass = 0;
00630   while (has_anything_changed)
00631   {
00632     current_pass++;
00633 
00634     has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */
00635 
00636     for (y = 1; y < new_pgm->height - 1; y++)
00637     {
00638       for (x = 1; x < new_pgm->width - 1; x++)
00639       {
00640         if (new_pgm->pixel[(y * new_pgm->width) + x] >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */
00641             new_pgm->pixel[(y * new_pgm->width) + (x + 1)] >= current_pass &&
00642             new_pgm->pixel[(y * new_pgm->width) + (x - 1)] >= current_pass &&
00643             new_pgm->pixel[((y + 1) * new_pgm->width) + x] >= current_pass &&
00644             new_pgm->pixel[((y - 1) * new_pgm->width) + x] >= current_pass)
00645          {
00646            new_pgm->pixel[(y * new_pgm->width) + x]++; /* Increment the value since it still has not been eroded,
00647                                                     as evidenced by the if statement that just evaluated to true. */
00648            has_anything_changed = 1;
00649          }
00650       }
00651     }
00652   }
00653 
00654   for (y = 1; y < new_pgm->height - 1; y++)
00655   {
00656    for (x = 1; x < new_pgm->width - 1; x++)
00657     {
00658       new_pgm->pixel[(y * new_pgm->width) + x] = apply_mask_fudge_factor(new_pgm->pixel[(y * new_pgm->width) + x]);
00659     }
00660   }
00661 
00662   max_mask_size = current_pass + 1; /* As a side-effect, we now know the maximum mask size, which we'll use to generate our masks. */
00663   max_mask_size = apply_mask_fudge_factor(max_mask_size);
00664   /* Commit the newly calculated max_mask_size to the vf->priv struct. */
00665   vf->priv->max_mask_size = max(max_mask_size, vf->priv->max_mask_size);
00666 
00667   return new_pgm;
00668 }
00669 
00673 static unsigned int find_best(struct vf_instance *vf){
00674   int is_format_okay = vf_next_query_format(vf, IMGFMT_YV12);
00675   if ((is_format_okay & VFCAP_CSP_SUPPORTED_BY_HW) || (is_format_okay & VFCAP_CSP_SUPPORTED))
00676     return IMGFMT_YV12;
00677   else
00678     return 0;
00679 }
00680 
00681 //===========================================================================//
00682 
00686 static int config(struct vf_instance *vf, int width, int height, int d_width, int d_height, unsigned int flags, unsigned int outfmt)
00687 {
00688   if(!(vf->priv->fmt=find_best(vf)))
00689     return 0;
00690   else
00691     return vf_next_config(vf,width,height,d_width,d_height,flags,vf->priv->fmt);
00692 }
00693 
00718 static void convert_yv12(const vf_instance_t * const vf, const char * const source, const int source_stride,
00719                          const mp_image_t * const source_image, const int width, const int height,
00720                          char * const destination, const int destination_stride, int is_image_direct, pgm_structure * filter,
00721                          const int plane, const int logo_start_x, const int logo_start_y, const int logo_end_x, const int logo_end_y)
00722 {
00723   int y;
00724   int x;
00725 
00726   /* These pointers point to where we are getting our pixel data (inside mpi) and where we are storing it (inside dmpi). */
00727   const unsigned char * source_line;
00728   unsigned char * destination_line;
00729 
00730   if (!is_image_direct)
00731     memcpy_pic(destination, source, width, height, destination_stride, source_stride);
00732 
00733   for (y = logo_start_y; y <= logo_end_y; y++)
00734   {
00735     source_line = (const unsigned char *) source + (source_stride * y);
00736     destination_line = (unsigned char *) destination + (destination_stride * y);
00737 
00738     for (x = logo_start_x; x <= logo_end_x; x++)
00739     {
00740       unsigned int output;
00741 
00742       if (filter->pixel[(y * filter->width) + x]) /* Only process if we are in the logo. */
00743       {
00744         get_blur(vf, &output, filter, source_image, x, y, plane);
00745         destination_line[x] = output;
00746       }
00747       else /* Else just copy the data. */
00748         if (!is_image_direct)
00749           destination_line[x] = source_line[x];
00750     }
00751   }
00752 }
00753 
00767 static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
00768     mp_image_t *dmpi;
00769 
00770     dmpi=vf_get_image(vf->next,vf->priv->fmt,
00771         MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
00772         mpi->w, mpi->h);
00773 
00774     /* Check to make sure that the filter image and the video stream are the same size. */
00775     if (vf->priv->filter->width != mpi->w || vf->priv->filter->height != mpi->h)
00776     {
00777       mp_msg(MSGT_VFILTER,MSGL_ERR, "Filter image and video stream are not of the same size. (Filter: %d x %d, Stream: %d x %d)\n",
00778              vf->priv->filter->width, vf->priv->filter->height, mpi->w, mpi->h);
00779       return 0;
00780     }
00781 
00782     switch(dmpi->imgfmt){
00783     case IMGFMT_YV12:
00784           convert_yv12(vf, mpi->planes[0],  mpi->stride[0], mpi, mpi->w, mpi->h,
00785                           dmpi->planes[0], dmpi->stride[0],
00786                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->filter, 0,
00787                           vf->priv->bounding_rectangle_posx1, vf->priv->bounding_rectangle_posy1,
00788                           vf->priv->bounding_rectangle_posx2, vf->priv->bounding_rectangle_posy2);
00789           convert_yv12(vf, mpi->planes[1],  mpi->stride[1], mpi, mpi->w / 2, mpi->h / 2,
00790                           dmpi->planes[1], dmpi->stride[1],
00791                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->half_size_filter, 1,
00792                           vf->priv->bounding_rectangle_half_size_posx1, vf->priv->bounding_rectangle_half_size_posy1,
00793                           vf->priv->bounding_rectangle_half_size_posx2, vf->priv->bounding_rectangle_half_size_posy2);
00794           convert_yv12(vf, mpi->planes[2],  mpi->stride[2], mpi, mpi->w / 2, mpi->h / 2,
00795                           dmpi->planes[2], dmpi->stride[2],
00796                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->half_size_filter, 2,
00797                           vf->priv->bounding_rectangle_half_size_posx1, vf->priv->bounding_rectangle_half_size_posy1,
00798                           vf->priv->bounding_rectangle_half_size_posx2, vf->priv->bounding_rectangle_half_size_posy2);
00799           break;
00800 
00801     default:
00802         mp_msg(MSGT_VFILTER,MSGL_ERR,"Unhandled format: 0x%X\n",dmpi->imgfmt);
00803         return 0;
00804     }
00805 
00806     return vf_next_put_image(vf,dmpi, pts);
00807 }
00808 
00809 //===========================================================================//
00810 
00814 static int query_format(struct vf_instance *vf, unsigned int fmt)
00815 {
00816   if (fmt == IMGFMT_YV12)
00817     return vf_next_query_format(vf, IMGFMT_YV12);
00818   else
00819     return 0;
00820 }
00821 
00827 static void uninit(vf_instance_t *vf)
00828 {
00829   /* Destroy our masks and images. */
00830   destroy_pgm(vf->priv->filter);
00831   destroy_pgm(vf->priv->half_size_filter);
00832   destroy_masks(vf);
00833 
00834   /* Destroy our private structure that had been used to store those masks and images. */
00835   free(vf->priv);
00836 
00837   return;
00838 }
00839 
00849 static int vf_open(vf_instance_t *vf, char *args)
00850 {
00851   vf->priv = safe_malloc(sizeof(vf_priv_s));
00852   vf->uninit = uninit;
00853 
00854   /* Load our filter image. */
00855   if (args)
00856     vf->priv->filter = load_pgm(args);
00857   else
00858   {
00859     mp_msg(MSGT_VFILTER, MSGL_ERR, "[vf]remove_logo usage: remove_logo=/path/to/filter_image_file.pgm\n");
00860     free(vf->priv);
00861     return 0;
00862   }
00863 
00864   if (vf->priv->filter == NULL)
00865   {
00866     /* Error message was displayed by load_pgm(). */
00867     free(vf->priv);
00868     return 0;
00869   }
00870 
00871   /* Create the scaled down filter image for the chroma planes. */
00872   convert_mask_to_strength_mask(vf, vf->priv->filter);
00873   vf->priv->half_size_filter = generate_half_size_image(vf, vf->priv->filter);
00874 
00875   /* Now that we know how many masks we need (the info is in vf), we can generate the masks. */
00876   initialize_masks(vf);
00877 
00878   /* Calculate our bounding rectangles, which determine in what region the logo resides for faster processing. */
00879   calculate_bounding_rectangle(&vf->priv->bounding_rectangle_posx1, &vf->priv->bounding_rectangle_posy1,
00880                                &vf->priv->bounding_rectangle_posx2, &vf->priv->bounding_rectangle_posy2,
00881                                 vf->priv->filter);
00882   calculate_bounding_rectangle(&vf->priv->bounding_rectangle_half_size_posx1,
00883                                &vf->priv->bounding_rectangle_half_size_posy1,
00884                                &vf->priv->bounding_rectangle_half_size_posx2,
00885                                &vf->priv->bounding_rectangle_half_size_posy2,
00886                                 vf->priv->half_size_filter);
00887 
00888   vf->config=config;
00889   vf->put_image=put_image;
00890   vf->query_format=query_format;
00891   return 1;
00892 }
00893 
00897 const vf_info_t vf_info_remove_logo = {
00898     "Removes a tv logo based on a mask image.",
00899     "remove-logo",
00900     "Robert Edele",
00901     "",
00902     vf_open,
00903     NULL
00904 };
00905 
00906 //===========================================================================//

Generated on Fri Feb 22 2013 07:24:30 for FFmpeg by  doxygen 1.7.1