diff options
Diffstat (limited to 'libhb/decomb.c')
-rw-r--r-- | libhb/decomb.c | 821 |
1 files changed, 689 insertions, 132 deletions
diff --git a/libhb/decomb.c b/libhb/decomb.c index e984aa6cb..b9baa963a 100644 --- a/libhb/decomb.c +++ b/libhb/decomb.c @@ -2,8 +2,8 @@ This file is part of the HandBrake source code. Homepage: <http://handbrake.fr/>. - It may be used under the terms of the GNU General Public License. - + It may be used under the terms of the GNU General Public License. + The yadif algorithm was created by Michael Niedermayer. Tritical's work inspired much of the comb detection code: http://web.missouri.edu/~kes25c/ @@ -11,8 +11,8 @@ /***** Parameters: - Mode : Spatial metric : Motion thresh : Spatial thresh : Block thresh : - Block width : Block height + Mode : Spatial metric : Motion thresh : Spatial thresh : Mask Filter Mode : + Block thresh : Block width : Block height Appended for EEDI2: Magnitude thresh : Variance thresh : Laplacian thresh : Dilation thresh : @@ -20,9 +20,12 @@ Appended for EEDI2: Plus: Parity - + Defaults: - 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1 + 391:2:3:3:2:40:16:16:10:20:20:4:2:50:24:1:-1 + +Original "Faster" settings: + 7:2:6:9:1:80:16:16:10:20:20:4:2:50:24:1:-1 *****/ #define MODE_YADIF 1 // Use yadif @@ -31,8 +34,14 @@ Defaults: #define MODE_EEDI2 8 // Use EEDI2 interpolation #define MODE_MCDEINT 16 // Post-process with mcdeint #define MODE_MASK 32 // Output combing masks instead of pictures +#define MODE_GAMMA 128 // Scale gamma when decombing +#define MODE_FILTER 256 // Filter combing mask +#define MODE_COMPOSITE 512 // Overlay combing mask onto picture + +#define FILTER_CLASSIC 1 +#define FILTER_ERODE_DILATE 2 -/***** +/***** These modes can be layered. For example, Yadif (1) + EEDI2 (8) = 9, which will feed EEDI2 interpolations to yadif. @@ -136,13 +145,16 @@ struct hb_filter_private_s // Decomb parameters int mode; + int filter_mode; int spatial_metric; int motion_threshold; int spatial_threshold; int block_threshold; int block_width; int block_height; - + + float gamma_lut[256]; + // EEDI2 parameters int magnitude_threshold; int variance_threshold; @@ -155,7 +167,7 @@ struct hb_filter_private_s int parity; int tff; - + int yadif_ready; int mcdeint_mode; @@ -168,8 +180,14 @@ struct hb_filter_private_s uint8_t * ref[4][3]; int ref_stride[3]; - /* Make a buffer to store a comb mask. */ + /* Make buffers to store a comb masks. */ uint8_t * mask[3]; + uint8_t * mask_filtered[3]; + uint8_t * mask_temp[3]; + int mask_box_x; + int mask_box_y; + uint8_t mask_box_color; + uint8_t * eedi_half[4][3]; uint8_t * eedi_full[5][3]; @@ -177,19 +195,19 @@ struct hb_filter_private_s int * cy2; int * cxy; int * tmpc; - + AVPicture pic_in; AVPicture pic_out; hb_buffer_t * buf_out[2]; hb_buffer_t * buf_settings; - + int cpu_count; hb_thread_t ** yadif_threads; // Threads for Yadif - one per CPU hb_lock_t ** yadif_begin_lock; // Thread has work hb_lock_t ** yadif_complete_lock; // Thread has completed work yadif_arguments_t *yadif_arguments; // Arguments to thread for work - + hb_thread_t ** decomb_threads; // Threads for comb detection - one per CPU hb_lock_t ** decomb_begin_lock; // Thread has work hb_lock_t ** decomb_complete_lock; // Thread has completed work @@ -232,7 +250,7 @@ int cubic_interpolate_pixel( int y0, int y1, int y2, int y3 ) /* From http://www.neuron2.net/library/cubicinterp.html */ int result = ( y0 * -3 ) + ( y1 * 23 ) + ( y2 * 23 ) + ( y3 * -3 ); result /= 40; - + if( result > 255 ) { result = 255; @@ -241,7 +259,7 @@ int cubic_interpolate_pixel( int y0, int y1, int y2, int y3 ) { result = 0; } - + return result; } @@ -259,7 +277,7 @@ static void cubic_interpolate_line( uint8_t *dst, { int a, b, c, d; a = b = c = d = 0; - + if( y >= 3 ) { /* Normal top*/ @@ -278,12 +296,12 @@ static void cubic_interpolate_line( uint8_t *dst, a = cur[+refs]; b = cur[+refs]; } - + if( y <= ( pv->height[plane] - 4 ) ) { /* Normal bottom*/ c = cur[+refs]; - d = cur[3*refs]; + d = cur[3*refs]; } else if( y == ( pv->height[plane] - 3 ) || y == ( pv->height[plane] - 2 ) ) { @@ -297,45 +315,77 @@ static void cubic_interpolate_line( uint8_t *dst, c = cur[-refs]; d = cur[-refs]; } - + dst[0] = cubic_interpolate_pixel( a, b, c, d ); - + dst++; cur++; } } +void draw_mask_box( hb_filter_private_t * pv ) +{ + int x = pv->mask_box_x; + int y = pv->mask_box_y; + int box_width = pv->block_width; + int box_height = pv->block_height; + int stride = pv->ref_stride[0]; + uint8_t * mskp = ( pv->mode & MODE_FILTER ) ? pv->mask_filtered[0] : pv->mask[0]; + + + int block_x, block_y; + for( block_x = 0; block_x < box_width; block_x++) + { + mskp[y*stride+x+block_x] = 128; + mskp[(y+box_height)*stride+x+block_x] = 128; + } + + for( block_y = 0; block_y < box_height; block_y++) + { + mskp[stride*(y+block_y)+x] = 128; + mskp[stride*(y+block_y) + x + box_width] = 128; + } +} + void apply_mask_line( uint8_t * srcp, uint8_t * mskp, int width ) { int x; - + for( x = 0; x < width; x++ ) { if( mskp[x] == 255 ) { srcp[x] = 255; } + if( mskp[x] == 128 ) + { + srcp[x] = 128; + } } } void apply_mask( hb_filter_private_t * pv ) { + + /* draw_boxes */ + draw_mask_box( pv ); + int plane, height; - + for( plane = 0; plane < 3; plane++ ) { uint8_t * srcp = ( pv->mode & MODE_MCDEINT ) ? pv->pic_in.data[plane] : pv->pic_out.data[plane]; - uint8_t * mskp = pv->mask[plane]; - + uint8_t * mskp = ( pv->mode & MODE_FILTER ) ? pv->mask_filtered[plane] : pv->mask[plane]; + for( height = 0; height < pv->height[plane]; height++ ) { - if( pv->mode == MODE_MASK && plane == 0 ) + if( !(pv->mode & MODE_COMPOSITE) && plane == 0 ) { memcpy( srcp, mskp, pv->width[plane] ); } - else if( pv->mode == MODE_MASK ) + else if( !(pv->mode & MODE_COMPOSITE) ) { memset( srcp, 128, pv->width[plane] ); } @@ -392,7 +442,7 @@ static void get_ref( uint8_t ** pic, hb_filter_private_t * pv, int frm ) const uint8_t * ref = pv->ref[frm][i]; int w = pv->width[i]; int ref_stride = pv->ref_stride[i]; - + int y; for( y = 0; y < pv->height[i]; y++ ) { @@ -423,7 +473,7 @@ int blend_filter_pixel( int up2, int up1, int current, int down1, int down2 ) { result = 0; } - + return result; } @@ -440,13 +490,13 @@ static void blend_filter_line( uint8_t *dst, for( x = 0; x < w; x++) { int a, b, c, d, e; - + a = cur[-2*refs]; b = cur[-refs]; c = cur[0]; d = cur[+refs]; e = cur[2*refs]; - + if( y == 0 ) { /* First line, so A and B don't exist.*/ @@ -469,7 +519,7 @@ static void blend_filter_line( uint8_t *dst, d = cur[0]; e = cur[0]; } - + dst[0] = blend_filter_pixel( a, b, c, d, e ); dst++; @@ -477,6 +527,307 @@ static void blend_filter_line( uint8_t *dst, } } +void erode_combing_mask( hb_filter_private_t * pv ) +{ + //Take in mask, output to mask_temp + int x, y, k; + + uint8_t * cur; + uint8_t * dst; + + int count; + + int erosion_threshold = 2; + + int ref; + for( k = 0; k < 1; k++ ) + { + ref = pv->ref_stride[k]; + + for( y = 1; y < pv->height[k] -1; y++ ) + { + cur = &pv->mask_temp[k][y*ref]; + dst = &pv->mask_filtered[k][y*ref]; + + for( x = 1; x < pv->width[k]-1; x++ ) + { + if( cur[0] == 0 ) + { + dst[0] = 0; + cur++; + dst++; + continue; + } + + count = 0; + if( cur[-ref-1] == 255 ) + ++count; + if( cur[-ref] == 255 ) + ++count; + if( cur[-ref+1] == 255 ) + ++count; + if( cur[-1] == 255 ) + ++count; + if( cur[+1] == 255 ) + ++count; + if( cur[ref-1] == 255 ) + ++count; + if( cur[ref] == 255 ) + ++count; + if( cur[ref+1] == 255 ) + ++count; + + if( count < erosion_threshold ) + dst[0] = 0; + else + dst[0] = 255; + + cur++; + dst++; + } + } + } +} + +void dilate_combing_mask( hb_filter_private_t * pv ) +{ + //Take in mask_temp, output to mask + int x, y, k; + + uint8_t * curp; + uint8_t * cur; + uint8_t * curn; + uint8_t * dst; + + int count; + + int dilation_threshold = 4; + + int ref; + for( k = 0; k < 1; k++ ) + { + ref = pv->ref_stride[k]; + + for( y = 1; y < pv->height[k] -1; y++ ) + { + curp = &pv->mask_filtered[k][(y-1)*ref]; + cur = &pv->mask_filtered[k][y*ref]; + curn = &pv->mask_filtered[k][(y+1)*ref]; + dst = &pv->mask_temp[k][y*ref]; + + for( x = 1; x < pv->width[k]-1; x++ ) + { + if( cur[0] == 255 ) + { + dst[0] = 255; + curp++; + cur++; + curn++; + dst++; + continue; + } + + count = 0; + if( curp[-1] == 255 ) + ++count; + if( curp[0] == 255 ) + ++count; + if( curp[+1] == 255 ) + ++count; + if( cur[-1] == 255 ) + ++count; + if( cur[+1] == 255 ) + ++count; + if( curn[-1] == 255 ) + ++count; + if( curn[0] == 255 ) + ++count; + if( curn[+1] == 255 ) + ++count; + + if( count >= dilation_threshold ) + dst[0] = 255; + else + dst[0] = 0; + + curp++; + cur++; + curn++; + dst++; + } + } + } +} + + +void filter_combing_mask( hb_filter_private_t * pv ) +{ + int x, y, k; + + uint8_t * curp; + uint8_t * cur; + uint8_t * curn; + uint8_t * dst; + + int h_count, v_count; + + int ref; + for( k = 0; k < 1; k++ ) + { + ref = pv->ref_stride[k]; + + for( y = 0; y < pv->height[k] -1; y++ ) + { + curp = &pv->mask[k][(y-1)*ref]; + cur = &pv->mask[k][y*ref]; + curn = &pv->mask[k][(y+1)*ref]; + dst = (pv->filter_mode == FILTER_CLASSIC ) ? &pv->mask_filtered[k][y*ref] : &pv->mask_temp[k][y*ref] ; + + for( x = 0; x < pv->width[k]-1; x++ ) + { + + h_count = v_count = 0; + if( x == 0 ) + { + if( cur[0] == 255 && cur[1] == 255 ) + h_count++; + } + else if( x == pv->width[k]-1 ) + { + if( cur[-1] == 255 && cur[0] == 255 ) + h_count++; + } + else + { + if(cur[-1] == 255 && cur[0] == 255 && cur[1] == 255 ) + h_count++; + } + + if( y == 0 ) + { + if( cur[0] == 255 && curn[0] == 255 ) + v_count++; + } + else if( y == pv->height[k]-1 ) + { + if( curp[0] == 255 && cur[0] == 255 ) + v_count++; + } + else + { + if( curp[0] == 255 && cur[0] == 255 && curn[0] == 255 ) + v_count++; + } + + if( h_count && pv->filter_mode == FILTER_CLASSIC ) + dst[0] = 255; + else if( pv->filter_mode == FILTER_CLASSIC ) + dst[0] = 0; + else if( h_count && v_count ) + dst[0] = 255; + else + dst[0] = 0; + + curp++; + cur++; + curn++; + dst++; + } + } + } +} + +int check_filtered_combing_mask( hb_filter_private_t * pv ) +{ + /* Go through the mask in X*Y blocks. If any of these windows + have threshold or more combed pixels, consider the whole + frame to be combed and send it on to be deinterlaced. */ + + /* Block mask threshold -- The number of pixels + in a block_width * block_height window of + he mask that need to show combing for the + whole frame to be seen as such. */ + int threshold = pv->block_threshold; + int block_width = pv->block_width; + int block_height = pv->block_height; + int block_x, block_y; + int block_score = 0; int send_to_blend = 0; + uint8_t * mask_p; + int x, y, k; + + for( k = 0; k < 1; k++ ) + { + int ref_stride = pv->ref_stride[k]; + pv->mask_box_x = -1; + pv->mask_box_y = -1; + pv->mask_box_color = 0; + + for( y = 0; y < ( pv->height[k] - block_height ); y = y + block_height ) + { + for( x = 0; x < ( pv->width[k] - block_width ); x = x + block_width ) + { + block_score = 0; + + for( block_y = 0; block_y < block_height; block_y++ ) + { + int mask_y = y + block_y; + mask_p = &pv->mask_filtered[k][mask_y*ref_stride + x]; + + for( block_x = 0; block_x < block_width; block_x++ ) + { + + if( mask_p[ 0 ] == 255 ) + block_score++; + mask_p++; + } + } + + if( block_score >= ( threshold / 2 ) ) + { +#if 0 + hb_log("decomb: frame %i | score %i | type %s", pv->deinterlaced_frames + pv->blended_frames + pv->unfiltered_frames + 1, block_score, pv->buf_settings->flags & 16 ? "Film" : "Video"); +#endif + pv->mask_box_x = x; + pv->mask_box_y = y; + + if ( block_score <= threshold && !( pv->buf_settings->flags & 16) ) + { + /* Blend video content that scores between + ( threshold / 2 ) and threshold. */ + send_to_blend = 1; + pv->mask_box_color = 2; + } + else if( block_score > threshold ) + { + if( pv->buf_settings->flags & 16 ) + { + /* Blend progressive content above the threshold.*/ + pv->mask_box_color = 2; + return 2; + } + else + { + /* Yadif deinterlace video content above the threshold. */ + pv->mask_box_color = 1; + return 1; + } + } + } + } + } + } + + if( send_to_blend ) + { + return 2; + } + else + { + /* Consider this frame to be uncombed. */ + return 0; + } +} + int check_combing_mask( hb_filter_private_t * pv ) { /* Go through the mask in X*Y blocks. If any of these windows @@ -503,12 +854,12 @@ int check_combing_mask( hb_filter_private_t * pv ) for( x = 0; x < ( pv->width[k] - block_width ); x = x + block_width ) { block_score = 0; - + for( block_y = 0; block_y < block_height; block_y++ ) { int mask_y = y + block_y; mask_p = &pv->mask[k][mask_y*ref_stride + x]; - + for( block_x = 0; block_x < block_width; block_x++ ) { /* We only want to mark a pixel in a block as combed @@ -533,6 +884,28 @@ int check_combing_mask( hb_filter_private_t * pv ) mask_p[ 1 ] == 255 ) block_score++; } + +#if 0 + if( (y + block_y) == 0 ) + { + if( mask_p[ 0 ] == 255 && + mask_p[ ref_stride ] == 255 ) + block_score++; + } + else if( (y + block_y) == (pv->height[k] -1) ) + { + if( mask_p[ -ref_stride ] == 255 && + mask_p[ 0 ] == 255 ) + block_score++; + } + else + { + if( mask_p[ -ref_stride ] == 255 && + mask_p[ 0 ] == 255 && + mask_p[ +ref_stride ] == 255 ) + block_score++; + } +#endif mask_p++; } } @@ -542,30 +915,37 @@ int check_combing_mask( hb_filter_private_t * pv ) #if 0 hb_log("decomb: frame %i | score %i | type %s", pv->deinterlaced_frames + pv->blended_frames + pv->unfiltered_frames + 1, block_score, pv->buf_settings->flags & 16 ? "Film" : "Video"); #endif + + pv->mask_box_x = x; + pv->mask_box_y = y; + if ( block_score <= threshold && !( pv->buf_settings->flags & 16) ) { /* Blend video content that scores between ( threshold / 2 ) and threshold. */ send_to_blend = 1; + pv->mask_box_color = 2; } else if( block_score > threshold ) { if( pv->buf_settings->flags & 16 ) { /* Blend progressive content above the threshold.*/ + pv->mask_box_color = 2; return 2; } else { /* Yadif deinterlace video content above the threshold. */ + pv->mask_box_color = 1; return 1; } } } } - } + } } - + if( send_to_blend ) { return 2; @@ -577,15 +957,151 @@ int check_combing_mask( hb_filter_private_t * pv ) } } +void build_gamma_lut( hb_filter_private_t * pv ) +{ + int i; + for( i = 0; i < 256; i++ ) + { + pv->gamma_lut[i] = pow( ( (float)i / (float)255 ), 2.2f ); + } +} + +float scale_gamma( int pixel, hb_filter_private_t * pv ) +{ + return pv->gamma_lut[pixel]; +} + +void detect_gamma_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop ) +{ + /* A mish-mash of various comb detection tricks + picked up from neuron2's Decomb plugin for + AviSynth and tritical's IsCombedT and + IsCombedTIVTC plugins. */ + + int x, y, k, width, height; + + /* Comb scoring algorithm */ + /* Motion threshold */ + float mthresh = (float)pv->motion_threshold / (float)255; + /* Spatial threshold */ + float athresh = (float)pv->spatial_threshold / (float)255; + float athresh6 = 6 *athresh; + + /* One pas for Y, one pass for U, one pass for V */ + for( k = 0; k < 1; k++ ) + { + int ref_stride = pv->ref_stride[k]; + width = pv->width[k]; + height = pv->height[k]; + + /* Comb detection has to start at y = 2 and end at + y = height - 2, because it needs to examine + 2 pixels above and 2 below the current pixel. */ + if( segment_start < 2 ) + segment_start = 2; + if( segment_stop > height - 2 ) + segment_stop = height - 2; + + for( y = segment_start; y < segment_stop; y++ ) + { + /* These are just to make the buffer locations easier to read. */ + int up_2 = -2*ref_stride ; + int up_1 = -1*ref_stride; + int down_1 = ref_stride; + int down_2 = 2*ref_stride; + + /* We need to examine a column of 5 pixels + in the prev, cur, and next frames. */ + uint8_t * cur = &pv->ref[1][k][y*ref_stride]; + uint8_t * prev = &pv->ref[0][k][y*ref_stride]; + uint8_t * next = &pv->ref[2][k][y*ref_stride]; + uint8_t * mask = &pv->mask[k][y*ref_stride]; + + for( x = 0; x < width; x++ ) + { + float up_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[up_1]]; + float down_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[down_1]]; + + if( ( up_diff > athresh && down_diff > athresh ) || + ( up_diff < -athresh && down_diff < -athresh ) ) + { + /* The pixel above and below are different, + and they change in the same "direction" too.*/ + int motion = 0; + if( mthresh > 0 ) + { + /* Make sure there's sufficient motion between frame t-1 to frame t+1. */ + if( fabs( pv->gamma_lut[prev[0]] - pv->gamma_lut[cur[0]] ) > mthresh && + fabs( pv->gamma_lut[cur[up_1]] - pv->gamma_lut[next[up_1]] ) > mthresh && + fabs( pv->gamma_lut[cur[down_1]] - pv->gamma_lut[next[down_1]] ) > mthresh ) + motion++; + if( fabs( pv->gamma_lut[next[0]] - pv->gamma_lut[cur[0]] ) > mthresh && + fabs( pv->gamma_lut[prev[up_1]] - pv->gamma_lut[cur[up_1]] ) > mthresh && + fabs( pv->gamma_lut[prev[down_1]] - pv->gamma_lut[cur[down_1]] ) > mthresh ) + motion++; + +// hb_log("prev->cur motion: %f, mthresh: %f", fabs( scale_gamma( prev[0] ) - scale_gamma( cur[0] ) ), mthresh); + } + else + { + /* User doesn't want to check for motion, + so move on to the spatial check. */ + motion = 1; + } + + if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) ) + { + + /* Tritical's noise-resistant combing scorer. + The check is done on a bob+blur convolution. */ + float combing = fabs( pv->gamma_lut[cur[up_2]] + + ( 4 * pv->gamma_lut[cur[0]] ) + + pv->gamma_lut[cur[down_2]] + - ( 3 * ( pv->gamma_lut[cur[up_1]] + + pv->gamma_lut[cur[down_1]] ) ) ); + /* If the frame is sufficiently combed, + then mark it down on the mask as 255. */ + + // hb_log("combing: %f, athresh6: %f", combing, athresh6); + if( combing > athresh6 ) + { + mask[0] = 255; + } + else + { + mask[0] = 0; + } + + } + else + { + mask[0] = 0; + } + } + else + { + mask[0] = 0; + } + + cur++; + prev++; + next++; + mask++; + } + } + } +} + + void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop ) { /* A mish-mash of various comb detection tricks picked up from neuron2's Decomb plugin for AviSynth and tritical's IsCombedT and IsCombedTIVTC plugins. */ - + int x, y, k, width, height; - + /* Comb scoring algorithm */ int spatial_metric = pv->spatial_metric; /* Motion threshold */ @@ -595,13 +1111,13 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg int athresh_squared = athresh * athresh; int athresh6 = 6 *athresh; - /* One pas for Y, one pass for U, one pass for V */ + /* One pas for Y, one pass for U, one pass for V */ for( k = 0; k < 1; k++ ) { int ref_stride = pv->ref_stride[k]; width = pv->width[k]; height = pv->height[k]; - + /* Comb detection has to start at y = 2 and end at y = height - 2, because it needs to examine 2 pixels above and 2 below the current pixel. */ @@ -609,7 +1125,7 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg segment_start = 2; if( segment_stop > height - 2 ) segment_stop = height - 2; - + for( y = segment_start; y < segment_stop; y++ ) { /* These are just to make the buffer locations easier to read. */ @@ -617,19 +1133,19 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg int up_1 = -1*ref_stride; int down_1 = ref_stride; int down_2 = 2*ref_stride; - + /* We need to examine a column of 5 pixels in the prev, cur, and next frames. */ uint8_t * cur = &pv->ref[1][k][y*ref_stride]; uint8_t * prev = &pv->ref[0][k][y*ref_stride]; uint8_t * next = &pv->ref[2][k][y*ref_stride]; uint8_t * mask = &pv->mask[k][y*ref_stride]; - + for( x = 0; x < width; x++ ) { int up_diff = cur[0] - cur[up_1]; int down_diff = cur[0] - cur[down_1]; - + if( ( up_diff > athresh && down_diff > athresh ) || ( up_diff < -athresh && down_diff < -athresh ) ) { @@ -654,7 +1170,7 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg so move on to the spatial check. */ motion = 1; } - + if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) ) { /* That means it's time for the spatial check. @@ -678,9 +1194,9 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg It's better, but still noise senstive. */ int combing = ( cur[up_1] - cur[0] ) * ( cur[down_1] - cur[0] ); - + if( combing > athresh_squared ) - mask[0] = 255; + mask[0] = 255; else mask[0] = 0; } @@ -715,7 +1231,7 @@ void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int seg { mask[0] = 0; } - + cur++; prev++; next++; @@ -752,7 +1268,7 @@ void eedi2_interpolate_plane( hb_filter_private_t * pv, int k ) // edge mask eedi2_build_edge_mask( mskp, pitch, srcp, pitch, - pv->magnitude_threshold, pv->variance_threshold, pv->laplacian_threshold, + pv->magnitude_threshold, pv->variance_threshold, pv->laplacian_threshold, half_height, width ); eedi2_erode_edge_mask( mskp, pitch, tmpp, pitch, pv->erosion_threshold, half_height, width ); eedi2_dilate_edge_mask( tmpp, pitch, mskp, pitch, pv->dilation_threshold, half_height, width ); @@ -836,13 +1352,13 @@ void eedi2_filter_thread( void *thread_args_v ) */ run = 0; continue; - } + } /* * Process plane */ eedi2_interpolate_plane( pv, plane ); - + /* * Finished this segment, let everyone know. */ @@ -863,12 +1379,12 @@ void eedi2_planer( hb_filter_private_t * pv ) int start_line = !pv->tff; eedi2_fill_half_height_buffer_plane( &pv->ref[1][i][pitch*start_line], pv->eedi_half[SRCPF][i], pitch, pv->height[i] ); } - + int plane; for( plane = 0; plane < 3; plane++ ) - { + { /* - * Let the thread for this plane know that we've setup work + * Let the thread for this plane know that we've setup work * for it by releasing the begin lock (ensuring that the * complete lock is already locked so that we block when * we try to lock it again below). @@ -924,7 +1440,7 @@ void decomb_filter_thread( void *thread_args_v ) */ run = 0; continue; - } + } /* * Process segment (for now just from luma) @@ -943,8 +1459,15 @@ void decomb_filter_thread( void *thread_args_v ) } else { segment_stop = ( h / pv->cpu_count ) * ( segment + 1 ); } - - detect_combed_segment( pv, segment_start, segment_stop ); + + if( pv->mode & MODE_GAMMA ) + { + detect_gamma_combed_segment( pv, segment_start, segment_stop ); + } + else + { + detect_combed_segment( pv, segment_start, segment_stop ); + } } /* * Finished this segment, let everyone know. @@ -959,9 +1482,9 @@ int comb_segmenter( hb_filter_private_t * pv ) int segment; for( segment = 0; segment < pv->cpu_count; segment++ ) - { + { /* - * Let the thread for this plane know that we've setup work + * Let the thread for this plane know that we've setup work * for it by releasing the begin lock (ensuring that the * complete lock is already locked so that we block when * we try to lock it again below). @@ -981,8 +1504,22 @@ int comb_segmenter( hb_filter_private_t * pv ) hb_lock( pv->decomb_complete_lock[segment] ); hb_unlock( pv->decomb_complete_lock[segment] ); } - - return check_combing_mask( pv ); + + if( pv->mode & MODE_FILTER ) + { + filter_combing_mask( pv ); + if( pv->filter_mode == FILTER_ERODE_DILATE ) + { + erode_combing_mask( pv ); + dilate_combing_mask( pv ); + erode_combing_mask( pv ); + } + return check_filtered_combing_mask( pv ); + } + else + { + return check_combing_mask( pv ); + } } static void yadif_filter_line( uint8_t *dst, @@ -1000,12 +1537,12 @@ static void yadif_filter_line( uint8_t *dst, to the other field in the current frame--the one not being filtered. */ uint8_t *prev2 = parity ? prev : cur ; uint8_t *next2 = parity ? cur : next; - + int w = pv->width[plane]; int refs = pv->ref_stride[plane]; int x; int eedi2_mode = ( pv->mode & MODE_EEDI2 ); - + /* We can replace spatial_pred with this interpolation*/ uint8_t * eedi2_guess = &pv->eedi_full[DST2PF][plane][y*refs]; @@ -1024,7 +1561,7 @@ static void yadif_filter_line( uint8_t *dst, int d = (prev2[0] + next2[0])>>1; /* Pixel below */ int e = cur[+refs]; - + /* How the current pixel changes between the adjacent fields */ int temporal_diff0 = ABS(prev2[0] - next2[0]); /* The average of how much the pixels above and below change from the frame before to now. */ @@ -1035,7 +1572,7 @@ static void yadif_filter_line( uint8_t *dst, int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); int spatial_pred; - + if( eedi2_mode ) { /* Who needs yadif's spatial predictions when we can have EEDI2's? */ @@ -1044,10 +1581,10 @@ static void yadif_filter_line( uint8_t *dst, } else // Yadif spatial interpolation { - /* SAD of how the pixel-1, the pixel, and the pixel+1 change from the line above to below. */ + /* SAD of how the pixel-1, the pixel, and the pixel+1 change from the line above to below. */ int spatial_score = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(cur[-refs]-cur[+refs]) + - ABS(cur[-refs+1] - cur[+refs+1]) - 1; - + ABS(cur[-refs+1] - cur[+refs+1]) - 1; + /* Spatial pred is either a bilinear or cubic vertical interpolation. */ if( ( pv->mode & MODE_CUBIC ) && !vertical_edge) { @@ -1114,12 +1651,12 @@ static void yadif_filter_line( uint8_t *dst, comparing against lines in the adjacent fields. */ int b = (prev2[-2*refs] + next2[-2*refs])>>1; int f = (prev2[+2*refs] + next2[+2*refs])>>1; - + /* Find the median value */ int max = MAX3(d-e, d-c, MIN(b-c, f-e)); int min = MIN3(d-e, d-c, MAX(b-c, f-e)); diff = MAX3( diff, min, -max ); - + if( spatial_pred > d + diff ) { spatial_pred = d + diff; @@ -1128,9 +1665,9 @@ static void yadif_filter_line( uint8_t *dst, { spatial_pred = d - diff; } - + dst[0] = spatial_pred; - + dst++; cur++; prev++; @@ -1176,7 +1713,7 @@ void yadif_decomb_filter_thread( void *thread_args_v ) */ run = 0; continue; - } + } if( yadif_work->dst == NULL ) { @@ -1184,7 +1721,7 @@ void yadif_decomb_filter_thread( void *thread_args_v ) hb_snooze(500); continue; } - + is_combed = pv->yadif_arguments[segment].is_combed; /* @@ -1230,7 +1767,7 @@ void yadif_decomb_filter_thread( void *thread_args_v ) /* Just apply vertical cubic interpolation */ uint8_t *cur = &pv->ref[1][plane][y*ref_stride]; uint8_t *dst2 = &dst[plane][y*w]; - + cubic_interpolate_line( dst2, cur, plane, y, pv ); } else if( pv->mode & MODE_YADIF && ( ( y ^ parity ) & 1 ) && ( is_combed == 1 ) ) @@ -1249,13 +1786,13 @@ void yadif_decomb_filter_thread( void *thread_args_v ) uint8_t *next = &pv->ref[2][plane][y*ref_stride]; uint8_t *dst2 = &dst[plane][y*w]; - yadif_filter_line( dst2, - prev, - cur, - next, - plane, + yadif_filter_line( dst2, + prev, + cur, + next, + plane, parity ^ tff, - y, + y, pv ); } else if( y == 0 ) @@ -1291,7 +1828,7 @@ void yadif_decomb_filter_thread( void *thread_args_v ) { memcpy( &dst[plane][y*w], &pv->ref[1][plane][y*ref_stride], - w * sizeof(uint8_t) ); + w * sizeof(uint8_t) ); } } } @@ -1310,7 +1847,7 @@ static void yadif_filter( uint8_t ** dst, { /* If we're running comb detection, do it now, otherwise default to true. */ int is_combed = pv->spatial_metric >= 0 ? comb_segmenter( pv ) : 1; - + /* The comb detector suggests three different values: 0: Don't comb this frame. 1: Deinterlace this frame. @@ -1340,7 +1877,7 @@ static void yadif_filter( uint8_t ** dst, /* No deinterlacer or mask chosen, pass the frame through. */ is_combed = 0; } - + if( is_combed == 1 ) { pv->deinterlaced_frames++; @@ -1353,13 +1890,13 @@ static void yadif_filter( uint8_t ** dst, { pv->unfiltered_frames++; } - + if( is_combed == 1 && ( pv->mode & MODE_EEDI2 ) ) { /* Generate an EEDI2 interpolation */ eedi2_planer( pv ); } - + if( is_combed ) { if( ( pv->mode & MODE_EEDI2 ) && !( pv->mode & MODE_YADIF ) && is_combed == 1 ) @@ -1388,7 +1925,7 @@ static void yadif_filter( uint8_t ** dst, int segment; for( segment = 0; segment < pv->cpu_count; segment++ ) - { + { /* * Setup the work for this plane. */ @@ -1398,7 +1935,7 @@ static void yadif_filter( uint8_t ** dst, pv->yadif_arguments[segment].is_combed = is_combed; /* - * Let the thread for this plane know that we've setup work + * Let the thread for this plane know that we've setup work * for it by releasing the begin lock (ensuring that the * complete lock is already locked so that we block when * we try to lock it again below). @@ -1427,19 +1964,19 @@ static void yadif_filter( uint8_t ** dst, else { /* Just passing through... */ - + /* For mcdeint's benefit... */ pv->yadif_arguments[0].is_combed = is_combed; // 0 - + int i; for( i = 0; i < 3; i++ ) { uint8_t * ref = pv->ref[1][i]; uint8_t * dest = dst[i]; - + int w = pv->width[i]; int ref_stride = pv->ref_stride[i]; - + int y; for( y = 0; y < pv->height[i]; y++ ) { @@ -1449,10 +1986,10 @@ static void yadif_filter( uint8_t ** dst, } } } - + if( pv->mode & MODE_MASK && pv->spatial_metric >= 0 ) { - if( pv->mode == MODE_MASK || is_combed ) + if( pv->mode == MODE_MASK || (pv->mode & MODE_MASK && pv->mode & MODE_GAMMA) || is_combed || (pv->mode & MODE_MASK && pv->mode & MODE_FILTER )) apply_mask( pv ); } } @@ -1476,6 +2013,8 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, pv->width[1] = pv->width[2] = width >> 1; pv->height[1] = pv->height[2] = height >> 1; + build_gamma_lut( pv ); + pv->buf_out[0] = hb_video_buffer_init( width, height ); pv->buf_out[1] = hb_video_buffer_init( width, height ); pv->buf_settings = hb_buffer_init( 0 ); @@ -1486,14 +2025,16 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, pv->yadif_ready = 0; - pv->mode = MODE_YADIF | MODE_BLEND | MODE_CUBIC; + pv->mode = MODE_YADIF | MODE_BLEND | MODE_CUBIC | + MODE_GAMMA | MODE_FILTER; + pv->filter_mode = FILTER_ERODE_DILATE; pv->spatial_metric = 2; - pv->motion_threshold = 6; - pv->spatial_threshold = 9; - pv->block_threshold = 80; + pv->motion_threshold = 3; + pv->spatial_threshold = 3; + pv->block_threshold = 40; pv->block_width = 16; pv->block_height = 16; - + pv->magnitude_threshold = 10; pv->variance_threshold = 20; pv->laplacian_threshold = 20; @@ -1510,11 +2051,12 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, if( settings ) { - sscanf( settings, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + sscanf( settings, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", &pv->mode, &pv->spatial_metric, &pv->motion_threshold, &pv->spatial_threshold, + &pv->filter_mode, &pv->block_threshold, &pv->block_width, &pv->block_height, @@ -1528,15 +2070,15 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, &pv->post_processing, &pv->parity ); } - + pv->cpu_count = hb_get_cpu_count(); - + if( pv->mode & MODE_MCDEINT ) { pv->mcdeint_mode = MCDEINT_MODE_ENABLED; } - + /* Allocate yadif specific buffers */ int i, j; for( i = 0; i < 3; i++ ) @@ -1553,7 +2095,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, } } - /* Allocate a buffer to store a comb mask. */ + /* Allocate buffers to store comb masks. */ for( i = 0; i < 3; i++ ) { int is_chroma = !!i; @@ -1561,8 +2103,10 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, int h = ((pv->height[0]+6+ 31) & (~31))>>is_chroma; pv->mask[i] = calloc( 1, w*h*sizeof(uint8_t) ) + 3*w; + pv->mask_filtered[i] = calloc( 1, w*h*sizeof(uint8_t) ) + 3*w; + pv->mask_temp[i] = calloc( 1, w*h*sizeof(uint8_t) ) + 3*w; } - + if( pv->mode & MODE_EEDI2 ) { /* Allocate half-height eedi2 buffers */ @@ -1593,7 +2137,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, } } } - + /* * Create yadif threads and locks. */ @@ -1624,7 +2168,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, pv->yadif_arguments[i].stop = 0; pv->yadif_arguments[i].dst = NULL; - + pv->yadif_threads[i] = hb_thread_init( "yadif_filter_segment", yadif_decomb_filter_thread, thread_args, @@ -1635,7 +2179,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, hb_error( "yadif could not create threads" ); } } - + /* * Create decomb threads and locks. */ @@ -1643,29 +2187,29 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, pv->decomb_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); pv->decomb_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); pv->decomb_arguments = malloc( sizeof( decomb_arguments_t ) * pv->cpu_count ); - + for( i = 0; i < pv->cpu_count; i++ ) { decomb_thread_arg_t *decomb_thread_args; - + decomb_thread_args = malloc( sizeof( decomb_thread_arg_t ) ); - + if( decomb_thread_args ) { decomb_thread_args->pv = pv; decomb_thread_args->segment = i; - + pv->decomb_begin_lock[i] = hb_lock_init(); pv->decomb_complete_lock[i] = hb_lock_init(); - + /* * Important to start off with the threads locked waiting * on input. */ hb_lock( pv->decomb_begin_lock[i] ); - + pv->decomb_arguments[i].stop = 0; - + pv->decomb_threads[i] = hb_thread_init( "decomb_filter_segment", decomb_filter_thread, decomb_thread_args, @@ -1676,7 +2220,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, hb_error( "decomb could not create threads" ); } } - + if( pv->mode & MODE_EEDI2 ) { /* @@ -1732,7 +2276,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt, } } } - + mcdeint_init( &pv->mcdeint, pv->mcdeint_mode, mcdeint_qp, width, height ); return pv; @@ -1744,7 +2288,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) { return; } - + hb_log("decomb: deinterlaced %i | blended %i | unfiltered %i | total %i", pv->deinterlaced_frames, pv->blended_frames, pv->unfiltered_frames, pv->deinterlaced_frames + pv->blended_frames + pv->unfiltered_frames); /* Cleanup frame buffers */ @@ -1772,18 +2316,31 @@ void hb_decomb_close( hb_filter_private_t * pv ) *p = NULL; } } - - /* Cleanup combing mask. */ + + /* Cleanup combing masks. */ for( i = 0; i<3*3; i++ ) { uint8_t **p = &pv->mask[i/3]; + uint8_t **p2 = &pv->mask_filtered[i/3]; + uint8_t **p3 = &pv->mask_temp[i/3]; + if (*p) { free( *p - 3*pv->ref_stride[i/3] ); *p = NULL; } + if (*p2) + { + free( *p2 - 3*pv->ref_stride[i/3] ); + *p2 = NULL; + } + if (*p3) + { + free( *p3 - 3*pv->ref_stride[i/3] ); + *p3 = NULL; + } } - + if( pv->mode & MODE_EEDI2 ) { /* Cleanup eedi-half buffers */ @@ -1797,7 +2354,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) { free( *p - 3*pv->ref_stride[i] ); *p = NULL; - } + } } } @@ -1811,11 +2368,11 @@ void hb_decomb_close( hb_filter_private_t * pv ) { free( *p - 3*pv->ref_stride[i] ); *p = NULL; - } + } } } } - + if( pv->post_processing > 1 && ( pv->mode & MODE_EEDI2 ) ) { if (pv->cx2) eedi2_aligned_free(pv->cx2); @@ -1823,7 +2380,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) if (pv->cxy) eedi2_aligned_free(pv->cxy); if (pv->tmpc) eedi2_aligned_free(pv->tmpc); } - + for( i = 0; i < pv->cpu_count; i++) { /* @@ -1836,7 +2393,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) hb_lock_close( &pv->yadif_begin_lock[i] ); hb_lock_close( &pv->yadif_complete_lock[i] ); } - + /* * free memory for yadif structs */ @@ -1844,7 +2401,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) free( pv->yadif_begin_lock ); free( pv->yadif_complete_lock ); free( pv->yadif_arguments ); - + for( i = 0; i < pv->cpu_count; i++) { /* @@ -1857,7 +2414,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) hb_lock_close( &pv->decomb_begin_lock[i] ); hb_lock_close( &pv->decomb_complete_lock[i] ); } - + /* * free memory for decomb structs */ @@ -1865,7 +2422,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) free( pv->decomb_begin_lock ); free( pv->decomb_complete_lock ); free( pv->decomb_arguments ); - + if( pv->mode & MODE_EEDI2 ) { for( i = 0; i < 3; i++) @@ -1889,7 +2446,7 @@ void hb_decomb_close( hb_filter_private_t * pv ) free( pv->eedi2_complete_lock ); free( pv->eedi2_arguments ); } - + /* Cleanup mcdeint specific buffers */ mcdeint_close( &pv->mcdeint ); @@ -1945,19 +2502,19 @@ int hb_decomb_work( const hb_buffer_t * cbuf_in, return FILTER_DELAY; } - /* Perform yadif filtering */ + /* Perform yadif filtering */ int frame; for( frame = 0; frame <= ( ( pv->mode & MODE_MCDEINT ) ? 1 : 0 ) ; frame++ ) // This would be what to use for bobbing: for( frame = 0; frame <= 0 ; frame++ ) { -#if 0 +#if 0 /* Perhaps skip the second run if the frame is uncombed? */ if( frame && !pv->yadif_arguments[0].is_combed ) { break; } -#endif +#endif int parity = frame ^ tff ^ 1; // This will be for bobbing |