diff options
author | jstebbins <[email protected]> | 2011-10-22 19:36:15 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2011-10-22 19:36:15 +0000 |
commit | 0187936a8899237b0310432b23e570f14ef63679 (patch) | |
tree | 0e59ac845016356956f05b099e606088a1651eba /libhb/decomb.c | |
parent | 8700732ef39eaa67ed996e869a162a9aee72dd9a (diff) |
Add new decomb modes
This is jbrjakes decomb3 patch.
New modes are gamma compensation and erode dialate filter.
Gamma adjusting pixel values makes less visible differences count less
in the metric that is calculated. Filtering removes isolated "hits"
that are more likely noise. This all makes decomb more discerning and
therefore the thresholds can be lowered.
The filter's defaults use these new modes with lower thresholds. If you
want the old behavior, use:
7:2:6:9:1:80
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4308 b64f7644-9d1e-0410-96f1-a4d463321fa5
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 |