]> git.sesse.net Git - ffmpeg/blob - libavfilter/libmpcodecs/vf_remove_logo.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavfilter / libmpcodecs / vf_remove_logo.c
1 /*
2  * This filter loads a .pgm mask file showing where a logo is and uses
3  * a blur transform to remove the logo.
4  *
5  * Copyright (C) 2005 Robert Edele <yartrebo@earthlink.net>
6  *
7  * This file is part of MPlayer.
8  *
9  * MPlayer is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * MPlayer is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 /**
25  * \file
26  *
27  * \brief Advanced blur-based logo removing filter.
28
29  *     Hello and welcome. This code implements a filter to remove annoying TV
30  * logos and other annoying images placed onto a video stream. It works by filling
31  * in the pixels that comprise the logo with neighboring pixels. The transform is
32  * very loosely based on a gaussian blur, but it is different enough to merit its
33  * own paragraph later on. It is a major improvement on the old delogo filter as
34  * it both uses a better blurring algorithm and uses a bitmap to use an arbitrary
35  * and generally much tighter fitting shape than a rectangle.
36  *
37  *     The filter requires 1 argument and has no optional arguments. It requires
38  * a filter bitmap, which must be in PGM or PPM format. A sample invocation would
39  * be -vf remove_logo=/home/username/logo_bitmaps/xyz.pgm.  Pixels with a value of
40  * zero are not part of the logo, and non-zero pixels are part of the logo. If you
41  * use white (255) for the logo and black (0) for the rest, you will be safe. For
42  * making the filter bitmap, I recommend taking a screen capture of a black frame
43  * with the logo visible, and then using The GIMP's threshold filter followed by
44  * the erode filter once or twice. If needed, little splotches can be fixed
45  * manually. Remember that if logo pixels are not covered, the filter quality will
46  * be much reduced. Marking too many pixels as part of the logo doesn't hurt as
47  * much, but it will increase the amount of blurring needed to cover over the
48  * image and will destroy more information than necessary. Additionally, this blur
49  * algorithm is O(n) = n^4, where n is the width and height of a hypothetical
50  * square logo, so extra pixels will slow things down on a large lo
51  *
52  *     The logo removal algorithm has two key points. The first is that it
53  * distinguishes between pixels in the logo and those not in the logo by using the
54  * passed-in bitmap. Pixels not in the logo are copied over directly without being
55  * modified and they also serve as source pixels for the logo fill-in. Pixels
56  * inside the logo have the mask applied.
57  *
58  *     At init-time the bitmap is reprocessed internally, and the distance to the
59  * nearest edge of the logo (Manhattan distance), along with a little extra to
60  * remove rough edges, is stored in each pixel. This is done using an in-place
61  * erosion algorithm, and incrementing each pixel that survives any given erosion.
62  * Once every pixel is eroded, the maximum value is recorded, and a set of masks
63  * from size 0 to this size are generaged. The masks are circular binary masks,
64  * where each pixel within a radius N (where N is the size of the mask) is a 1,
65  * and all other pixels are a 0. Although a gaussian mask would be more
66  * mathematically accurate, a binary mask works better in practice because we
67  * generally do not use the central pixels in the mask (because they are in the
68  * logo region), and thus a gaussian mask will cause too little blur and thus a
69  * very unstable image.
70  *
71  *     The mask is applied in a special way. Namely, only pixels in the mask that
72  * line up to pixels outside the logo are used. The dynamic mask size means that
73  * the mask is just big enough so that the edges touch pixels outside the logo, so
74  * the blurring is kept to a minimum and at least the first boundary condition is
75  * met (that the image function itself is continuous), even if the second boundary
76  * condition (that the derivative of the image function is continuous) is not met.
77  * A masking algorithm that does preserve the second boundary coundition
78  * (perhaps something based on a highly-modified bi-cubic algorithm) should offer
79  * even better results on paper, but the noise in a typical TV signal should make
80  * anything based on derivatives hopelessly noisy.
81  */
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <ctype.h>
87 #include <inttypes.h>
88
89 #include "config.h"
90 #include "mp_msg.h"
91 #include "libvo/fastmemcpy.h"
92
93 #include "img_format.h"
94 #include "mp_image.h"
95 #include "vf.h"
96
97 //===========================================================================//
98
99 /** \brief Returns the larger of the two arguments. **/
100 #define max(x,y) ((x)>(y)?(x):(y))
101 /** \brief Returns the smaller of the two arguments. **/
102 #define min(x,y) ((x)>(y)?(y):(x))
103
104 /**
105  * \brief Test if a pixel is part of the logo.
106  */
107 #define test_filter(image, x, y) ((unsigned char) (image->pixel[((y) * image->width) + (x)]))
108
109 /**
110  * \brief Chooses a slightly larger mask size to improve performance.
111  *
112  * This function maps the absolute minimum mask size needed to the mask size we'll
113  * actually use. f(x) = x (the smallest that will work) will produce the sharpest
114  * results, but will be quite jittery. f(x) = 1.25x (what I'm using) is a good
115  * tradeoff in my opinion. This will calculate only at init-time, so you can put a
116  * long expression here without effecting performance.
117  */
118 #define apply_mask_fudge_factor(x) (((x) >> 2) + x)
119
120 /**
121  * \brief Simple implementation of the PGM image format.
122  *
123  * This struct holds a bare-bones image loaded from a PGM or PPM file. Once
124  * loaded and pre-processed, each pixel in this struct will contain how far from
125  * the edge of the logo each pixel is, using the manhattan distance (|dx| + |dy|).
126  *
127  * pixels in char * pixel can be addressed using (y * width) + height.
128  */
129 typedef struct
130 {
131   unsigned int width;
132   unsigned int height;
133
134   unsigned char * pixel;
135
136 } pgm_structure;
137
138 /**
139  * \brief Stores persistant variables.
140  *
141  * Variables stored here are kept from frame to frame, and separate instances of
142  * the filter will get their own separate copies.
143  */
144 struct vf_priv_s
145 {
146   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. */
147   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. */
148   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. */
149   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. */
150   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. */
151   /* These 8 variables store the bounding rectangles that the logo resides in. */
152   int bounding_rectangle_posx1;
153   int bounding_rectangle_posy1;
154   int bounding_rectangle_posx2;
155   int bounding_rectangle_posy2;
156   int bounding_rectangle_half_size_posx1;
157   int bounding_rectangle_half_size_posy1;
158   int bounding_rectangle_half_size_posx2;
159   int bounding_rectangle_half_size_posy2;
160 } vf_priv_s;
161
162 /**
163  * \brief Mallocs memory and checks to make sure it succeeded.
164  *
165  * \param size How many bytes to allocate.
166  *
167  * \return A pointer to the freshly allocated memory block, or NULL on failutre.
168  *
169  * Mallocs memory, and checks to make sure it was successfully allocated. Because
170  * of how MPlayer works, it cannot safely halt execution, but at least the user
171  * will get an error message before the segfault happens.
172  */
173 static void * safe_malloc(int size)
174 {
175   void * answer = malloc(size);
176   if (answer == NULL)
177     mp_msg(MSGT_VFILTER, MSGL_ERR, "Unable to allocate memory in vf_remove_logo.c\n");
178
179   return answer;
180 }
181
182 /**
183  * \brief Calculates the smallest rectangle that will encompass the logo region.
184  *
185  * \param filter This image contains the logo around which the rectangle will
186  *        will be fitted.
187  *
188  * The bounding rectangle is calculated by testing successive lines (from the four
189  * sides of the rectangle) until no more can be removed without removing logo
190  * pixels. The results are returned by reference to posx1, posy1, posx2, and
191  * posy2.
192  */
193 static void calculate_bounding_rectangle(int * posx1, int * posy1, int * posx2, int * posy2, pgm_structure * filter)
194 {
195   int x; /* Temporary variables to run  */
196   int y; /* through each row or column. */
197   int start_x;
198   int start_y;
199   int end_x = filter->width - 1;
200   int end_y = filter->height - 1;
201   int did_we_find_a_logo_pixel = 0;
202
203   /* Let's find the top bound first. */
204   for (start_x = 0; start_x < filter->width && !did_we_find_a_logo_pixel; start_x++)
205   {
206     for (y = 0; y < filter->height; y++)
207     {
208       did_we_find_a_logo_pixel |= test_filter(filter, start_x, y);
209     }
210   }
211   start_x--;
212
213   /* Now the bottom bound. */
214   did_we_find_a_logo_pixel = 0;
215   for (end_x = filter->width - 1; end_x > start_x && !did_we_find_a_logo_pixel; end_x--)
216   {
217     for (y = 0; y < filter->height; y++)
218     {
219       did_we_find_a_logo_pixel |= test_filter(filter, end_x, y);
220     }
221   }
222   end_x++;
223
224   /* Left bound. */
225   did_we_find_a_logo_pixel = 0;
226   for (start_y = 0; start_y < filter->height && !did_we_find_a_logo_pixel; start_y++)
227   {
228     for (x = 0; x < filter->width; x++)
229     {
230       did_we_find_a_logo_pixel |= test_filter(filter, x, start_y);
231     }
232   }
233   start_y--;
234
235   /* Right bound. */
236   did_we_find_a_logo_pixel = 0;
237   for (end_y = filter->height - 1; end_y > start_y && !did_we_find_a_logo_pixel; end_y--)
238   {
239     for (x = 0; x < filter->width; x++)
240     {
241       did_we_find_a_logo_pixel |= test_filter(filter, x, end_y);
242     }
243   }
244   end_y++;
245
246   *posx1 = start_x;
247   *posy1 = start_y;
248   *posx2 = end_x;
249   *posy2 = end_y;
250
251   return;
252 }
253
254 /**
255  * \brief Free mask memory.
256  *
257  * \param vf Data structure which stores our persistant data, and is to be freed.
258  *
259  * We call this function when our filter is done. It will free the memory
260  * allocated to the masks and leave the variables in a safe state.
261  */
262 static void destroy_masks(vf_instance_t * vf)
263 {
264   int a, b;
265
266   /* Load values from the vf->priv struct for faster dereferencing. */
267   int * * * mask = vf->priv->mask;
268   int max_mask_size = vf->priv->max_mask_size;
269
270   if (mask == NULL)
271     return; /* Nothing allocated, so return before we segfault. */
272
273   /* Free all allocated memory. */
274   for (a = 0; a <= max_mask_size; a++) /* Loop through each mask. */
275   {
276     for (b = -a; b <= a; b++) /* Loop through each scanline in a mask. */
277     {
278       free(mask[a][b + a]); /* Free a scanline. */
279     }
280     free(mask[a]); /* Free a mask. */
281   }
282   free(mask); /* Free the array of pointers pointing to the masks. */
283
284   /* Set the pointer to NULL, so that any duplicate calls to this function will not cause a crash. */
285   vf->priv->mask = NULL;
286
287   return;
288 }
289
290 /**
291  * \brief Set up our array of masks.
292  *
293  * \param vf Where our filter stores persistance data, like these masks.
294  *
295  * This creates an array of progressively larger masks and calculates their
296  * values. The values will not change during program execution once this function
297  * is done.
298  */
299 static void initialize_masks(vf_instance_t * vf)
300 {
301   int a, b, c;
302
303   /* Load values from the vf->priv struct for faster dereferencing. */
304   int * * * mask = vf->priv->mask;
305   int max_mask_size = vf->priv->max_mask_size; /* This tells us how many masks we'll need to generate. */
306
307   /* Create a circular mask for each size up to max_mask_size. When the filter is applied, the mask size is
308      determined on a pixel by pixel basis, with pixels nearer the edge of the logo getting smaller mask sizes. */
309   mask = (int * * *) safe_malloc(sizeof(int * *) * (max_mask_size + 1));
310   for (a = 0; a <= max_mask_size; a++)
311   {
312     mask[a] = (int * *) safe_malloc(sizeof(int *) * ((a * 2) + 1));
313     for (b = -a; b <= a; b++)
314     {
315       mask[a][b + a] = (int *) safe_malloc(sizeof(int) * ((a * 2) + 1));
316       for (c = -a; c <= a; c++)
317       {
318         if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */
319           mask[a][b + a][c + a] = 1;
320         else
321           mask[a][b + a][c + a] = 0;
322       }
323     }
324   }
325
326   /* Store values back to vf->priv so they aren't lost after the function returns. */
327   vf->priv->mask = mask;
328
329   return;
330 }
331
332 /**
333  * \brief Pre-processes an image to give distance information.
334  *
335  * \param vf Data structure that holds persistant information. All it is used for
336              in this function is to store the calculated max_mask_size variable.
337  * \param mask This image will be converted from a greyscale image into a
338  *             distance image.
339  *
340  * This function takes a greyscale image (pgm_structure * mask) and converts it
341  * in place into a distance image. A distance image is zero for pixels ourside of
342  * the logo and is the manhattan distance (|dx| + |dy|) for pixels inside of the
343  * logo. This will overestimate the distance, but that is safe, and is far easier
344  * to implement than a proper pythagorean distance since I'm using a modified
345  * erosion algorithm to compute the distances.
346  */
347 static void convert_mask_to_strength_mask(vf_instance_t * vf, pgm_structure * mask)
348 {
349   int x, y; /* Used by our for loops to go through every single pixel in the picture one at a time. */
350   int has_anything_changed = 1; /* Used by the main while() loop to know if anything changed on the last erosion. */
351   int current_pass = 0; /* How many times we've gone through the loop. Used in the in-place erosion algorithm
352                            and to get us max_mask_size later on. */
353   int max_mask_size; /* This will record how large a mask the pixel that is the furthest from the edge of the logo
354                            (and thus the neediest) is. */
355   char * current_pixel = mask->pixel; /* This stores the actual pixel data. */
356
357   /* First pass, set all non-zero values to 1. After this loop finishes, the data should be considered numeric
358      data for the filter, not color data. */
359   for (x = 0; x < mask->height * mask->width; x++, current_pixel++)
360     if(*current_pixel) *current_pixel = 1;
361
362   /* Second pass and future passes. For each pass, if a pixel is itself the same value as the current pass,
363      and its four neighbors are too, then it is incremented. If no pixels are incremented by the end of the pass,
364      then we go again. Edge pixels are counted as always excluded (this should be true anyway for any sane mask,
365      but if it isn't this will ensure that we eventually exit). */
366   while (has_anything_changed)
367   {
368     current_pass++;
369     current_pixel = mask->pixel;
370
371     has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */
372
373     for (y = 1; y < mask->height - 1; y++)
374     {
375       for (x = 1; x < mask->width - 1; x++)
376       {
377         /* Apply the in-place erosion transform. It is based on the following two premises: 1 - Any pixel that fails 1 erosion
378            will fail all future erosions. 2 - Only pixels having survived all erosions up to the present will be >= to
379            current_pass. It doesn't matter if it survived the current pass, failed it, or hasn't been tested yet. */
380         if (*current_pixel >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */
381             *(current_pixel + 1) >= current_pass &&
382             *(current_pixel - 1) >= current_pass &&
383             *(current_pixel + mask->width) >= current_pass &&
384             *(current_pixel - mask->width) >= current_pass)
385          {
386            (*current_pixel)++; /* Increment the value since it still has not been eroded, as evidenced by the if statement
387                                   that just evaluated to true. */
388            has_anything_changed = 1;
389          }
390         current_pixel++;
391       }
392     }
393   }
394
395   /* Apply the fudge factor, which will increase the size of the mask a little to reduce jitter at the cost of more blur. */
396   for (y = 1; y < mask->height - 1; y++)
397   {
398    for (x = 1; x < mask->width - 1; x++)
399     {
400       mask->pixel[(y * mask->width) + x] = apply_mask_fudge_factor(mask->pixel[(y * mask->width) + x]);
401     }
402   }
403
404   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. */
405   max_mask_size = apply_mask_fudge_factor(max_mask_size); /* Apply the fudge factor to this number too, since we must
406                                                              ensure that enough masks are generated. */
407   vf->priv->max_mask_size = max_mask_size; /* Commit the newly calculated max_mask_size to the vf->priv struct. */
408
409   return;
410 }
411
412 /**
413  * \brief Our blurring function.
414  *
415  * \param vf Stores persistant data. In this function we are interested in the
416  *           array of masks.
417  * \param value_out The properly blurred and delogoed pixel is outputted here.
418  * \param logo_mask Tells us which pixels are in the logo and which aren't.
419  * \param image The image that is having its logo removed.
420  * \param x x-coordinate of the pixel to blur.
421  * \param y y-coordinate of the pixel to blur.
422  * \param plane 0 = luma, 1 = blue chroma, 2 = red chroma (YUV).
423  *
424  * This function is the core of the filter. It takes a pixel that is inside the
425  * logo and blurs it. It does so by finding the average of all the pixels within
426  * the mask and outside of the logo.
427  */
428 static void get_blur(const vf_instance_t * const vf, unsigned int * const value_out, const pgm_structure * const logo_mask,
429               const mp_image_t * const image, const int x, const int y, const int plane)
430 {
431   int mask_size; /* Mask size tells how large a circle to use. The radius is about (slightly larger than) mask size. */
432   /* Get values from vf->priv for faster dereferencing. */
433   int * * * mask = vf->priv->mask;
434
435   int start_posx, start_posy, end_posx, end_posy;
436   int i, j;
437   unsigned int accumulator = 0, divisor = 0;
438   const unsigned char * mask_read_position; /* What pixel we are reading out of the circular blur mask. */
439   const unsigned char * logo_mask_read_position; /* What pixel we are reading out of the filter image. */
440
441   /* Prepare our bounding rectangle and clip it if need be. */
442   mask_size = test_filter(logo_mask, x, y);
443   start_posx = max(0, x - mask_size);
444   start_posy = max(0, y - mask_size);
445   end_posx = min(image->width - 1, x + mask_size);
446   end_posy = min(image->height - 1, y + mask_size);
447
448   mask_read_position = image->planes[plane] + (image->stride[plane] * start_posy) + start_posx;
449   logo_mask_read_position = logo_mask->pixel + (start_posy * logo_mask->width) + start_posx;
450
451   for (j = start_posy; j <= end_posy; j++)
452   {
453     for (i = start_posx; i <= end_posx; i++)
454     {
455       if (!(*logo_mask_read_position) && mask[mask_size][i - start_posx][j - start_posy])
456       { /* Check to see if this pixel is in the logo or not. Only use the pixel if it is not. */
457         accumulator += *mask_read_position;
458         divisor++;
459       }
460
461       mask_read_position++;
462       logo_mask_read_position++;
463     }
464
465     mask_read_position += (image->stride[plane] - ((end_posx + 1) - start_posx));
466     logo_mask_read_position += (logo_mask->width - ((end_posx + 1) - start_posx));
467   }
468
469   if (divisor == 0) /* This means that not a single pixel is outside of the logo, so we have no data. */
470   { /* We should put some eye catching value here, to indicate the flaw to the user. */
471     *value_out = 255;
472   }
473   else /* Else we need to normalise the data using the divisor. */
474   {
475     *value_out = (accumulator + (divisor / 2)) / divisor; /* Divide, taking into account average rounding error. */
476   }
477
478   return;
479 }
480
481 /**
482  * \brief Free a pgm_structure. Undoes load_pgm(...).
483  */
484 static void destroy_pgm(pgm_structure * to_be_destroyed)
485 {
486   if (to_be_destroyed == NULL)
487     return; /* Don't do anything if a NULL pointer was passed it. */
488
489   /* Internally allocated memory. */
490   if (to_be_destroyed->pixel != NULL)
491   {
492     free(to_be_destroyed->pixel);
493     to_be_destroyed->pixel = NULL;
494   }
495
496   /* Free the actual struct instance. This is done here and not by the calling function. */
497   free(to_be_destroyed);
498 }
499
500 /** \brief Helper function for load_pgm(...) to skip whitespace. */
501 static void load_pgm_skip(FILE *f) {
502   int c, comment = 0;
503   do {
504     c = fgetc(f);
505     if (c == '#')
506       comment = 1;
507     if (c == '\n')
508       comment = 0;
509   } while (c != EOF && (isspace(c) || comment));
510   ungetc(c, f);
511 }
512
513 #define REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE(message) {mp_msg(MSGT_VFILTER, MSGL_ERR, message); return NULL;}
514
515 /**
516  * \brief Loads a raw pgm or ppm file into a newly created pgm_structure object.
517  *
518  * \param file_name The name of the file to be loaded. So long as the file is a
519  *                  valid pgm or ppm file, it will load correctly, even if the
520  *                  extension is missing or invalid.
521  *
522  * \return A pointer to the newly created pgm_structure object. Don't forget to
523  *         call destroy_pgm(...) when you're done with this. If an error occurs,
524  *         NULL is returned.
525  *
526  * Can load either raw pgm (P5) or raw ppm (P6) image files as a binary image.
527  * While a pgm file will be loaded normally (greyscale), the only thing that is
528  * guaranteed with ppm is that all zero (R = 0, G = 0, B = 0) pixels will remain
529  * zero, and non-zero pixels will remain non-zero.
530  */
531 static pgm_structure * load_pgm(const char * file_name)
532 {
533   int maximum_greyscale_value;
534   FILE * input;
535   int pnm_number;
536   pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));
537   char * write_position;
538   char * end_position;
539   int image_size; /* width * height */
540
541   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");
542
543   /* Parse the PGM header. */
544   if (fgetc(input) != 'P') REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: File is not a valid PGM or PPM file.\n");
545   pnm_number = fgetc(input) - '0';
546   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");
547   load_pgm_skip(input);
548   if (fscanf(input, "%i", &(new_pgm->width)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
549   load_pgm_skip(input);
550   if (fscanf(input, "%i", &(new_pgm->height)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
551   load_pgm_skip(input);
552   if (fscanf(input, "%i", &maximum_greyscale_value) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");
553   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");
554   load_pgm_skip(input);
555
556   new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);
557
558   /* Load the pixels. */
559   /* 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.*/
560   image_size = new_pgm->width * new_pgm->height;
561   end_position = new_pgm->pixel + image_size;
562   for (write_position = new_pgm->pixel; write_position < end_position; write_position++)
563   {
564     *write_position = fgetc(input);
565     if (pnm_number == 6) /* This tests to see if the file is a PPM file. */
566     { /* 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. */
567       *write_position |= fgetc(input);
568       *write_position |= fgetc(input);
569     }
570   }
571
572   return new_pgm;
573 }
574
575 /**
576  * \brief Generates a scaled down image with half width, height, and intensity.
577  *
578  * \param vf Our struct for persistant data. In this case, it is used to update
579  *           mask_max_size with the larger of the old or new value.
580  * \param input_image The image from which the new half-sized one will be based.
581  *
582  * \return The newly allocated and shrunken image.
583  *
584  * This function not only scales down an image, but halves the value in each pixel
585  * too. The purpose of this is to produce a chroma filter image out of a luma
586  * filter image. The pixel values store the distance to the edge of the logo and
587  * halving the dimensions halves the distance. This function rounds up, because
588  * a downwards rounding error could cause the filter to fail, but an upwards
589  * rounding error will only cause a minor amount of excess blur in the chroma
590  * planes.
591  */
592 static pgm_structure * generate_half_size_image(vf_instance_t * vf, pgm_structure * input_image)
593 {
594   int x, y;
595   pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));
596   int has_anything_changed = 1;
597   int current_pass;
598   int max_mask_size;
599   char * current_pixel;
600
601   new_pgm->width = input_image->width / 2;
602   new_pgm->height = input_image->height / 2;
603   new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);
604
605   /* Copy over the image data, using the average of 4 pixels for to calculate each downsampled pixel. */
606   for (y = 0; y < new_pgm->height; y++)
607     for (x = 0; x < new_pgm->width; x++)
608     {
609       /* Set the pixel if there exists a non-zero value in the source pixels, else clear it. */
610       new_pgm->pixel[(y * new_pgm->width) + x] = input_image->pixel[((y << 1) * input_image->width) + (x << 1)] ||
611                                                  input_image->pixel[((y << 1) * input_image->width) + (x << 1) + 1] ||
612                                                  input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1)] ||
613                                                  input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1) + 1];
614       new_pgm->pixel[(y * new_pgm->width) + x] = min(1, new_pgm->pixel[(y * new_pgm->width) + x]);
615     }
616
617   /* Now we need to recalculate the numbers for the smaller size. Just using the old_value / 2 can cause subtle
618      and fairly rare, but very nasty, bugs. */
619
620   current_pixel = new_pgm->pixel;
621   /* First pass, set all non-zero values to 1. */
622   for (x = 0; x < new_pgm->height * new_pgm->width; x++, current_pixel++)
623     if(*current_pixel) *current_pixel = 1;
624
625   /* Second pass and future passes. For each pass, if a pixel is itself the same value as the current pass,
626      and its four neighbors are too, then it is incremented. If no pixels are incremented by the end of the pass,
627      then we go again. Edge pixels are counted as always excluded (this should be true anyway for any sane mask,
628      but if it isn't this will ensure that we eventually exit). */
629   current_pass = 0;
630   while (has_anything_changed)
631   {
632     current_pass++;
633
634     has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */
635
636     for (y = 1; y < new_pgm->height - 1; y++)
637     {
638       for (x = 1; x < new_pgm->width - 1; x++)
639       {
640         if (new_pgm->pixel[(y * new_pgm->width) + x] >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */
641             new_pgm->pixel[(y * new_pgm->width) + (x + 1)] >= current_pass &&
642             new_pgm->pixel[(y * new_pgm->width) + (x - 1)] >= current_pass &&
643             new_pgm->pixel[((y + 1) * new_pgm->width) + x] >= current_pass &&
644             new_pgm->pixel[((y - 1) * new_pgm->width) + x] >= current_pass)
645          {
646            new_pgm->pixel[(y * new_pgm->width) + x]++; /* Increment the value since it still has not been eroded,
647                                                     as evidenced by the if statement that just evaluated to true. */
648            has_anything_changed = 1;
649          }
650       }
651     }
652   }
653
654   for (y = 1; y < new_pgm->height - 1; y++)
655   {
656    for (x = 1; x < new_pgm->width - 1; x++)
657     {
658       new_pgm->pixel[(y * new_pgm->width) + x] = apply_mask_fudge_factor(new_pgm->pixel[(y * new_pgm->width) + x]);
659     }
660   }
661
662   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. */
663   max_mask_size = apply_mask_fudge_factor(max_mask_size);
664   /* Commit the newly calculated max_mask_size to the vf->priv struct. */
665   vf->priv->max_mask_size = max(max_mask_size, vf->priv->max_mask_size);
666
667   return new_pgm;
668 }
669
670 /**
671  * \brief Checks if YV12 is supported by the next filter.
672  */
673 static unsigned int find_best(struct vf_instance *vf){
674   int is_format_okay = vf_next_query_format(vf, IMGFMT_YV12);
675   if ((is_format_okay & VFCAP_CSP_SUPPORTED_BY_HW) || (is_format_okay & VFCAP_CSP_SUPPORTED))
676     return IMGFMT_YV12;
677   else
678     return 0;
679 }
680
681 //===========================================================================//
682
683 /**
684  * \brief Configure the filter and call the next filter's config function.
685  */
686 static int config(struct vf_instance *vf, int width, int height, int d_width, int d_height, unsigned int flags, unsigned int outfmt)
687 {
688   if(!(vf->priv->fmt=find_best(vf)))
689     return 0;
690   else
691     return vf_next_config(vf,width,height,d_width,d_height,flags,vf->priv->fmt);
692 }
693
694 /**
695  * \brief Removes the logo from a plane (either luma or chroma).
696  *
697  * \param vf Not needed by this function, but needed by the blur function.
698  * \param source The image to have it's logo removed.
699  * \param destination Where the output image will be stored.
700  * \param source_stride How far apart (in memory) two consecutive lines are.
701  * \param destination Same as source_stride, but for the destination image.
702  * \param width Width of the image. This is the same for source and destination.
703  * \param height Height of the image. This is the same for source and destination.
704  * \param is_image_direct If the image is direct, then source and destination are
705  *        the same and we can save a lot of time by not copying pixels that
706  *        haven't changed.
707  * \param filter The image that stores the distance to the edge of the logo for
708  *        each pixel.
709  * \param logo_start_x Smallest x-coordinate that contains at least 1 logo pixel.
710  * \param logo_start_y Smallest y-coordinate that contains at least 1 logo pixel.
711  * \param logo_end_x Largest x-coordinate that contains at least 1 logo pixel.
712  * \param logo_end_y Largest y-coordinate that contains at least 1 logo pixel.
713  *
714  * This function processes an entire plane. Pixels outside of the logo are copied
715  * to the output without change, and pixels inside the logo have the de-blurring
716  * function applied.
717  */
718 static void convert_yv12(const vf_instance_t * const vf, const char * const source, const int source_stride,
719                          const mp_image_t * const source_image, const int width, const int height,
720                          char * const destination, const int destination_stride, int is_image_direct, pgm_structure * filter,
721                          const int plane, const int logo_start_x, const int logo_start_y, const int logo_end_x, const int logo_end_y)
722 {
723   int y;
724   int x;
725
726   /* These pointers point to where we are getting our pixel data (inside mpi) and where we are storing it (inside dmpi). */
727   const unsigned char * source_line;
728   unsigned char * destination_line;
729
730   if (!is_image_direct)
731     memcpy_pic(destination, source, width, height, destination_stride, source_stride);
732
733   for (y = logo_start_y; y <= logo_end_y; y++)
734   {
735     source_line = (const unsigned char *) source + (source_stride * y);
736     destination_line = (unsigned char *) destination + (destination_stride * y);
737
738     for (x = logo_start_x; x <= logo_end_x; x++)
739     {
740       unsigned int output;
741
742       if (filter->pixel[(y * filter->width) + x]) /* Only process if we are in the logo. */
743       {
744         get_blur(vf, &output, filter, source_image, x, y, plane);
745         destination_line[x] = output;
746       }
747       else /* Else just copy the data. */
748         if (!is_image_direct)
749           destination_line[x] = source_line[x];
750     }
751   }
752 }
753
754 /**
755  * \brief Process a frame.
756  *
757  * \param mpi The image sent to use by the previous filter.
758  * \param dmpi Where we will store the processed output image.
759  * \param vf This is how the filter gets access to it's persistant data.
760  *
761  * \return The return code of the next filter, or 0 on failure/error.
762  *
763  * This function processes an entire frame. The frame is sent by the previous
764  * filter, has the logo removed by the filter, and is then sent to the next
765  * filter.
766  */
767 static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
768     mp_image_t *dmpi;
769
770     dmpi=vf_get_image(vf->next,vf->priv->fmt,
771         MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
772         mpi->w, mpi->h);
773
774     /* Check to make sure that the filter image and the video stream are the same size. */
775     if (vf->priv->filter->width != mpi->w || vf->priv->filter->height != mpi->h)
776     {
777       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",
778              vf->priv->filter->width, vf->priv->filter->height, mpi->w, mpi->h);
779       return 0;
780     }
781
782     switch(dmpi->imgfmt){
783     case IMGFMT_YV12:
784           convert_yv12(vf, mpi->planes[0],  mpi->stride[0], mpi, mpi->w, mpi->h,
785                           dmpi->planes[0], dmpi->stride[0],
786                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->filter, 0,
787                           vf->priv->bounding_rectangle_posx1, vf->priv->bounding_rectangle_posy1,
788                           vf->priv->bounding_rectangle_posx2, vf->priv->bounding_rectangle_posy2);
789           convert_yv12(vf, mpi->planes[1],  mpi->stride[1], mpi, mpi->w / 2, mpi->h / 2,
790                           dmpi->planes[1], dmpi->stride[1],
791                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->half_size_filter, 1,
792                           vf->priv->bounding_rectangle_half_size_posx1, vf->priv->bounding_rectangle_half_size_posy1,
793                           vf->priv->bounding_rectangle_half_size_posx2, vf->priv->bounding_rectangle_half_size_posy2);
794           convert_yv12(vf, mpi->planes[2],  mpi->stride[2], mpi, mpi->w / 2, mpi->h / 2,
795                           dmpi->planes[2], dmpi->stride[2],
796                           mpi->flags & MP_IMGFLAG_DIRECT, vf->priv->half_size_filter, 2,
797                           vf->priv->bounding_rectangle_half_size_posx1, vf->priv->bounding_rectangle_half_size_posy1,
798                           vf->priv->bounding_rectangle_half_size_posx2, vf->priv->bounding_rectangle_half_size_posy2);
799           break;
800
801     default:
802         mp_msg(MSGT_VFILTER,MSGL_ERR,"Unhandled format: 0x%X\n",dmpi->imgfmt);
803         return 0;
804     }
805
806     return vf_next_put_image(vf,dmpi, pts);
807 }
808
809 //===========================================================================//
810
811 /**
812  * \brief Checks to see if the next filter accepts YV12 images.
813  */
814 static int query_format(struct vf_instance *vf, unsigned int fmt)
815 {
816   if (fmt == IMGFMT_YV12)
817     return vf_next_query_format(vf, IMGFMT_YV12);
818   else
819     return 0;
820 }
821
822 /**
823  * \brief Frees memory that our filter allocated.
824  *
825  * This is called at exit-time.
826  */
827 static void uninit(vf_instance_t *vf)
828 {
829   /* Destroy our masks and images. */
830   destroy_pgm(vf->priv->filter);
831   destroy_pgm(vf->priv->half_size_filter);
832   destroy_masks(vf);
833
834   /* Destroy our private structure that had been used to store those masks and images. */
835   free(vf->priv);
836
837   return;
838 }
839
840 /**
841  * \brief Initializes our filter.
842  *
843  * \param args The arguments passed in from the command line go here. This
844  *             filter expects only a single argument telling it where the PGM
845  *             or PPM file that describes the logo region is.
846  *
847  * This sets up our instance variables and parses the arguments to the filter.
848  */
849 static int vf_open(vf_instance_t *vf, char *args)
850 {
851   vf->priv = safe_malloc(sizeof(vf_priv_s));
852   vf->uninit = uninit;
853
854   /* Load our filter image. */
855   if (args)
856     vf->priv->filter = load_pgm(args);
857   else
858   {
859     mp_msg(MSGT_VFILTER, MSGL_ERR, "[vf]remove_logo usage: remove_logo=/path/to/filter_image_file.pgm\n");
860     free(vf->priv);
861     return 0;
862   }
863
864   if (vf->priv->filter == NULL)
865   {
866     /* Error message was displayed by load_pgm(). */
867     free(vf->priv);
868     return 0;
869   }
870
871   /* Create the scaled down filter image for the chroma planes. */
872   convert_mask_to_strength_mask(vf, vf->priv->filter);
873   vf->priv->half_size_filter = generate_half_size_image(vf, vf->priv->filter);
874
875   /* Now that we know how many masks we need (the info is in vf), we can generate the masks. */
876   initialize_masks(vf);
877
878   /* Calculate our bounding rectangles, which determine in what region the logo resides for faster processing. */
879   calculate_bounding_rectangle(&vf->priv->bounding_rectangle_posx1, &vf->priv->bounding_rectangle_posy1,
880                                &vf->priv->bounding_rectangle_posx2, &vf->priv->bounding_rectangle_posy2,
881                                 vf->priv->filter);
882   calculate_bounding_rectangle(&vf->priv->bounding_rectangle_half_size_posx1,
883                                &vf->priv->bounding_rectangle_half_size_posy1,
884                                &vf->priv->bounding_rectangle_half_size_posx2,
885                                &vf->priv->bounding_rectangle_half_size_posy2,
886                                 vf->priv->half_size_filter);
887
888   vf->config=config;
889   vf->put_image=put_image;
890   vf->query_format=query_format;
891   return 1;
892 }
893
894 /**
895  * \brief Meta data about our filter.
896  */
897 const vf_info_t vf_info_remove_logo = {
898     "Removes a tv logo based on a mask image.",
899     "remove-logo",
900     "Robert Edele",
901     "",
902     vf_open,
903     NULL
904 };
905
906 //===========================================================================//