vf_remove_logo.c
来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 910 行 · 第 1/3 页
C
910 行
/* 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_s *)vf->priv)->max_mask_size = max(max_mask_size, ((vf_priv_s *)vf->priv)->max_mask_size); return new_pgm;}/** * \brief Checks if YV12 is supported by the next filter. */static unsigned int find_best(struct vf_instance_s* vf){ int is_format_okay = vf->next->query_format(vf->next, IMGFMT_YV12); if ((is_format_okay & VFCAP_CSP_SUPPORTED_BY_HW) || (is_format_okay & VFCAP_CSP_SUPPORTED)) return IMGFMT_YV12; else return 0;}//===========================================================================///** * \brief Configure the filter and call the next filter's config function. */static int config(struct vf_instance_s* vf, int width, int height, int d_width, int d_height, unsigned int flags, unsigned int outfmt){ if(!(((vf_priv_s *)vf->priv)->fmt=find_best(vf))) return 0; else return vf_next_config(vf,width,height,d_width,d_height,flags,((vf_priv_s *)vf->priv)->fmt);}/** * \brief Removes the logo from a plane (either luma or chroma). * * \param vf Not needed by this function, but needed by the blur function. * \param source The image to have it's logo removed. * \param destination Where the output image will be stored. * \param source_stride How far apart (in memory) two consecutive lines are. * \param destination Same as source_stride, but for the destination image. * \param width Width of the image. This is the same for source and destination. * \param height Height of the image. This is the same for source and destination. * \param is_image_direct If the image is direct, then source and destination are * the same and we can save a lot of time by not copying pixels that * haven't changed. * \param filter The image that stores the distance to the edge of the logo for * each pixel. * \param logo_start_x Smallest x-coordinate that contains at least 1 logo pixel. * \param logo_start_y Smallest y-coordinate that contains at least 1 logo pixel. * \param logo_end_x Largest x-coordinate that contains at least 1 logo pixel. * \param logo_end_y Largest y-coordinate that contains at least 1 logo pixel. * * This function processes an entire plane. Pixels outside of the logo are copied * to the output without change, and pixels inside the logo have the de-blurring * function applied. */static void convert_yv12(const vf_instance_t * const vf, const char * const source, const int source_stride, const mp_image_t * const source_image, const int width, const int height, char * const destination, const int destination_stride, int is_image_direct, pgm_structure * filter, const int plane, const int logo_start_x, const int logo_start_y, const int logo_end_x, const int logo_end_y){ int y; int x; /* These pointers point to where we are getting our pixel data (inside mpi) and where we are storing it (inside dmpi). */ const unsigned char * source_line; unsigned char * destination_line; if (!is_image_direct) memcpy_pic(destination, source, width, height, destination_stride, source_stride); for (y = logo_start_y; y <= logo_end_y; y++) { source_line = (const unsigned char *) source + (source_stride * y); destination_line = (unsigned char *) destination + (destination_stride * y); for (x = logo_start_x; x <= logo_end_x; x++) { unsigned int output; if (filter->pixel[(y * filter->width) + x]) /* Only process if we are in the logo. */ { get_blur(vf, &output, filter, source_image, x, y, plane); destination_line[x] = output; } else /* Else just copy the data. */ if (!is_image_direct) destination_line[x] = source_line[x]; } }}/** * \brief Process a frame. * * \param mpi The image sent to use by the previous filter. * \param dmpi Where we will store the processed output image. * \param vf This is how the filter gets access to it's persistant data. * * \return The return code of the next filter, or 0 on failure/error. * * This function processes an entire frame. The frame is sent by the previous * filter, has the logo removed by the filter, and is then sent to the next * filter. */static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts){ mp_image_t *dmpi; dmpi=vf_get_image(vf->next,((vf_priv_s *)vf->priv)->fmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w, mpi->h); /* Check to make sure that the filter image and the video stream are the same size. */ if ((((vf_priv_s *)vf->priv)->filter->width != mpi->w) || (((vf_priv_s *)vf->priv)->filter->height != mpi->h)) { 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", ((vf_priv_s *)vf->priv)->filter->width, ((vf_priv_s *)vf->priv)->filter->height, mpi->w, mpi->h); return 0; } switch(dmpi->imgfmt){ case IMGFMT_YV12: convert_yv12(vf, mpi->planes[0], mpi->stride[0], mpi, mpi->w, mpi->h, dmpi->planes[0], dmpi->stride[0], mpi->flags & MP_IMGFLAG_DIRECT, ((vf_priv_s *)vf->priv)->filter, 0, ((vf_priv_s *)vf->priv)->bounding_rectangle_posx1, ((vf_priv_s *)vf->priv)->bounding_rectangle_posy1, ((vf_priv_s *)vf->priv)->bounding_rectangle_posx2, ((vf_priv_s *)vf->priv)->bounding_rectangle_posy2); convert_yv12(vf, mpi->planes[1], mpi->stride[1], mpi, mpi->w / 2, mpi->h / 2, dmpi->planes[1], dmpi->stride[1], mpi->flags & MP_IMGFLAG_DIRECT, ((vf_priv_s *)vf->priv)->half_size_filter, 1, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx1, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy1, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx2, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy2); convert_yv12(vf, mpi->planes[2], mpi->stride[2], mpi, mpi->w / 2, mpi->h / 2, dmpi->planes[2], dmpi->stride[2], mpi->flags & MP_IMGFLAG_DIRECT, ((vf_priv_s *)vf->priv)->half_size_filter, 2, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx1, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy1, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx2, ((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy2); break; default: mp_msg(MSGT_VFILTER,MSGL_ERR,"Unhandled format: 0x%X\n",dmpi->imgfmt); return 0; } return vf_next_put_image(vf,dmpi, pts);}//===========================================================================///** * \brief Checks to see if the next filter accepts YV12 images. */static int query_format(struct vf_instance_s * vf, unsigned int fmt){ if (fmt == IMGFMT_YV12) return vf->next->query_format(vf->next, IMGFMT_YV12); else return 0;}/** * \brief Initializes our filter. * * \param args The arguments passed in from the command line go here. This * filter expects only a single argument telling it where the PGM * or PPM file that describes the logo region is. * * This sets up our instance variables and parses the arguments to the filter. */static int vf_open(vf_instance_t * vf, char * args){ vf->priv = safe_malloc(sizeof(vf_priv_s)); /* Load our filter image. */ if (args) ((vf_priv_s *)vf->priv)->filter = load_pgm(args); else { mp_msg(MSGT_VFILTER, MSGL_ERR, "[vf]remove_logo usage: remove_logo=/path/to/filter_image_file.pgm\n"); free(vf->priv); return 0; } if (((vf_priv_s *)vf->priv)->filter == NULL) { /* Error message was displayed by load_pgm(). */ free(vf->priv); return 0; } /* Create the scaled down filter image for the chroma planes. */ convert_mask_to_strength_mask(vf, ((vf_priv_s *)vf->priv)->filter); ((vf_priv_s *)vf->priv)->half_size_filter = generate_half_size_image(vf, ((vf_priv_s *)vf->priv)->filter); /* Now that we know how many masks we need (the info is in vf), we can generate the masks. */ initialize_masks(vf); /* Calculate our bounding rectangles, which determine in what region the logo resides for faster processing. */ calculate_bounding_rectangle(&((vf_priv_s *)vf->priv)->bounding_rectangle_posx1, &((vf_priv_s *)vf->priv)->bounding_rectangle_posy1, &((vf_priv_s *)vf->priv)->bounding_rectangle_posx2, &((vf_priv_s *)vf->priv)->bounding_rectangle_posy2, ((vf_priv_s *)vf->priv)->filter); calculate_bounding_rectangle(&((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx1, &((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy1, &((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posx2, &((vf_priv_s *)vf->priv)->bounding_rectangle_half_size_posy2, ((vf_priv_s *)vf->priv)->half_size_filter); vf->config=config; vf->put_image=put_image; vf->query_format=query_format; return 1;}/** * \brief Frees memory that our filter allocated. * * This is called at exit-time. */void uninit(vf_instance_t * vf){ /* Destroy our masks and images. */ destroy_pgm(((vf_priv_s *)vf->priv)->filter); destroy_pgm(((vf_priv_s *)vf->priv)->half_size_filter); destroy_masks(vf); /* Destroy our private structure that had been used to store those masks and images. */ free(vf->priv); return;}/** * \brief Meta data about our filter. */vf_info_t vf_info_remove_logo = { "Removes a tv logo based on a mask image.", "remove-logo", "Robert Edele", "", vf_open, NULL};//===========================================================================//
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?