vf_remove_logo.c

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 910 行 · 第 1/3 页

C
910
字号
  int a, b, c;  /* Load values from the vf->priv struct for faster dereferencing. */  int * * * mask = ((vf_priv_s *)vf->priv)->mask;  int max_mask_size = ((vf_priv_s *)vf->priv)->max_mask_size; /* This tells us how many masks we'll need to generate. */  /* Create a circular mask for each size up to max_mask_size. When the filter is applied, the mask size is     determined on a pixel by pixel basis, with pixels nearer the edge of the logo getting smaller mask sizes. */  mask = (int * * *) safe_malloc(sizeof(int * *) * (max_mask_size + 1));  for (a = 0; a <= max_mask_size; a++)  {    mask[a] = (int * *) safe_malloc(sizeof(int *) * ((a * 2) + 1));    for (b = -a; b <= a; b++)    {      mask[a][b + a] = (int *) safe_malloc(sizeof(int) * ((a * 2) + 1));      for (c = -a; c <= a; c++)      {        if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */          mask[a][b + a][c + a] = 1;        else          mask[a][b + a][c + a] = 0;       }    }  }  /* Store values back to vf->priv so they aren't lost after the function returns. */  ((vf_priv_s *)vf->priv)->mask = mask;  return;}/** * \brief Pre-processes an image to give distance information. * * \param vf Data structure that holds persistant information. All it is used for             in this function is to store the calculated max_mask_size variable. * \param mask This image will be converted from a greyscale image into a *             distance image. * * This function takes a greyscale image (pgm_structure * mask) and converts it * in place into a distance image. A distance image is zero for pixels ourside of * the logo and is the manhattan distance (|dx| + |dy|) for pixels inside of the * logo. This will overestimate the distance, but that is safe, and is far easier * to implement than a proper pythagorean distance since I'm using a modified * erosion algorithm to compute the distances. */static void convert_mask_to_strength_mask(vf_instance_t * vf, pgm_structure * mask){  int x, y; /* Used by our for loops to go through every single pixel in the picture one at a time. */  int has_anything_changed = 1; /* Used by the main while() loop to know if anything changed on the last erosion. */  int current_pass = 0; /* How many times we've gone through the loop. Used in the in-place erosion algorithm                           and to get us max_mask_size later on. */  int max_mask_size; /* This will record how large a mask the pixel that is the furthest from the edge of the logo                           (and thus the neediest) is. */  char * current_pixel = mask->pixel; /* This stores the actual pixel data. */  /* First pass, set all non-zero values to 1. After this loop finishes, the data should be considered numeric     data for the filter, not color data. */  for (x = 0; x < mask->height * mask->width; x++, current_pixel++)    if(*current_pixel) *current_pixel = 1;  /* Second pass and future passes. For each pass, if a pixel is itself the same value as the current pass,     and its four neighbors are too, then it is incremented. If no pixels are incremented by the end of the pass,     then we go again. Edge pixels are counted as always excluded (this should be true anyway for any sane mask,     but if it isn't this will ensure that we eventually exit). */  while (has_anything_changed)  {    current_pass++;    current_pixel = mask->pixel;    has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */    for (y = 1; y < mask->height - 1; y++)    {      for (x = 1; x < mask->width - 1; x++)      {        /* Apply the in-place erosion transform. It is based on the following two premises: 1 - Any pixel that fails 1 erosion           will fail all future erosions. 2 - Only pixels having survived all erosions up to the present will be >= to           current_pass. It doesn't matter if it survived the current pass, failed it, or hasn't been tested yet. */        if (*current_pixel >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */            *(current_pixel + 1) >= current_pass &&            *(current_pixel - 1) >= current_pass &&            *(current_pixel + mask->width) >= current_pass &&            *(current_pixel - mask->width) >= current_pass)         {           (*current_pixel)++; /* Increment the value since it still has not been eroded, as evidenced by the if statement                                  that just evaluated to true. */           has_anything_changed = 1;         }        current_pixel++;      }    }  }  /* Apply the fudge factor, which will increase the size of the mask a little to reduce jitter at the cost of more blur. */  for (y = 1; y < mask->height - 1; y++)  {   for (x = 1; x < mask->width - 1; x++)    {      mask->pixel[(y * mask->width) + x] = apply_mask_fudge_factor(mask->pixel[(y * mask->width) + x]);    }  }  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. */  max_mask_size = apply_mask_fudge_factor(max_mask_size); /* Apply the fudge factor to this number too, since we must                                                             ensure that enough masks are generated. */  ((vf_priv_s *)vf->priv)->max_mask_size = max_mask_size; /* Commit the newly calculated max_mask_size to the vf->priv struct. */  return;}/** * \brief Our blurring function. * * \param vf Stores persistant data. In this function we are interested in the *           array of masks. * \param value_out The properly blurred and delogoed pixel is outputted here. * \param logo_mask Tells us which pixels are in the logo and which aren't. * \param image The image that is having its logo removed. * \param x x-coordinate of the pixel to blur. * \param y y-coordinate of the pixel to blur. * \param plane 0 = luma, 1 = blue chroma, 2 = red chroma (YUV). * * This function is the core of the filter. It takes a pixel that is inside the * logo and blurs it. It does so by finding the average of all the pixels within * the mask and outside of the logo. */static void get_blur(const vf_instance_t * const vf, unsigned int * const value_out, const pgm_structure * const logo_mask,              const mp_image_t * const image, const int x, const int y, const int plane){  int mask_size; /* Mask size tells how large a circle to use. The radius is about (slightly larger than) mask size. */  /* Get values from vf->priv for faster dereferencing. */  int * * * mask = ((vf_priv_s *)vf->priv)->mask;  int start_posx, start_posy, end_posx, end_posy;  int i, j;  unsigned int accumulator = 0, divisor = 0;  const unsigned char * mask_read_position; /* What pixel we are reading out of the circular blur mask. */  const unsigned char * logo_mask_read_position; /* What pixel we are reading out of the filter image. */  /* Prepare our bounding rectangle and clip it if need be. */  mask_size = test_filter(logo_mask, x, y);  start_posx = max(0, x - mask_size);  start_posy = max(0, y - mask_size);  end_posx = min(image->width - 1, x + mask_size);  end_posy = min(image->height - 1, y + mask_size);  mask_read_position = image->planes[plane] + (image->stride[plane] * start_posy) + start_posx;  logo_mask_read_position = logo_mask->pixel + (start_posy * logo_mask->width) + start_posx;  for (j = start_posy; j <= end_posy; j++)  {    for (i = start_posx; i <= end_posx; i++)    {      if (!(*logo_mask_read_position) && mask[mask_size][i - start_posx][j - start_posy])      { /* Check to see if this pixel is in the logo or not. Only use the pixel if it is not. */        accumulator += *mask_read_position;        divisor++;      }      mask_read_position++;      logo_mask_read_position++;    }    mask_read_position += (image->stride[plane] - ((end_posx + 1) - start_posx));    logo_mask_read_position += (logo_mask->width - ((end_posx + 1) - start_posx));  }  if (divisor == 0) /* This means that not a single pixel is outside of the logo, so we have no data. */  { /* We should put some eye catching value here, to indicate the flaw to the user. */    *value_out = 255;  }  else /* Else we need to normalise the data using the divisor. */  {    *value_out = (accumulator + (divisor / 2)) / divisor; /* Divide, taking into account average rounding error. */  }  return;}/** * \brief Free a pgm_structure. Undoes load_pgm(...). */static void destroy_pgm(pgm_structure * to_be_destroyed){  if (to_be_destroyed == NULL)    return; /* Don't do anything if a NULL pointer was passed it. */  /* Internally allocated memory. */  if (to_be_destroyed->pixel != NULL)  {    free(to_be_destroyed->pixel);    to_be_destroyed->pixel = NULL;  }  /* Free the actual struct instance. This is done here and not by the calling function. */  free(to_be_destroyed);}/** \brief Helper function for load_pgm(...) to skip whitespace. */static void load_pgm_skip(FILE *f) {  int c, comment = 0;  do {    c = fgetc(f);    if (c == '#')      comment = 1;    if (c == '\n')      comment = 0;  } while (c != EOF && (isspace(c) || comment));  ungetc(c, f);}#define REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE(message) {mp_msg(MSGT_VFILTER, MSGL_ERR, message); return NULL;}/** * \brief Loads a raw pgm or ppm file into a newly created pgm_structure object. * * \param file_name The name of the file to be loaded. So long as the file is a *                  valid pgm or ppm file, it will load correctly, even if the *                  extension is missing or invalid. * * \return A pointer to the newly created pgm_structure object. Don't forget to *         call destroy_pgm(...) when you're done with this. If an error occurs, *         NULL is returned. * * Can load either raw pgm (P5) or raw ppm (P6) image files as a binary image. * While a pgm file will be loaded normally (greyscale), the only thing that is * guaranteed with ppm is that all zero (R = 0, G = 0, B = 0) pixels will remain * zero, and non-zero pixels will remain non-zero. */static pgm_structure * load_pgm(const char * file_name){  int maximum_greyscale_value;  FILE * input;  int pnm_number;  pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));  char * write_position;  char * end_position;  int image_size; /* width * height */  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");  /* Parse the PGM header. */  if (fgetc(input) != 'P') REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: File is not a valid PGM or PPM file.\n");  pnm_number = fgetc(input) - '0';  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");  load_pgm_skip(input);  if (fscanf(input, "%i", &(new_pgm->width)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");  load_pgm_skip(input);  if (fscanf(input, "%i", &(new_pgm->height)) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");  load_pgm_skip(input);  if (fscanf(input, "%i", &maximum_greyscale_value) != 1) REMOVE_LOGO_LOAD_PGM_ERROR_MESSAGE("[vf]remove-logo: Invalid PGM/PPM header.\n");  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");  load_pgm_skip(input);  new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);  /* Load the pixels. */  /* 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.*/  image_size = new_pgm->width * new_pgm->height;  end_position = new_pgm->pixel + image_size;  for (write_position = new_pgm->pixel; write_position < end_position; write_position++)  {    *write_position = fgetc(input);    if (pnm_number == 6) /* This tests to see if the file is a PPM file. */    { /* 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. */      *write_position |= fgetc(input);      *write_position |= fgetc(input);    }  }  return new_pgm;}/** * \brief Generates a scaled down image with half width, height, and intensity. * * \param vf Our struct for persistant data. In this case, it is used to update *           mask_max_size with the larger of the old or new value. * \param input_image The image from which the new half-sized one will be based. * * \return The newly allocated and shrunken image. * * This function not only scales down an image, but halves the value in each pixel * too. The purpose of this is to produce a chroma filter image out of a luma * filter image. The pixel values store the distance to the edge of the logo and * halving the dimensions halves the distance. This function rounds up, because * a downwards rounding error could cause the filter to fail, but an upwards * rounding error will only cause a minor amount of excess blur in the chroma * planes. */static pgm_structure * generate_half_size_image(vf_instance_t * vf, pgm_structure * input_image){  int x, y;  pgm_structure * new_pgm = (pgm_structure *) safe_malloc (sizeof(pgm_structure));  int has_anything_changed = 1;  int current_pass;  int max_mask_size;  char * current_pixel;  new_pgm->width = input_image->width / 2;  new_pgm->height = input_image->height / 2;  new_pgm->pixel = (unsigned char *) safe_malloc (sizeof(unsigned char) * new_pgm->width * new_pgm->height);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?