Esempio n. 1
0
/**
 * \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->max_mask_size = max_mask_size; /* Commit the newly calculated max_mask_size to the vf->priv struct. */

  return;
}
Esempio n. 2
0
/**
 * \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);

  /* Copy over the image data, using the average of 4 pixels for to calculate each downsampled pixel. */
  for (y = 0; y < new_pgm->height; y++)
    for (x = 0; x < new_pgm->width; x++)
    {
      /* Set the pixel if there exists a non-zero value in the source pixels, else clear it. */
      new_pgm->pixel[(y * new_pgm->width) + x] = input_image->pixel[((y << 1) * input_image->width) + (x << 1)] ||
                                                 input_image->pixel[((y << 1) * input_image->width) + (x << 1) + 1] ||
                                                 input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1)] ||
                                                 input_image->pixel[(((y << 1) + 1) * input_image->width) + (x << 1) + 1];
      new_pgm->pixel[(y * new_pgm->width) + x] = min(1, new_pgm->pixel[(y * new_pgm->width) + x]);
    }

  /* Now we need to recalculate the numbers for the smaller size. Just using the old_value / 2 can cause subtle
     and fairly rare, but very nasty, bugs. */

  current_pixel = new_pgm->pixel;
  /* First pass, set all non-zero values to 1. */
  for (x = 0; x < new_pgm->height * new_pgm->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). */
  current_pass = 0;
  while (has_anything_changed)
  {
    current_pass++;

    has_anything_changed = 0; /* If this doesn't get set by the end of this pass, then we're done. */

    for (y = 1; y < new_pgm->height - 1; y++)
    {
      for (x = 1; x < new_pgm->width - 1; x++)
      {
        if (new_pgm->pixel[(y * new_pgm->width) + x] >= current_pass && /* By using >= instead of ==, we allow the algorithm to work in place. */
            new_pgm->pixel[(y * new_pgm->width) + (x + 1)] >= current_pass &&
            new_pgm->pixel[(y * new_pgm->width) + (x - 1)] >= current_pass &&
            new_pgm->pixel[((y + 1) * new_pgm->width) + x] >= current_pass &&
            new_pgm->pixel[((y - 1) * new_pgm->width) + x] >= current_pass)
         {
           new_pgm->pixel[(y * new_pgm->width) + x]++; /* 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;
         }
      }
    }
  }

  for (y = 1; y < new_pgm->height - 1; y++)
  {
   for (x = 1; x < new_pgm->width - 1; x++)
    {
      new_pgm->pixel[(y * new_pgm->width) + x] = apply_mask_fudge_factor(new_pgm->pixel[(y * new_pgm->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);
  /* Commit the newly calculated max_mask_size to the vf->priv struct. */
  vf->priv->max_mask_size = max(max_mask_size, vf->priv->max_mask_size);

  return new_pgm;
}
Esempio n. 3
0
/**
 * Pre-process an image to give distance information.
 *
 * This function takes a bitmap image and converts it in place into a
 * distance image. A distance image is zero for pixels outside of the
 * logo and is the Manhattan distance (|dx| + |dy|) from the logo edge
 * 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.
 *
 * @param mask image which will be converted from a greyscale image
 * into a distance image.
 */
static void convert_mask_to_strength_mask(uint8_t *data, int linesize,
                                          int w, int h, int min_val,
                                          int *max_mask_size)
{
    int x, y;

    /* 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 current_pass = 0;

    /* set all non-zero values to 1 */
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            data[y*linesize + x] = data[y*linesize + x] > min_val;

    /* 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 (1) {
        /* If this doesn't get set by the end of this pass, then we're done. */
        int has_anything_changed = 0;
        uint8_t *current_pixel0 = data, *current_pixel;
        current_pass++;

        for (y = 1; y < h-1; y++) {
            current_pixel = current_pixel0;
            for (x = 1; x < w-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.  By using >=
                   instead of ==, we allow the algorithm to work in
                   place. */
                if ( *current_pixel      >= current_pass &&
                    *(current_pixel + 1) >= current_pass &&
                    *(current_pixel - 1) >= current_pass &&
                    *(current_pixel + w) >= current_pass &&
                    *(current_pixel - w) >= current_pass) {
                    /* Increment the value since it still has not been
                     * eroded, as evidenced by the if statement that
                     * just evaluated to true. */
                    (*current_pixel)++;
                    has_anything_changed = 1;
                }
                current_pixel++;
            }
            current_pixel0 += linesize;
        }
        if (!has_anything_changed)
            break;
    }

    /* 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 < h - 1; y++)
        for (x = 1; x < w - 1; x++)
            data[(y * linesize) + x] = apply_mask_fudge_factor(data[(y * linesize) + x]);

    /* As a side-effect, we now know the maximum mask size, which
     * we'll use to generate our masks. */
    /* Apply the fudge factor to this number too, since we must ensure
     * that enough masks are generated. */
    *max_mask_size = apply_mask_fudge_factor(current_pass + 1);
}