All pastes #938674 Raw Edit

Combing Detection

public diff v1 · immutable
·published 2008-03-11 20:15 UTC
rendered paste body
Index: test/test.c===================================================================--- test/test.c	(revision 1335)+++ test/test.c	(working copy)@@ -300,6 +300,13 @@         fprintf( stderr, "    + %d, %s (iso639-2: %s)\n", i + 1, subtitle->lang,             subtitle->iso639_2);     }+    +    if(title->detected_interlacing)+    {+        /* Interlacing was found in 6 or more preview frames */+        fprintf( stderr, "  + interlacing artifacts detected\n");+    }+     }  static int HandleEvents( hb_handle_t * h )Index: libhb/hb.c===================================================================--- libhb/hb.c	(revision 1335)+++ libhb/hb.c	(working copy)@@ -426,6 +426,111 @@     avpicture_free( &pic_in ); } + /**+ * Analyzes a frame to detect interlacing artifacts+ * and returns true if interlacing (combing) is found.+ *+ * Code taken from Thomas Oestreich's 32detect filter+ * in the Transcode project, with minor formatting changes.+ *+ * @param buf An hb_buffer structure holding valid frame data.+ * @param width The frame's width in pixels.+ * @param height The frame's height in pixels.+ */+int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold)+{+    int j, k, n, off, block, cc_1, cc_2, cc[3], flag[3];+    uint16_t s1, s2, s3, s4;+    cc_1 = 0; cc_2 = 0;+    +    /* These values are defined in hb.h */+    int thres = threshold;+    int eq = color_equal;+    int diff = color_diff;+    +    int offset = 0;+    +    for(k=0; k < 3; k++)+    {+        /* One pas for Y, one pass for Cb, one pass for Cr */+        if( k == 1 )+        {+            /* Y has already been checked, now offset by Y's dimensions+               and divide all the other values by 2, since Cr and Cb+               are half-size compared to Y.                               */+            offset = width * height;+            width >>= 1;+            height >>= 1;+            thres >>= 1;+            eq >>= 1;+            diff >>= 1;+        }+        else if (k == 2 )+        {+            /* Y and Cb are done, so the offset needs to be bumped+               so it's width*height + (width / 2) * (height / 2)  */+            offset *= 5/4;+        }+        +        /* Look at one horizontal line at a time */+        block = width;+        +        for(j=0; j<block; ++j)+        {+            off=0;+            +            for(n=0; n<(height-4); n=n+2)+            {+                /* Look at groups of 4 sequential horizontal lines */+                s1 = ((buf->data+offset)[off+j        ] & 0xff);+                s2 = ((buf->data+offset)[off+j+  block] & 0xff);+                s3 = ((buf->data+offset)[off+j+2*block] & 0xff);+                s4 = ((buf->data+offset)[off+j+3*block] & 0xff);+                +                /* Note if the 1st and 2nd lines are more different in+                   color than the 1st and 3rd lines are similar in color.*/+                if((abs(s1 - s3) < eq) &&+                    (abs(s1 - s2) > diff)) ++cc_1;+    +                /* Note if the 2nd and 3rd lines are more different in+                   color than the 2nd and 4th lines are similar in color.*/+                if((abs(s2 - s4) < eq) &&+                    (abs(s2 - s3) > diff)) ++cc_2;+                +                /* Now move down 2 horizontal lines before starting over.*/+                off +=2*block;+            }+        }+        +        // compare results+        /* The final metric seems to be doing some kind of bits per pixel style calculation+           to decide whether or not enough lines showed alternating colors for the frame size. */+        cc[k] = (int)((cc_1 + cc_2)*1000.0/(width*height));+        +        /* If the plane's cc score meets the threshold, flag it as combed. */+        flag[k] = 0;+        if(cc[k] > thres)+        {+            flag[k] = 1;+        }+    }+    +#if 0+/* Debugging info */+//    if(flag)+        hb_log("flags: %i/%i/%i | cc0: %i | cc1: %i | cc2: %i", flag[0], flag[1], flag[2], cc[0], cc[1], cc[2]);+#endif+    +    /* When more than one plane shows combing, tell the caller. */+    if (flag[0] || flag[1] || flag[2] )+    {+        return 1;+    }++    return 0;+}++ /**  * Calculates job width and height for anamorphic content,  *Index: libhb/hb.h===================================================================--- libhb/hb.h	(revision 1335)+++ libhb/hb.h	(working copy)@@ -75,6 +75,14 @@    Returns the list of valid titles detected by the latest scan. */ hb_list_t   * hb_get_titles( hb_handle_t * ); +/* hb_detect_comb()+   Analyze a frame for interlacing artifacts, returns true if they're found.+   Taken from Thomas Oestreich's 32detect filter in the Transcode project.  */+int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold);+#define COLOR_EQUAL  10 // Sensitivity for detecting similar colors.+#define COLOR_DIFF   30 // Sensitivity for detecting different colors+#define THRESHOLD     9 // Sensitivity for flagging planes as combed+ void          hb_get_preview( hb_handle_t *, hb_title_t *, int,                               uint8_t * ); void          hb_set_size( hb_job_t *, int ratio, int pixels );Index: libhb/deinterlace.c===================================================================--- libhb/deinterlace.c	(revision 1335)+++ libhb/deinterlace.c	(working copy)@@ -42,6 +42,12 @@     int              yadif_parity;     int              yadif_ready; +    int              comb;+    int              comb_detect;+    int              color_equal;+    int              color_diff;+    int              threshold;+     uint8_t        * yadif_ref[4][3];     int              yadif_ref_stride[3]; @@ -58,6 +64,9 @@     AVPicture        pic_out;     hb_buffer_t *    buf_out[2];     hb_buffer_t *    buf_settings;+    +    int              deinterlaced_frames;+    int              passed_frames; };  hb_filter_private_t * hb_deinterlace_init( int pix_fmt,@@ -87,6 +96,7 @@ static void yadif_store_ref( const uint8_t ** pic,                              hb_filter_private_t * pv ) {+    /*  1st entry becomes 4th, 2nd entry because 1st. */     memcpy( pv->yadif_ref[3],             pv->yadif_ref[0],             sizeof(uint8_t *)*3 );@@ -94,17 +104,22 @@     memmove( pv->yadif_ref[0],              pv->yadif_ref[1],              sizeof(uint8_t *)*3*3 );-+    +    /* 3 color planes */     int i;     for( i = 0; i < 3; i++ )     {+        /* Source is the input plane */         const uint8_t * src = pic[i];+        /* Ref points to where it'll be stored, the 3rd slot in the ref array.*/         uint8_t * ref = pv->yadif_ref[2][i];-+        +        /* Dimensions will be halved for Cb + Cr, stride is offset for line length..*/         int w = pv->width[i];         int h = pv->height[i];         int ref_stride = pv->yadif_ref_stride[i];-+        +        /* Go through each horizontal line of the src plane and copy it to the buffer. */         int y;         for( y = 0; y < pv->height[i]; y++ )         {@@ -115,6 +130,27 @@     } } +static void yadif_get_ref( uint8_t ** pic, hb_filter_private_t * pv, int frm )+{+    int i;+    for( i = 0; i < 3; i++ )+    {+        uint8_t * dst = pic[i];+        const uint8_t * ref = pv->yadif_ref[frm][i];+        int w = pv->width[i];+        int ref_stride = pv->yadif_ref_stride[i];+        +        int y;+        for( y = 0; y < pv->height[i]; y++ )+        {+            memcpy(dst, ref, w);+            dst += w;+            ref += ref_stride;+        }+    }+}++ static void yadif_filter_line( uint8_t *dst,                                uint8_t *prev,                                uint8_t *cur,@@ -123,26 +159,83 @@                                int parity,                                hb_filter_private_t * pv ) {+    /*  If TFF, look at the previous and current frames, otherwise the current and next */     uint8_t *prev2 = parity ? prev : cur ;     uint8_t *next2 = parity ? cur  : next;-+    +    /* Width aries with plane size.*/     int w = pv->width[plane];+    /* Stride offset varies with planar width. */     int refs = pv->yadif_ref_stride[plane];-+    +    /* Step through the horizontal pixels in this line. */     int x;     for( x = 0; x < w; x++)     {+        /* C: Pixel above*/         int c              = cur[-refs];+        /* D: the average of this pixel in the frames before and after.*/         int d              = (prev2[0] + next2[0])>>1;+        /* E: Pixel below*/         int e              = cur[+refs];+        /* diff0: The energy delta between this pixel in the previous and next frames.*/         int temporal_diff0 = ABS(prev2[0] - next2[0]);+        /* diff1: The average of the deltas between:+           The pixel above in the last frame minus the pixel above in the current frame, and+           The pixel below in the last frame minus the pixel below in the current frame.     */         int temporal_diff1 = ( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) ) >> 1;+        /* diff2: The average of the deltas between:+           The pixel above in the next frame minus the pixel above in the current frame, and+           The pixel below in the next frame minus the pixel below in the current frame.    */         int temporal_diff2 = ( ABS(next[-refs] - c) + ABS(next[+refs] - e) ) >> 1;+        /* diff: Choose the largest of the three:+           Half the change in the current pixel between the previous and next frames,+           The average vertical change since the last frame, or+           The average vertical change in the next frame.       */         int diff           = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2);+        /* spatial_pred: Average of the pixels above and below in the current frame. */         int spatial_pred   = (c+e)>>1;++        /* spatial_score:+           The difference between the pixels above and below the pixel to the left, plus+           The difference between the pixels above and below the current pixel, plus,+           The difference between the pixels above and below the pixel to the right,+           ...minus one. Why? */         int spatial_score  = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) +                              ABS(cur[-refs+1] - cur[+refs+1]) - 1; +/* The Yadif Spatial Check Score is run 2-4 times.+   PASS ONE, j=-1+   The pixel 2 to the left above minus the pixel below, plus+   The pixel 1 to the left abve minus the pixel 1 to the right below, plus+   The pixel in the line above minus the pixel 2 to the right below.+   IF this is less than the spatial score:+    It replaces the spatial score.+    The spatial_pred is set to the average of the pixel 1 to the left above and the pixel 1 to the right below.+    Run PASS 2:+    PASS TWO j = -2+    The pixel 3 to the left above minus the pixel 1 to the right below, plus+    The pixel 2 to the left above minus the pixel 2 to the right below, plus+    The pixel 1 to the left above minus the pixel 3 to the right below.+    IF this is less than the spatial score:+     It replaces the spatial score.+     The spatial_pred is set to the average of the pixel 2 to the left above minus the pixel 2 to the right below.+   PASS THREE: j = 1+   The pixel above minus the pixel 2 to the left below, plus+   The pixel 1 to the right above minus the pixel 1 to the left below, plus+   The pixel 2 to the right above minus the pixel below.+   IF this is less than the spatial score:+    It replaces the spatial score.+    The spatial_pred is set ot the average of the pixel 1 to the right above and the pixel 1 to the left below.+    Run PASS 4:+     PASS FOUR: j = 2+     The pixel 1 to the right above minus the pixel 3 to the left below, plus+     The pixel 2 to the right above minus the pixel 2 to the left below, plus+     The pixel 3 to the right above, minus the pixel 1 to the left below.+     IF this is less than the spatal score:+      It repalces the spatial score.+      The spatial_pred is set to the average of the pixel 2 to the right above and 2 to the left below.+*/ #define YADIF_CHECK(j)\         {   int score = ABS(cur[-refs-1+j] - cur[+refs-1-j])\                       + ABS(cur[-refs  +j] - cur[+refs  -j])\@@ -153,18 +246,32 @@          YADIF_CHECK(-1) YADIF_CHECK(-2) }} }}         YADIF_CHECK( 1) YADIF_CHECK( 2) }} }}-+        +        /* The slow stuff */         if( pv->yadif_mode < 2 )         {+            /* B: the average of the pixel 2 lines above in the frames before and after. */             int b = (prev2[-2*refs] + next2[-2*refs])>>1;+            /* F: the average of the pixel 2 lines below in the frames before and after. */             int f = (prev2[+2*refs] + next2[+2*refs])>>1;-+            +            /* Which is bigger? / Which is smaller?+               The average of this pixel in the frames before and after minus the pixel below, or+               The average of this pixel in the frames before and after miinus the pixel above, or+               The smaller of: / The bigger of:+                The average of the pixel 2 lines above in the frames before and afer minus the pixel above, or+                The average of the pixel 2 lines below in the frames before and after minus the pixel below     */             int max = MAX3(d-e, d-c, MIN(b-c, f-e));             int min = MIN3(d-e, d-c, MAX(b-c, f-e));-+            +            /* For the real temporal diff, use whichever's largest.*/             diff = MAX3( diff, min, -max );         }-+        +        /* If the prediction is larger than the average of the pixel in frames before and after+           plus temporal correction, replace it. Otherwise --+           If the prediction is smaller than the average of the pixel in the frames before and+           after minus temporal correction, replace it. */         if( spatial_pred > d + diff )         {             spatial_pred = d + diff;@@ -173,9 +280,11 @@         {             spatial_pred = d - diff;         }-+        +        /* Use the prediction as the output. */         dst[0] = spatial_pred;-+        +        /* Bump the head for the next pixel. */         dst++;         cur++;         prev++;@@ -190,27 +299,32 @@                           int tff,                           hb_filter_private_t * pv ) {+    /* Step through the color planes. */     int i;     for( i = 0; i < 3; i++ )     {+        /* Dimensions will be halved for Cb+Cr, stride is offset for previous planes.*/         int w = pv->width[i];         int h = pv->height[i];         int ref_stride = pv->yadif_ref_stride[i];-+        +        /* Step through horizontal lines.*/         int y;         for( y = 0; y < h; y++ )         {             if( (y ^ parity) &  1 )             {+                /* Only filter second field? */                 uint8_t *prev = &pv->yadif_ref[0][i][y*ref_stride];                 uint8_t *cur  = &pv->yadif_ref[1][i][y*ref_stride];                 uint8_t *next = &pv->yadif_ref[2][i][y*ref_stride];                 uint8_t *dst2 = &dst[i][y*w];-+                         yadif_filter_line( dst2, prev, cur, next, i, parity ^ tff, pv );             }             else             {+                /* Pass the line through unscathed.*/                 memcpy( &dst[i][y*w],                         &pv->yadif_ref[1][i][y*ref_stride],                         w * sizeof(uint8_t) );@@ -356,6 +470,13 @@     pv->buf_out[1] = hb_buffer_init( buf_size );     pv->buf_settings = hb_buffer_init( 0 ); +    pv->deinterlaced_frames = 0;+    pv->passed_frames = 0;+    pv->comb_detect = 0;+    pv->color_equal = 10;+    pv->color_diff = 30;+    pv->threshold = 9;+         pv->yadif_ready    = 0;     pv->yadif_mode     = YADIF_MODE_DEFAULT;     pv->yadif_parity   = YADIF_PARITY_DEFAULT;@@ -365,13 +486,19 @@      if( settings )     {-        sscanf( settings, "%d:%d:%d:%d",+        sscanf( settings, "%d:%d:%d:%d:%d:%d:%d:%d",                 &pv->yadif_mode,                 &pv->yadif_parity,                 &pv->mcdeint_mode,-                &pv->mcdeint_qp );+                &pv->mcdeint_qp,+                &pv->comb_detect,+                &pv->color_equal,+                &pv->color_diff,+                &pv->threshold );     } +    hb_log("DEINTERLACE: detect: %i eq: %i diff: %i thresh: %i", pv->comb_detect, pv->color_equal, pv->color_diff, pv->threshold);+         /* Allocate yadif specific buffers */     if( pv->yadif_mode >= 0 )     {@@ -451,6 +578,8 @@         return;     } +    hb_log("deinterlacer: filtered %i | unfiltered %i | total %i", pv->deinterlaced_frames, pv->passed_frames, pv->deinterlaced_frames + pv->passed_frames);+     /* Cleanup frame buffers */     if( pv->buf_out[0] )     {@@ -521,13 +650,31 @@         avpicture_fill( &pv->pic_out, pv->buf_out[0]->data,                         pix_fmt, width, height ); -        avpicture_deinterlace( &pv->pic_out, &pv->pic_in,-                               pix_fmt, width, height );+        /* Check for combing on the input frame */+        int interlaced = pv->comb_detect ? ( hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold)) : 1;+        +        if(interlaced)+        {+            avpicture_deinterlace( &pv->pic_out, &pv->pic_in,+                                   pix_fmt, width, height ); -        hb_buffer_copy_settings( pv->buf_out[0], buf_in );+            pv->deinterlaced_frames++; -        *buf_out = pv->buf_out[0];+            hb_buffer_copy_settings( pv->buf_out[0], buf_in ); +            *buf_out = pv->buf_out[0];            +        }+        else+        {+            /* No combing detected, pass input frame through unmolested.*/+            +            pv->passed_frames++;+            +            hb_buffer_copy_settings( pv->buf_out[0], buf_in );+            *buf_out = buf_in;+            +        }+         return FILTER_OK;     } @@ -545,6 +692,9 @@     /* Store current frame in yadif cache */     yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv ); +    /* Note down if the input frame is combed */+    pv->comb = (pv->comb << 1) | ( pv->comb_detect? hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold) : 1);+     /* If yadif is not ready, store another ref and return FILTER_DELAY */     if( pv->yadif_ready == 0 )     {@@ -559,32 +709,51 @@          return FILTER_DELAY;     }--    /* Perform yadif and mcdeint filtering */-    int frame;-    for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )+    +    /* yadif & mcdeint work one frame behind so if the previous frame+     * had combing, deinterlace it otherwise just output it. */+    if(  (pv->comb & 2 ) == 0 )     {-        int parity = frame ^ tff ^ 1;+        /* previous frame not interlaced - copy cached input frame to buf_out */+        +        pv->passed_frames++;+        +        avpicture_fill( &pv->pic_out,  pv->buf_out[0]->data, pix_fmt, width, height );+        yadif_get_ref( (uint8_t**)pv->pic_out.data, pv, 1 );+        *buf_out = pv->buf_out[0];+    }+    else+    {+        /* Perform yadif and mcdeint filtering */+        +        pv->deinterlaced_frames++;+        +        int frame;+        for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )+        {+            int parity = frame ^ tff ^ 1; -        avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data,-                        pix_fmt, width, height );+            avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data,+                            pix_fmt, width, height ); -        yadif_filter( pv->pic_out.data, parity, tff, pv );+            yadif_filter( pv->pic_out.data, parity, tff, pv ); -        if( pv->mcdeint_mode >= 0 )-        {-            avpicture_fill( &pv->pic_in,  pv->buf_out[(frame^1)]->data,-                            pix_fmt, width, height );+            if( pv->mcdeint_mode >= 0 )+            {+                avpicture_fill( &pv->pic_in,  pv->buf_out[(frame^1)]->data,+                                pix_fmt, width, height ); -            mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );+                mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv ); -            *buf_out = pv->buf_out[ (frame^1)];+                *buf_out = pv->buf_out[ (frame^1)];+            }+            else+            {+                *buf_out = pv->buf_out[!(frame^1)];+            }         }-        else-        {-            *buf_out = pv->buf_out[!(frame^1)];-        }     }+          /* Copy buffered settings to output buffer settings */     hb_buffer_copy_settings( *buf_out, pv->buf_settings );Index: libhb/scan.c===================================================================--- libhb/scan.c	(revision 1335)+++ libhb/scan.c	(working copy)@@ -286,6 +286,8 @@     hb_list_t     * list_es, * list_raw;     hb_libmpeg2_t * mpeg2;     int progressive_count = 0;+    int interlacing[10];+     int ar16_count = 0, ar4_count = 0;      buf_ps   = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );@@ -419,7 +421,7 @@                 */                 if( progressive_count == 6 )                 {-                    hb_log("Title's mostly progressive NTSC, setting fps to 23.976");+                    hb_log("Title's mostly NTSC Film, setting fps to 23.976");                 }                 title->rate_base = 1126125;             }@@ -449,7 +451,19 @@         }          buf_raw = hb_list_item( list_raw, 0 );-+        +        /* Check preview for interlacing artifacts */+        if( hb_detect_comb(buf_raw, title->width, title->height, 10, 30, 9) )+        {+            hb_log("Interlacing detected in preview frame %i", i);+            interlacing[i] = 1;+        }+        else+        {+            interlacing[i] = 0;+        }+        +                 hb_get_tempory_filename( data->h, filename, "%x%d",                                  (intptr_t)title, i ); @@ -522,7 +536,28 @@             (float) title->rate_base, title->crop[0], title->crop[1],             title->crop[2], title->crop[3],             title->aspect == HB_ASPECT_BASE * 16 / 9 ? "16:9" :-                title->aspect == HB_ASPECT_BASE * 4 / 3 ? "4:3" : "none" );+            title->aspect == HB_ASPECT_BASE * 4 / 3 ? "4:3" : "none" );++    /* Add up how many previews were interlaced.*/+    int interlacing_sum, t;+    for(t = 0; t < 10; t++ )+    {+        if( interlacing[t] == 1 )+        {+            interlacing_sum++;+        }+    }++    if( interlacing_sum >= 6)+    {+        hb_log("Title is mostly interlaced or telecined (%i out of 10 previews). You should do something about that.", interlacing_sum);+        title->detected_interlacing = 1;+    }+    else+    {+        title->detected_interlacing = 0;+    }+     goto cleanup;  error:Index: libhb/common.h===================================================================--- libhb/common.h	(revision 1335)+++ libhb/common.h	(working copy)@@ -426,6 +426,7 @@     int         rate;     int         rate_base;     int         crop[4];+    int         detected_interlacing;      uint32_t    palette[16];