/* * yuvdeinterlace.c * deinterlace 2004 Mark Heath * converts interlaced source material into progressive by doubling the frame rate and adaptively interpolating required pixels. * * based on code: * Copyright (C) 2002 Alfonso Garcia-PatiÃ’o Barbolani * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * gcc yuvdeinterlace.c -I/sw/include/mjpegtools -L/sw/lib -lmjpegutils -o yuvdeinterlace */ #ifdef HAVE_CONFIG_H #include "config.h" #endif // minimum number of pixels needed to detect interlace. #define PIXELS 4 #include #include #include #include #include #include #include #include "yuv4mpeg.h" #include "mpegconsts.h" #define YUVDE_VERSION "0.1" typedef struct frame_dimensions { size_t plane_length_luma; size_t plane_length_chroma; size_t plane_width_luma; size_t plane_width_chroma; size_t plane_height_luma; size_t plane_height_chroma; size_t chroma_width_ratio; size_t chroma_height_ratio; } frame_dimensions; static void print_usage() { fprintf (stderr, "usage: yuvdeinterlace [-v] [-It|b] [-f] [-h]\n" "yuvdeinterlace deinterlaces source material by\n" "doubling the frame rate and interpolating the interlaced scanlines.\n" "\n" "\t -I force interlace mode\n" /* "\t -n half vertical height (not duplicating fields)\n" */ "\t -i interlace (rather than deinterlace -n has no meaning)\n" "\t -f force full frame deinterlacing\n" "\t -v Verbosity degree : 0=quiet, 1=normal, 2=verbose/debug\n" "\t -h print this help\n" ); } // Allocate a uint8_t frame int chromalloc(uint8_t *m[3],y4m_stream_info_t *sinfo) { int fs,cfs; fs = y4m_si_get_plane_length(sinfo,0); cfs = y4m_si_get_plane_length(sinfo,1); m[0] = (uint8_t *)malloc(fs); m[1] = (uint8_t *)malloc(cfs); m[2] = (uint8_t *)malloc(cfs); if( !m[0] || !m[1] || !m[2]) { return -1; } else { return 0; } } //Copy a uint8_t frame int chromacpy(uint8_t *m[3],uint8_t *n[3],frame_dimensions *fd) { int fs,cfs; fs = fd->plane_length_luma; cfs = fd->plane_length_chroma; memcpy (m[0],n[0],fs); memcpy (m[1],n[1],cfs); memcpy (m[2],n[2],cfs); } // returns the opposite field ordering int invert_order(int f) { switch (f) { case Y4M_ILACE_TOP_FIRST: return Y4M_ILACE_BOTTOM_FIRST; case Y4M_ILACE_BOTTOM_FIRST: return Y4M_ILACE_TOP_FIRST; case Y4M_ILACE_NONE: return Y4M_ILACE_NONE; default: return Y4M_UNKNOWN; } } int int_detect (int x, int y,uint8_t *m[3],frame_dimensions *fd) { uint8_t luma[PIXELS]; int hp = PIXELS/2; int i,w,h; int hfd=0 ,lfd=0; int ar,ai,br,bi,cr,ci,dr,di; int a,b,c,d; w = fd->plane_width_luma; h = fd->plane_height_luma; // Unroll this loop // read the pixels above and below the target pixel. for(i=0; i=h)) luma[i]=128; else luma[i] = m[0][(y+i-hp)*w+x]; // perform a 4 point DFT // taking only the components we need //ar = luma[0] + luma[1] + luma[2] + luma[3]; // ai = 0; br = luma[0] - luma[2]; bi = luma[3] - luma[1]; cr = luma[0] - luma[1] + luma[2] - luma[3]; ci = 0; // dr = br; // di = -bi; // ar >>= 1; // ai >>= 1; br >>= 1; bi >>= 1; cr >>= 1; // ci >>= 1; // dr >>= 1; // di >>= 1; // we are not going to perform square root // a = ar * ar; b = br * br + bi * bi; c = cr * cr; // d = sqrt ( dr * dr + di * di); // printf ("%d %d\n",c,b); /* if (c>127) { printf ("Interlace luma values: %d,%d,%d,%d DCT coefficients %d-%d\n", luma[0],luma[1],luma[2],luma[3],c,b); } */ // if the power of the ac frequency is heigher than the half frequency // find simple alternating pattern and areas where the AC is strong. if (c > b && c > 12 && ( (luma[0] < luma[1] && luma[2] < luma[1] && luma[2] < luma[3] ) || (luma[0] > luma[1] && luma[2] > luma[1] && luma[2] > luma[3] )) ) { // printf ("%d %d\n",c,b); return 1; } return 0; // trial and error // c is the high frequency amount, so above a threshold it should trigger // however b is the "edge" amount which can cause large c values. //return (c>128 && c > (b<<2)); return (c>12 && c > (b<<2)); } static void mark_deint_pixels (int x, int y,uint8_t *m[3],uint8_t *n[3],frame_dimensions *fd) { int w,h,cw,ch,cwr,chr; h = fd->plane_height_luma; w = fd->plane_width_luma; ch = fd->plane_height_chroma; cw = fd->plane_width_chroma; cwr = w / cw; chr = h / ch; m[0][x+y*w] = 128; m[1][x/cwr+y/chr*cw] = 128; m[2][x/cwr+y/chr*cw] = 192; } uint8_t linear_interpolate (uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3) { int r = v2 - v1; // Assuming halfway between the pixels. return (r >> 1) + v1; } // Perform a cubic interpolation uint8_t cubic_interpolate (uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3) { int p = (v3 - v2) - (v0 - v1); int q = (v0 - v1) - p; int r = v2 - v0; // Assuming halfway between the pixels. int tot = (p >> 3) + (q >> 2) + (r >> 1) + v1; if (tot > 255) { // fprintf (stderr,"pixel overflow %d (%d,%d,%d,%d)\n",tot,v0,v1,v2,v3); tot = 255; } if (tot < 0) { // fprintf (stderr,"pixel overflow %d (%d,%d,%d,%d)\n",tot,v0,v1,v2,v3); tot = 0; } return tot; } // wrapper for the scaling algorithm uint8_t scalar_interpolate (uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3) { return cubic_interpolate(v0,v1,v2,v3); } static void deint_pixels (int x, int y,uint8_t *m[3],uint8_t *n[3],frame_dimensions *fd) { int w,h,cw,ch,cwr,chr,xcwr,ychr,ychrn,ychrp,ychrcw; int ychrn2,ychrp2; int tluma, tchromu,tchromv; int chroma_posp,chroma_posn; int chroma_posp2,chroma_posn2; h = fd->plane_height_luma; w = fd->plane_width_luma; ch = fd->plane_height_chroma; cw = fd->plane_width_chroma; // most common values for cwr and chr are 1,2 or 4 cwr = fd->chroma_width_ratio; chr = fd->chroma_height_ratio; // optimize for these common values if ( cwr == 1 ) { xcwr = x; } else if ( cwr == 2) { xcwr = x >> 1; } else if (cwr == 4) { xcwr = x >> 2; } else { xcwr = x / cwr; } if (chr == 1) { ychr = y; ychrn = y-1; ychrp = y+1; ychrn2 = y-3; ychrp2 = y+3; } else if (chr == 2) { ychr = y >> 1; ychrn = (y-1) >> 1; ychrp = (y+1) >> 1; ychrn2 = (y-3) >> 1; ychrp2 = (y+3) >> 1; } else if (chr == 4) { ychr = y >> 2; ychrn = (y-1) >> 2; ychrp = (y+1) >> 2; ychrn2 = (y-3) >> 2; ychrp2 = (y+3) >> 2; } else { ychr = y / chr; ychrn = (y-1) / chr; ychrp = (y+1) / chr; ychrn2 = (y-3) / chr; ychrp2 = (y+3) / chr; } ychrcw = ychr * cw; chroma_posn = xcwr + ychrn * cw; chroma_posp = xcwr + ychrp * cw; chroma_posn2 = xcwr + ychrn2 * cw; chroma_posp2 = xcwr + ychrp2 * cw; // 4 point interpolate if (y==0) { tluma = scalar_interpolate(16,16,n[0][x+w],n[0][x+w*3]); tchromu = scalar_interpolate(128,128,n[1][xcwr + cw],n[1][xcwr + cw * 3]); tchromv = scalar_interpolate(128,128,n[2][xcwr + cw],n[1][xcwr + cw * 3]); } else if ((y == 1) || (y == 2)) { tluma = scalar_interpolate(16,n[0][x+w*(y-1)],n[0][x+w*(y+1)],n[0][x+w*(y+3)]); tchromu = scalar_interpolate(128,n[1][chroma_posn],n[1][chroma_posp],n[1][chroma_posp2]); tchromv = scalar_interpolate(128,n[2][chroma_posn],n[2][chroma_posp],n[2][chroma_posp2]); } else if (((y+3)==h) || ((y+2)==h)) { tluma = scalar_interpolate(n[0][x+w*(y-3)],n[0][x+w*(y-1)],n[0][x+w*(y+1)],16); tchromu = scalar_interpolate(n[1][chroma_posn2],n[1][chroma_posn],n[1][chroma_posp],128); tchromv = scalar_interpolate(n[2][chroma_posn2],n[2][chroma_posn],n[2][chroma_posp],128); } else if ((y+1) == h) { tluma = scalar_interpolate(n[0][x+w*(y-3)],n[0][x+w*(y-1)],16,16); tchromu = scalar_interpolate(n[1][chroma_posn2],n[1][chroma_posn],128,128); tchromv = scalar_interpolate(n[2][chroma_posn2],n[2][chroma_posn],128,128); } else { tluma = scalar_interpolate(n[0][x+w*(y-3)],n[0][x+w*(y-1)],n[0][x+w*(y+1)],n[0][x+w*(y+3)]); tchromu = scalar_interpolate(n[1][chroma_posn2],n[1][chroma_posn],n[1][chroma_posp],n[1][chroma_posp2]); tchromv = scalar_interpolate(n[2][chroma_posn2],n[2][chroma_posn],n[2][chroma_posp],n[2][chroma_posp2]); } if (chr == 1 && cwr == 1) { m[0][x+ychrcw] = tluma; } else { m[0][x+y*w] = tluma; } m[1][xcwr+ychrcw] = tchromu; m[2][xcwr+ychrcw] = tchromv; } static void deint_frame (uint8_t *l[3], uint8_t *m[3], uint8_t *n[3], frame_dimensions *fd , int full) { // detect and interpolate // this detection algorithm takes PIXELS vertical pixels and looks for the classic interlace pattern // if interlace is detected the *missing* pixels are interpolated. int x,y,hp; int w,h,cw,ch; int mark = 0; uint8_t luma[PIXELS]; h = fd->plane_height_luma; w = fd->plane_width_luma; chromacpy(m,n,fd); chromacpy(l,n,fd); for (x=0; xplane_height_luma = y4m_si_get_plane_height(si,0) ; fd->plane_width_luma = y4m_si_get_plane_width(si,0); fd->plane_height_chroma = y4m_si_get_plane_height(si,1) ; fd->plane_width_chroma = y4m_si_get_plane_width(si,1); fd->plane_length_luma = y4m_si_get_plane_length(si,0); fd->plane_length_chroma = y4m_si_get_plane_length(si,1); fd->chroma_width_ratio = fd->plane_width_luma / fd->plane_width_chroma; fd->chroma_height_ratio = fd->plane_height_luma / fd->plane_height_chroma; } static void deinterlace(int fdIn, y4m_stream_info_t *inStrInfo, int fdOut, y4m_stream_info_t *outStrInfo, int full ) { y4m_frame_info_t in_frame ; uint8_t *yuv_data[3] ; uint8_t *yuv_tdata[3] ; uint8_t *yuv_bdata[3] ; int read_error_code ; int write_error_code ; int interlaced = Y4M_UNKNOWN; //=Y4M_ILACE_NONE for not-interlaced scaling, =Y4M_ILACE_TOP_FIRST or Y4M_ILACE_BOTTOM_FIRST for interlaced scaling struct frame_dimensions fdim; set_dimensions (&fdim, outStrInfo); // Allocate memory for the YUV channels interlaced = y4m_si_get_interlace(inStrInfo); if (chromalloc(yuv_data,inStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if (chromalloc(yuv_tdata,outStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if (chromalloc(yuv_bdata,outStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); /* Initialize counters */ write_error_code = Y4M_OK ; y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data ); while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) { // de interlace frame. if (read_error_code == Y4M_OK) { // de-interlace one field to odata and the other in place deint_frame(yuv_bdata, yuv_tdata, yuv_data, &fdim, full); if (interlaced == Y4M_ILACE_TOP_FIRST) { write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_tdata ); write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_bdata ); } else { write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_bdata ); write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_tdata ); } } y4m_fini_frame_info( &in_frame ); y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data ); } // Clean-up regardless an error happened or not y4m_fini_frame_info( &in_frame ); free( yuv_data[0] ); free( yuv_data[1] ); free( yuv_data[2] ); free( yuv_tdata[0] ); free( yuv_tdata[1] ); free( yuv_tdata[2] ); free( yuv_bdata[0] ); free( yuv_bdata[1] ); free( yuv_bdata[2] ); if( read_error_code != Y4M_ERR_EOF ) mjpeg_error_exit1 ("Error reading from input stream!"); if( write_error_code != Y4M_OK ) mjpeg_error_exit1 ("Error writing output stream!"); } static void depro( int fdIn , y4m_stream_info_t *inStrInfo , int fdOut , y4m_stream_info_t *outStrInfo , int proc ) { y4m_frame_info_t in_frame ; uint8_t *yuv_data[3] ; uint8_t *yuv_o1data[3] ; uint8_t *yuv_o2data[3] ; int frame_data_size ; int read_error_code ; int write_error_code ; int interlaced = -1; //=Y4M_ILACE_NONE for not-interlaced scaling, =Y4M_ILACE_TOP_FIRST or Y4M_ILACE_BOTTOM_FIRST for interlaced scaling int w,h,y; // Allocate memory for the YUV channels interlaced = y4m_si_get_interlace(outStrInfo); h = y4m_si_get_height(inStrInfo) ; w = y4m_si_get_width(inStrInfo); frame_data_size = h * w; if (chromalloc(yuv_data,inStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); h = y4m_si_get_height(outStrInfo) ; w = y4m_si_get_width(outStrInfo); frame_data_size = h * w; if (chromalloc(yuv_o1data,inStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if (chromalloc(yuv_o2data,inStrInfo)) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); /* Initialize counters */ write_error_code = Y4M_OK ; y4m_init_frame_info( &in_frame ); if (interlaced == Y4M_ILACE_BOTTOM_FIRST ) { read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o2data ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o1data ); } else if (interlaced == Y4M_ILACE_TOP_FIRST) { read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o1data ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o2data ); } else { mjpeg_warn ("Cannot determine interlace order (assuming top first)\n"); /* assume top first */ read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o1data ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o2data ); } while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) { if (read_error_code == Y4M_OK) { // interlace frame. for (y=0; y 2) mjpeg_error_exit1 ("Verbose level must be [0..2]"); break; case 'h': case '?': print_usage (argv); return 0 ; break; case 'f': fullframe = -1; break; case 'I': switch (optarg[0]) { case 't': yuv_interlacing = Y4M_ILACE_TOP_FIRST; break; case 'b': yuv_interlacing = Y4M_ILACE_BOTTOM_FIRST; break; default: mjpeg_error("Unknown value for interlace: '%c'", optarg[ 0]); return -1; break; } break; case 't': top_field = 1 ; break; case 'b': bottom_field = 1; break; case 'c': pro_chroma = 1; break; case 'n': double_height = 0; break; case 'i': ilace = 1; break; } } if ((bottom_field==1) && (top_field==1)) { fprintf (stderr,"top field and bottom field specified\n"); exit(1); } // mjpeg tools global initialisations mjpeg_default_handler_verbosity (verbose); // Initialize input streams y4m_init_stream_info (&in_streaminfo); y4m_init_stream_info (&out_streaminfo); // *************************************************************** // Get video stream informations (size, framerate, interlacing, aspect ratio). // The streaminfo structure is filled in // *************************************************************** // INPUT comes from stdin, we check for a correct file header if (y4m_read_stream_header (fdIn, &in_streaminfo) != Y4M_OK) mjpeg_error_exit1 ("Could'nt read YUV4MPEG header!"); // Check input parameters height = y4m_si_get_height (&in_streaminfo); frame_rate = y4m_si_get_framerate( &in_streaminfo ); interlaced = y4m_si_get_interlace (&in_streaminfo); if ((height%2) && (ilace == 0)) mjpeg_error_exit1("material is not even frame height"); if ((interlaced == Y4M_ILACE_NONE) && (ilace == 0)) mjpeg_warn("source material not interlaced"); if ((interlaced != Y4M_ILACE_NONE) && (ilace == 1)) mjpeg_warn("source material is interlaced"); if (ilace == 1) { frame_rate.d *= 2; } if (ilace == 0) { if (yuv_interlacing != Y4M_UNKNOWN) y4m_si_set_interlace(&in_streaminfo, yuv_interlacing); frame_rate.n *= 2 ; interlaced = Y4M_ILACE_NONE; } // Prepare output stream // De interlacer doubles framerate but halves vertical resolution. y4m_copy_stream_info( &out_streaminfo, &in_streaminfo ); // Information output mjpeg_info ("yuvdeinterlace (version " YUVDE_VERSION ") is a general deinterlace/interlace utility for yuv streams"); mjpeg_info ("(C) 2005 Mark Heath "); mjpeg_info ("yuvdeinterlace -h for help"); y4m_si_set_framerate( &out_streaminfo, frame_rate ); y4m_si_set_height (&out_streaminfo, height); y4m_si_set_interlace(&out_streaminfo, interlaced); y4m_write_stream_header(fdOut,&out_streaminfo); /* in that function we do all the important work */ if (ilace == 0) deinterlace( fdIn, &in_streaminfo, fdOut, &out_streaminfo, fullframe); /* this has been replaced by yuvafps */ if (ilace == 1) depro( fdIn, &in_streaminfo, fdOut,&out_streaminfo,pro_chroma); y4m_fini_stream_info (&in_streaminfo); y4m_fini_stream_info (&out_streaminfo); return 0; } /* * Local variables: * tab-width: 8 * indent-tabs-mode: nil * End: */