Statistics
| Branch: | Revision:

ffmpeg / libavfilter / libmpcodecs / vf_remove_logo.c @ e4852fb3

History | View | Annotate | Download (37.4 KB)

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 vf_remove_logo.c
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->next, 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->next, 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
//===========================================================================//