y4mstabilizer.c

来自「Motion JPEG编解码器源代码」· C语言 代码 · 共 678 行 · 第 1/2 页

C
678
字号
/* * $Id: y4mstabilizer.c,v 1.4 2005/05/09 22:34:09 sms00 Exp $ * * written by J. Macropol <jm@wx.gd-ais.com> *	Framework and shifting code adapted from y4mshift by Steve Schultz. *	Motion detection was adapted from yuvdenoise. * * Program to stabilize a video stream. * Works only non-interlaced yuv4mpeg streams for now. * * Usage: y4mstab [-v] [-a <alpha>] [-r <srchRadius>] * *	-v		Verbose.   Move -vs make it more verbose. *	-a <alpha>	The alpha value is a "viscosity" measure (see below). *	-r <srchRadius>	How far to look for movement. *	-s <stride>	How far apart the motion search points are. *	-n		Do not supersample the chroma to get 1-pixel shifts. *	-i		Try alternate interlaced mode (does not work yet) * * Legal <alpha> values are beween 0 and 1. * Useful <alpha> values are beween 0.7 (or so) and .95. * Higher values resist panning more at the expense of greater * shifting.  <alpha> defaults to 0.95, a fairly high value, on the theory * that you probably have some significant vibrations that need smoothing. * * The <srchRadius> defaults to 15.  Smaller values speed things up, * but won't be able to cope with large/fast movements as well. * * The <stride> defaults to 48 pixels.  Giving a larger number here will * speed the process tremendously, but makes it easier for the motion search * to be fooled by local movement.  Use a smaller number if the search seems * to be following local movements. * * No file arguments are needed since this is a filter only program. * * TODO: * 	Get alternate interlace method working. * 	Get chroma super/subsampleing working better. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <sys/types.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <limits.h>#include "yuv4mpeg.h"#include "subsample.h"extern  char    *__progname;struct    {    int		verbose;	/* Talkative flag */    int		nosuper;	/* Flag not to supersample chroma on shift */    int		rad, diam;	/* Search radius and diameter */    int		stride;		/* Stride between motion points */    float	alpha;		/* Viscosity */    float	gsX, gsY;	/* Accumulated shift */    int		ss_h, ss_v;	/* UV decimation factors */    } Stab;#define SS_H Stab.ss_h#define SS_V Stab.ss_vtypedef struct { int x, y; } vec;static void usage(void);static void alloc_yuv(u_char**, int, int);static void subsample(uint8_t*, uint8_t*, int, int);static void gmotion(u_char**, u_char**, int, int, int, vec*);static void motion(u_char*, u_char*, int, int, int, int, vec*);static void motion0(u_char*, u_char*, int, int, int, vec*);static uint32_t calc_SAD_noaccel(uint8_t*, uint8_t*, int, int);static uint32_t calc_SAD_half_noaccel(uint8_t*, uint8_t*, uint8_t*, int, int);static void calcshift(vec*, vec*);static int xround(float, int);static void doshift(u_char**, u_char**, int, int, int, vec*);static void hshift(u_char*, u_char*, int, int, int, int, int);static void vertical_shift(u_char*, int, int, int, int, int);intmain (int argc, char **argv)    {    int i, c, width, height, frames, err;    vec g, shift;    int interlace, iflag = 0, chroma_ss;    u_char *yuv0[10], *yuv1[10], *yuv2[10], *line1;    y4m_stream_info_t istream, ostream;    y4m_frame_info_t iframe;    int fdin = fileno(stdin);    Stab.rad = 15;		/* Default search radius */    Stab.stride = 48;		/* Default stride between motion points */    Stab.alpha = 0.95;		/* Default viscosity */    y4m_accept_extensions(1);    opterr = 0;    while ((c = getopt(argc, argv, "va:r:bis:n")) != EOF)	switch  (c)	    {	  case  'n':	    Stab.nosuper = 1;	    break;	  case  'i':	    iflag |= 0200;	    break;	  case  'v':	    Stab.verbose++;	    break;	  case 's':	    Stab.stride = atoi(optarg);	    break;	  case 'a':	    Stab.alpha = atof(optarg);	    break;	  case 'r':	    Stab.rad = atoi(optarg);	    break;	  case    '?':	  case    'h':	  default:	    usage();	    }    /* Initialize your input stream */    y4m_init_stream_info(&istream);    y4m_init_frame_info(&iframe);    err = y4m_read_stream_header(fdin, &istream);    if (err != Y4M_OK)	mjpeg_error_exit1("Input stream error: %s\n", y4m_strerr(err));    if (y4m_si_get_plane_count(&istream) != 3)	mjpeg_error_exit1("Only 3 plane formats supported");    switch (interlace = y4m_si_get_interlace(&istream))	{      case Y4M_ILACE_NONE:	break;      case Y4M_ILACE_TOP_FIRST:      case Y4M_ILACE_BOTTOM_FIRST:	interlace |= iflag;	break;      case Y4M_ILACE_MIXED:	mjpeg_error_exit1("No mixed-interlaced streams!\n");      default:	mjpeg_error_exit1("Unknown interlace!\n");	}    chroma_ss = y4m_si_get_chroma(&istream);    SS_H = y4m_chroma_ss_x_ratio(chroma_ss).d;    SS_V = y4m_chroma_ss_y_ratio(chroma_ss).d;    switch (chroma_ss)	{      case Y4M_CHROMA_420JPEG:      case Y4M_CHROMA_420MPEG2:      case Y4M_CHROMA_444:	break;      case Y4M_CHROMA_MONO:	 mjpeg_error_exit1("MONO (1 plane) chroma not supported!\n");      case Y4M_CHROMA_444ALPHA:	 mjpeg_error_exit1("444ALPHA (4 plane) chroma not supported!\n");      default:	if (!Stab.nosuper)	    mjpeg_log(LOG_INFO, "Cannot supersample %s chroma",		y4m_chroma_description(chroma_ss));	Stab.nosuper = 1;	break;	}    width = y4m_si_get_width(&istream);    height = y4m_si_get_height(&istream);    if (Stab.verbose)	y4m_log_stream_info(LOG_INFO, "", &istream);    /* Initialize output stream */    y4m_init_stream_info(&ostream);    y4m_copy_stream_info(&ostream, &istream);    y4m_write_stream_header(fileno(stdout), &ostream);    /* Allocate our frame arrays */    alloc_yuv(yuv0, height, width);    alloc_yuv(yuv1, height, width);    alloc_yuv(yuv2, height, width);    /* Set up the search diameter. */    Stab.diam = Stab.rad + Stab.rad + 1;    /* Fetch 1st frame - nothing to compare, so just copy it out.     * (Note that this is not strictly true if we have interlace     * and use the -i modified mode where we treat the fields separately.     * But I am *SURE* nobody will notice...  err...) */    frames = 1;    if (y4m_read_frame(fdin,&istream,&iframe,yuv0) != Y4M_OK)	goto endit;    subsample(yuv0[0], yuv0[3], width, height);    subsample(yuv0[3], yuv0[4], width/2, height/2);    y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv0);    for (; y4m_read_frame(fdin,&istream,&iframe,yuv1) == Y4M_OK; frames++)	{	if ((Stab.verbose > 1) || (Stab.verbose && ((frames % 100) == 0)))	    mjpeg_log(LOG_INFO, "Frame %d", frames);	subsample(yuv1[0], yuv1[3], width, height);	subsample(yuv1[3], yuv1[4], width/2, height/2);	switch (interlace)	    {	  /* Easy - non-interlaced */	  case Y4M_ILACE_NONE:	    /* Find out how much this frame has changed from the previous */	    gmotion(yuv0, yuv1, width, width, height, &g);	    /* Figure out how much to shift this frame to compensate */	    calcshift(&g, &shift);	    /* If nothing to shift, just dump this frame and continue */	    if ((shift.x == 0) && (shift.y == 0))		y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv1);	    /* Else shift frame & write it out */	    else		{		doshift(yuv1, yuv2, height, width, width, &shift);		y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv2);		}	    break;	  /* Default interlaced method.	   * Treat fields as one wide field & shift both the same.  */	  case Y4M_ILACE_TOP_FIRST    | 0:	  case Y4M_ILACE_BOTTOM_FIRST | 0:	    /* Find out how much this frame has changed from the previous */	    gmotion(yuv0, yuv1, width*2, width*2, height/2, &g);	    /* Figure out how much to shift this frame to compensate */	    calcshift(&g, &shift);	    /* If nothing to shift, just dump this frame and continue */	    if ((shift.x == 0) && (shift.y == 0))		y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv1);	    /* Shift the fields separately & write the frame */	    else		{		doshift(yuv1,   yuv2,   height/2, width,width*2,&shift);		doshift(yuv1+5, yuv2+5, height/2, width,width*2,&shift);		y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv2);		}	    break;	    /* Alternate interlaced method:	     * Treat fields as separate frames, one half pixel apart vertically. */	  case Y4M_ILACE_TOP_FIRST    | 0200:	    /* Last bottom half -> Top half */	    gmotion(yuv0+5, yuv1, width, width*2, height/2, &g);	    g.y += 0.5;	    calcshift(&g, &shift);	    doshift(yuv1, yuv2, height/2, width, width*2, &shift);	    /* Top half -> Bottom half */	    gmotion(yuv1, yuv1+5, width, width*2, height/2, &g);	    g.y -= 0.5;	    calcshift(&g, &shift);	    doshift(yuv1+5, yuv2+5, height/2, width, width*2, &shift);	    y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv2);	    break;	  case Y4M_ILACE_BOTTOM_FIRST | 0200:	    /* Last top half -> Bottom half */	    gmotion(yuv0, yuv1+5, width, width*2, height/2, &g);	    g.y -= 0.5;	    calcshift(&g, &shift);	    doshift(yuv1+5, yuv2+5, height/2, width, width*2, &shift);	    /* Bottom half -> Top half */	    gmotion(yuv1+5, yuv1, width, width*2, height/2, &g);	    g.y += 0.5;	    calcshift(&g, &shift);	    doshift(yuv1, yuv2, height/2, width, width*2, &shift);	    y4m_write_frame(fileno(stdout), &ostream, &iframe, yuv2);	    break;	    }	/* swap yuv0 and yuv1, so yuv1 becomes the old reference frame	 * for the motion search. */	for (i = 0; i < 10; i++)	    { line1 = yuv0[i]; yuv0[i] = yuv1[i]; yuv1[i] = line1; }	}    /* All done - close out the streams and exit */endit:    y4m_fini_frame_info(&iframe);    y4m_fini_stream_info(&istream);    y4m_fini_stream_info(&ostream);    exit(0);    }static voidusage (void)        {	fputs("Program to stabilize a video stream.\n""Works only non-interlaced yuv4mpeg streams for now.\n""\n""Usage: y4mstab [-v] [-a <alpha>] [-r <srchRadius>]\n""\n""	-v		Verbose.   Move -vs make it more verbose.\n""	-a <alpha>	A \"viscosity\" measure (see below).\n""	-r <srchRadius>	How far to look for movement.\n""	-s <stride>	How far apart the motion search points are.\n""	-n		Do not supersample the chroma to get 1-pixel shifts.\n""	-i		Try alternate interlaced mode (does not work yet)\n""\n""Legal <alpha> values are beween 0 and 1.\n""Useful <alpha> values are beween 0.7 (or so) and .95.\n""Higher values resist panning more at the expense of greater\n""shifting.  <alpha> defaults to 0.95, a fairly high value, on the theory\n""that you probably have some significant vibrations that need smoothing.\n""\n""The <srchRadius> defaults to 15.  Smaller values speed things up,\n""but won't be able to cope with large/fast movements as well.\n""\n""The <stride> defaults to 48 pixels.  Giving a larger number here will\n""speed the process tremendously, but makes it easier for the motion search\n""to be fooled by local movement.  Use a smaller number if the search seems\n""to be following local movements.\n""\n""No file arguments are needed since this is a filter only program.\n""\n""This program presently works best when given 444, deinterlaced input.\n""Very good results can be obtained with the following pipeline:\n""    ... | yuvdeinterlace | \\\n""	   y4mscaler -v 0 -O sar=src -O chromass=444 | \\\n""	   y4mstab | \\\n""	   y4mscaler -v 0 -O sar=src -O chromass=420_MPEG2 | ...\n", stderr);exit(1);}static voidalloc_yuv (u_char **yuv, int h, int w){int len = h * w;int uvlen = Stab.nosuper ? (len / (SS_H * SS_V)) : len;yuv[0] = malloc(len);if (yuv[0] == NULL)mjpeg_error_exit1(" malloc(%d) failed\n", len);yuv[1] = malloc(uvlen);if (yuv[1] == NULL)mjpeg_error_exit1(" malloc(%d) failed\n", uvlen);yuv[2] = malloc(uvlen);if (yuv[2] == NULL)

⌨️ 快捷键说明

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