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 + -
显示快捷键?