Combing Detection

Archive of historical development discussions
Discussions / Development has moved to GitHub
Forum rules
*******************************
Please be aware we are now using GitHub for issue tracking and feature requests.
- This section of the forum is now closed to new topics.

*******************************
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Combing Detection

Post by jbrjake »

The Transcode project has a filter called 32detect. It's for identifying telecined and/or interlaced content:
http://cvs.exit1.org/cgi-bin/viewcvs.cg ... &view=auto

It's got a simple little comb detector called interlace_test(). For each frame it's passed, it examines it in groups of 4 horizontal lines. If the first line's color is sufficiently close to the third line's color, and is sufficiently different from the second line's color, it marks it down as a potential interlacing artifact in the cc_1 metric. It does the same for line 2 versus line 4 and line 3 in the cc_2 metric. Then, it moves on to the next group of 4 lines, and repeats the process for the whole frame buffer. A final metric is calculated by adding together cc_1 and cc_2, multiplying by 1000, and dividing by the number of squared pixels in the frame. If this metric is high enough, the frame is flagged as interlaced.

I've shoehorned the code into libhb/deinterlace.c as detect_comb(), and arranged it so the actual deinterlacing functions are only called when the 32detect code senses combing. Erring on the side of caution, I've set the threshold *very* low -- 0, whereas transcode uses 9. This is because I want everything deinterlaced except perfect progressive frames. If there's even a hint of similarity between alternating lines, I'd rather play on the safe side and deinterlace it. These thresholds still need to be played with -- there's probably something to be said for lowering the difference threshold between alternating lines and increasing the equality threshold for sequential lines.

It's working great with fast (ffmpeg) deinterlacing. I haven't quite smoothed out the kinks with the motion deinterlacers. The problem is yadif stores a frame ahead...I spent all day fooling with different ways of setting it up, but so far the best I've done is sucky: tell it to output the input frame when there's no combing, but use the cached frame where there is combing. This causes occasional out-of-order frames. Just a proof of concept, for now ;> I'd appreciate any help anyone could give me on this part.

When everything's working right with yadif, this will means it'll be possible to run deinterlacing without taking forever, or messing up progressive frames (as is yadif's wont, sadly)... because it never actually gets called on them. Same for hard telecine stuff that's been successfully IVTC'd. But it will catch things IVTC misses, as well as interlaced parts of hybrid content. It seems to take up around 5% of HandBrake's work loop load when it's running on progressive content, compared to 10% for vfr and 10% for scaling.

So, with pullup running first, followed by this comb detection and then one of the deinterlacers, I think we're approaching a one-click "set it and forget it" way of handling any NTSC content quirks. Of course, we could just run yadif all the time without checking if it's needed, but that's a lot slower and will lose detail on progressive frames. Plus, it should even save time on fully interlaced material, by skipping the deinterlacer during static shots. If we want to go for something grander, it seems to me that we could use a delay queue (expand the one used by yadif) and compare lines across frames.

http://pastebin.ca/896753

Code: Select all

Index: libhb/deinterlace.c
===================================================================
--- libhb/deinterlace.c	(revision 1252)
+++ libhb/deinterlace.c	(working copy)
@@ -84,6 +84,53 @@
     hb_deinterlace_close,
 };
 
+int detect_comb( hb_buffer_t * buf, int width, int height);
+
+int detect_comb( hb_buffer_t * buf, int width, int height)
+{
+    /* See if frame shows interlacing */
+    int j, n, off, block, cc_1, cc_2, cc, flag, eq, diff, thres;
+    uint16_t s1, s2, s3, s4;
+    cc_1 = 0; cc_2 = 0;
+    eq = 10; diff = 30; thres = 0;
+    
+    block = width;
+    flag = 0;
+    
+    for(j=0; j<block; ++j)
+    {
+        off=0;
+        
+        for(n=0; n<(height-4); n=n+2)
+        {
+            s1 = (buf->data[off+j        ] & 0xff);
+            s2 = (buf->data[off+j+  block] & 0xff);
+            s3 = (buf->data[off+j+2*block] & 0xff);
+            s4 = (buf->data[off+j+3*block] & 0xff);
+
+            if((abs(s1 - s3) < eq) &&
+                (abs(s1 - s2) > diff)) ++cc_1;
+
+            if((abs(s2 - s4) < eq) &&
+                (abs(s2 - s3) > diff)) ++cc_2;
+
+            off +=2*block;
+        }
+    }
+    
+    // compare results
+
+    cc = (int)((cc_1 + cc_2)*1000.0/(width*height));
+
+    flag = (cc > thres) ? 1:0;
+    
+    if(cc)
+        hb_log("flag: %i | cc: %i | cc1: %i | cc2: %i", flag, cc, cc_1, cc_2);
+        
+    return flag;
+}
+
+
 static void yadif_store_ref( const uint8_t ** pic, 
                              hb_filter_private_t * pv )
 {
@@ -520,13 +567,25 @@
     {        
         avpicture_fill( &pv->pic_out, pv->buf_out[0]->data, 
                         pix_fmt, width, height );
-        
-        avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
+
+        if( detect_comb(buf_in, width, height) )
+        {
+            hb_log("found combing!");
+            
+            avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
                                pix_fmt, width, height );
-        
-        hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            *buf_out = pv->buf_out[0];
 
-        *buf_out = pv->buf_out[0];
+        }
+        else
+        {
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+
+            *buf_out = buf_in;
+        }
         
         return FILTER_OK;
     }
@@ -551,50 +610,59 @@
         yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
         
         hb_buffer_copy_settings( pv->buf_settings, buf_in );
-
-        /* don't let 'work_loop' send a chapter mark upstream */
-        buf_in->new_chap  = 0;
 
+        /* don't let 'work_loop' send a chapter mark upstream */
+        buf_in->new_chap  = 0;
+
         pv->yadif_ready = 1;
         
         return FILTER_DELAY;
     }
 
-    /* Perform yadif and mcdeint filtering */
-    int frame;
-    for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )
+    if( detect_comb(buf_in, width, height) )
     {
-        int parity = frame ^ tff ^ 1;
-        
-        avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
-                        pix_fmt, width, height );
-        
-        yadif_filter( pv->pic_out.data, parity, tff, pv );
+        /* Perform yadif and mcdeint filtering */
+        hb_log("combing detected, passing to yadif");
 
-        if( pv->mcdeint_mode >= 0 )
+        int frame;
+        for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )
         {
-            avpicture_fill( &pv->pic_in,  pv->buf_out[(frame^1)]->data, 
+            int parity = frame ^ tff ^ 1;
+            
+            avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
                             pix_fmt, width, height );
-            
-            mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
-            
-            *buf_out = pv->buf_out[ (frame^1)];
+    
+            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 );
+    
+                mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
+    
+                *buf_out = pv->buf_out[ (frame^1)];
+            }
+            else
+            {
+                *buf_out = pv->buf_out[!(frame^1)];
+            }
         }
-        else
-        {
-            *buf_out = pv->buf_out[!(frame^1)];
-        }
     }
-    
+    else
+    {
+        *buf_out = buf_in;
+    }
+
     /* Copy buffered settings to output buffer settings */
     hb_buffer_copy_settings( *buf_out, pv->buf_settings );
     
     /* Replace buffered settings with input buffer settings */
     hb_buffer_copy_settings( pv->buf_settings, buf_in );    
-
-    /* don't let 'work_loop' send a chapter mark upstream */
-    buf_in->new_chap  = 0;
 
+    /* don't let 'work_loop' send a chapter mark upstream */
+    buf_in->new_chap  = 0;
+
     return FILTER_OK;
 }
 
Oh...if you're running this at the same time as VFR, be aware that you need to include --detelecine in your command line as well as --vfr. Otherwise, the deinterlacing filter will be applied first. Small issue I have to correct with VFR and the filter chain order.
van
Veteran User
Posts: 417
Joined: Wed Aug 29, 2007 6:35 am

Re: Combing Detection

Post by van »

Ohh cool! Seems to work great. Here's a diff that I think gets rid of the out of order frames. For yadif it keeps track of the previous frames' 'comb' state in a bitmask and does a deinterlace on the previous frame if it was combed (using the copy that yadif saved in its ref array). Here's what I changed in your code:

Code: Select all

--- libhb/deinterlace.c.jbr	2008-02-08 17:11:19.000000000 -0800
+++ libhb/deinterlace.c.new	2008-02-08 17:46:23.000000000 -0800
@@ -41,6 +41,7 @@
     int              yadif_mode;
     int              yadif_parity;
     int              yadif_ready;
+    int              comb;
     
     uint8_t        * yadif_ref[4][3];        
     int              yadif_ref_stride[3];
@@ -162,6 +163,26 @@
     }
 }
 
+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, 
@@ -603,6 +624,7 @@
     
     /* Store current frame in yadif cache */
     yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
+    pv->comb = (pv->comb << 1) | detect_comb(buf_in, width, height);
     
     /* If yadif is not ready, store another ref and return FILTER_DELAY */
     if( pv->yadif_ready == 0 )
@@ -619,7 +641,16 @@
         return FILTER_DELAY;
     }
 
-    if( detect_comb(buf_in, width, height) )
+    /* yadif & mcdeint work one frame behind so if the previous frame
+     * had combing, deinterlace it otherwise just output it. */
+    if(  (pv->comb & 2 ) == 0 )
+    {
+        /* previous frame not interlaced - copy cached input frame to buf_out */
+        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 */
         hb_log("combing detected, passing to yadif");
@@ -631,16 +662,16 @@
             
             avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
                             pix_fmt, width, height );
-    
+
             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 );
-    
+
                 mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
-    
+
                 *buf_out = pv->buf_out[ (frame^1)];
             }
             else
@@ -649,10 +680,6 @@
             }
         }
     }
-    else
-    {
-        *buf_out = buf_in;
-    }
 
     /* Copy buffered settings to output buffer settings */
     hb_buffer_copy_settings( *buf_out, pv->buf_settings );

The diff against head is here: http://pastebin.ca/896941
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Thanks van =)

Works great with yadif now.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Oh, just thought of another nifty thing this can allow. Since the detection works one frame at a time, we could run it during the preview scans and warn users if there's obvious interlacing, or even turn deinterlacing on automatically. It won't catch everything, but it sure would be helpful for CLI/wrapper users who don't get to see those previews to check for interlacing with their eyes.
sdm
Bright Spark User
Posts: 194
Joined: Mon Feb 19, 2007 4:53 pm

Re: Combing Detection

Post by sdm »

This sounds beautiful!
I've had to turn on deinterlace (slow) on several music videos to decomb interlaced scene cut frames that vfr+detelecine left behind.
Great work again.

--sdm.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Found a few minor bugs...like, uh, I'm not checking the chromas, only the luma ;p

I've got it working now with all three, and I'll have an updated patch up sometime soon, after I clean it up.

I'm still not happy with the parameter values. But maybe what I want is impossible...something sensitive enough to catch tiny little mouseteeth in grainy old TV footage when something slightly moves, but discriminating enough to let pass unscathed thin lines of hand-drawn animation.
rhester
Veteran User
Posts: 2888
Joined: Tue Apr 18, 2006 10:24 pm

Re: Combing Detection

Post by rhester »

jbrjake wrote:I'm still not happy with the parameter values. But maybe what I want is impossible...something sensitive enough to catch tiny little mouseteeth in grainy old TV footage when something slightly moves, but discriminating enough to let pass unscathed thin lines of hand-drawn animation.
Short of allowing the user to override a default threshold, you're unlikely to ever achieve this level of nirvana (or at least I'm unaware of anyone else having done so). What you've done already surpasses pretty much everything on the planet in the "set it and forget it" category, including Nero. You should be thrilled with what you've accomplished! =)

Congratulations,

Rodney
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

rhester wrote:What you've done already surpasses pretty much everything on the planet in the "set it and forget it" category, including Nero. You should be thrilled with what you've accomplished! =)
Thanks, rhester. I appreciate that :-)
However, I should point out that this doesn't hold a candle to what berrinam's got going on in MeGUI by splicing together a bunch of AviSynth filters:
http://megui.svn.sourceforge.net/viewvc ... iew=markup
http://forum.doom9.org/showthread.php?t=104760

Here's an updated patch. It examines Cb and Cr as well as Y`, and I've moved the comb detection function to hb.c, so I can tie it into scanning later.

I've adjusted the parameters slightly. I'm using 2 as the threshold, and marking the frame as interlaced if at least two planes of the frame have a metric above that threshold. For now the parameters are stored as defines in hb.h, but it might be a good idea to have two modes, with one being more sensitive than the other.

http://pastebin.ca/898817

Code: Select all

Index: libhb/deinterlace.c
===================================================================
--- libhb/deinterlace.c	(revision 1252)
+++ libhb/deinterlace.c	(working copy)
@@ -41,6 +41,7 @@
     int              yadif_mode;
     int              yadif_parity;
     int              yadif_ready;
+    int              comb;
     
     uint8_t        * yadif_ref[4][3];        
     int              yadif_ref_stride[3];
@@ -115,6 +116,26 @@
     }
 }
 
+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, 
@@ -520,13 +541,28 @@
     {        
         avpicture_fill( &pv->pic_out, pv->buf_out[0]->data, 
                         pix_fmt, width, height );
-        
-        avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
+
+        int interlaced = ( hb_detect_comb(buf_in, width, height) );
+
+        if( interlaced )
+        {
+//            hb_log("found combing! %i", interlaced);
+            
+            avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
                                pix_fmt, width, height );
-        
-        hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            *buf_out = pv->buf_out[0];
 
-        *buf_out = pv->buf_out[0];
+        }
+        else
+        {
+            
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+
+            *buf_out = buf_in;
+        }
         
         return FILTER_OK;
     }
@@ -544,6 +580,7 @@
     
     /* Store current frame in yadif cache */
     yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
+    pv->comb = (pv->comb << 1) | hb_detect_comb(buf_in, width, height);
     
     /* If yadif is not ready, store another ref and return FILTER_DELAY */
     if( pv->yadif_ready == 0 )
@@ -551,50 +588,63 @@
         yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
         
         hb_buffer_copy_settings( pv->buf_settings, buf_in );
-
-        /* don't let 'work_loop' send a chapter mark upstream */
-        buf_in->new_chap  = 0;
 
+        /* don't let 'work_loop' send a chapter mark upstream */
+        buf_in->new_chap  = 0;
+
         pv->yadif_ready = 1;
         
         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 */
+        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 */
+//        hb_log("combing detected, passing to yadif");
         
-        avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
-                        pix_fmt, width, height );
-        
-        yadif_filter( pv->pic_out.data, parity, tff, pv );
-
-        if( pv->mcdeint_mode >= 0 )
+        int frame;
+        for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )
         {
-            avpicture_fill( &pv->pic_in,  pv->buf_out[(frame^1)]->data, 
+            int parity = frame ^ tff ^ 1;
+            
+            avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
                             pix_fmt, width, height );
-            
-            mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
-            
-            *buf_out = pv->buf_out[ (frame^1)];
+    
+            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 );
+    
+                mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
+    
+                *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 );
     
     /* Replace buffered settings with input buffer settings */
     hb_buffer_copy_settings( pv->buf_settings, buf_in );    
-
-    /* don't let 'work_loop' send a chapter mark upstream */
-    buf_in->new_chap  = 0;
 
+    /* don't let 'work_loop' send a chapter mark upstream */
+    buf_in->new_chap  = 0;
+
     return FILTER_OK;
 }
 
Index: libhb/hb.c
===================================================================
--- libhb/hb.c	(revision 1252)
+++ libhb/hb.c	(working copy)
@@ -427,6 +427,88 @@
 }
 
 /**
+ * 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)
+{
+    /* See if frame shows interlacing */
+    int j, k, n, off, block, cc_1, cc_2, cc, flag;
+    uint16_t s1, s2, s3, s4;
+    cc = 0; cc_1 = 0; cc_2 = 0;
+    
+    int thres = THRESHOLD;
+    int eq = COLOR_EQUAL;
+    int diff = COLOR_DIFF;
+    
+    int offset = 0;
+    
+    flag = 0;
+
+    for(k=0; k < 3; k++)
+    {
+        if( k == 1 )
+        {
+            offset = width * height;
+            width >>= 1;
+            height >>= 1;
+            thres >>= 1;
+            eq >>= 1;
+            diff >>= 1;
+        }
+        else if (k == 2 )
+        {
+            offset *= 5/4;
+        }
+        
+        block = width;
+        
+        for(j=0; j<block; ++j)
+        {
+            off=0;
+            
+            for(n=0; n<(height-4); n=n+2)
+            {
+                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);
+    
+                if((abs(s1 - s3) < eq) &&
+                    (abs(s1 - s2) > diff)) ++cc_1;
+    
+                if((abs(s2 - s4) < eq) &&
+                    (abs(s2 - s3) > diff)) ++cc_2;
+    
+                off +=2*block;
+            }
+        }
+        
+        // compare results
+    
+        cc = (int)((cc_1 + cc_2)*1000.0/(width*height));
+
+        flag += (cc > thres) ? 1:0;
+    }
+    
+    if(flag)
+//        hb_log("flag: %i | cc: %f | cc1: %i | cc2: %i", flag, (float)((cc_1 + cc_2)*1000.0/(width*height)), cc_1, cc_2);
+    
+    if (flag > 1)    
+        return 1;
+    else
+        return 0;
+}
+
+
+/**
  * Calculates job width and height for anamorphic content,
  *
  * @param job Handle to hb_job_t
Index: libhb/hb.h
===================================================================
--- libhb/hb.h	(revision 1252)
+++ 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);
+#define COLOR_EQUAL  10
+#define COLOR_DIFF   30
+#define THRESHOLD     2
+
 void          hb_get_preview( hb_handle_t *, hb_title_t *, int,
                               uint8_t * );
 void          hb_set_size( hb_job_t *, int ratio, int pixels );
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Here's a working draft of comb detection. This time, with comments.

I've got the function in hb.c, and the parameter defines in hb.h. I'll probably check that part in soon, without hooking it up to anything. One benefit of modularizing it like this is we can switch out to a better comb detection function if we can find one.

I've got it hooked up two different ways in this diff:

First, if runs on each preview frame in scan.c. If 6 or more frames show combing, it sets a title->interlaced variable to pass it on to the rest of the workflow. For now, this is only used by the CLI to give a "You should probably deinterlace this title" message but the idea is it'll eventually turn the filter on by itself. Say, if a job->automatic_filtering boolean is true.

The second way I've got it hooked up is like what's above in this thread. If interlacing is on, it will only actually deinterlace frames that need it, using either pp=fd or yadif. I think I'm going to wrap this stuff in a (if job->vfr) conditional since vfr is the main time you're going to want to selectively deinterlace. But I'm open to a separate job->comb_detect or another parameter to the deinterlacer settings or whatever.

Still not set on what threshold and color parameters to use for the detection. Right now I'm going for pretty high sensitivity, that deinterlaces some anime frames it doesn't really have to.

http://pastebin.ca/911473

Code: Select all

Index: test/test.c
===================================================================
--- test/test.c	(revision 1306)
+++ test/test.c	(working copy)
@@ -300,6 +300,12 @@
         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 1306)
+++ libhb/hb.c	(working copy)
@@ -427,6 +427,109 @@
 }
 
 /**
+ * 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 j, k, n, off, block, cc_1, cc_2, cc, flag;
+    uint16_t s1, s2, s3, s4;
+    cc = 0; 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;
+    
+    flag = 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 = (int)((cc_1 + cc_2)*1000.0/(width*height));
+        
+        /* But only really flag the plane as showing interlacing when it exceeds a threshold parameter.*/
+        flag += (cc > thres) ? 1:0;
+    }
+
+#if 0
+/* Debugging info */
+    if(flag)
+        hb_log("flag: %i | cc: %f | cc1: %i | cc2: %i", flag, (float)((cc_1 + cc_2)*1000.0/(width*height)), cc_1, cc_2);
+#endif
+    
+    /* When more than one plane shows combing, tell the caller. */
+    if (flag > 1)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+/**
  * Calculates job width and height for anamorphic content,
  *
  * @param job Handle to hb_job_t
Index: libhb/hb.h
===================================================================
--- libhb/hb.h	(revision 1306)
+++ 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);
+#define COLOR_EQUAL  10 // Sensitivity for detecting similar colors.
+#define COLOR_DIFF   30 // Sensitivity for detecting different colors
+#define THRESHOLD     2 // 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 1306)
+++ libhb/deinterlace.c	(working copy)
@@ -41,6 +41,7 @@
     int              yadif_mode;
     int              yadif_parity;
     int              yadif_ready;
+    int              comb;
     
     uint8_t        * yadif_ref[4][3];        
     int              yadif_ref_stride[3];
@@ -115,6 +116,26 @@
     }
 }
 
+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, 
@@ -521,12 +542,30 @@
         avpicture_fill( &pv->pic_out, pv->buf_out[0]->data, 
                         pix_fmt, width, height );
         
-        avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
+        /* Check for coming on the input frame */
+        int interlaced = ( hb_detect_comb(buf_in, width, height) );
+
+        if( interlaced )
+        {
+            /* Combing detected, deinterlace this frame. */
+#if 0
+            hb_log("found combing! %i", interlaced);
+#endif            
+            avpicture_deinterlace( &pv->pic_out, &pv->pic_in, 
                                pix_fmt, width, height );
-        
-        hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+            
+            *buf_out = pv->buf_out[0];
 
-        *buf_out = pv->buf_out[0];
+        }
+        else
+        {
+            /* No combing detected, pass input frame through unmolested.*/
+            hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+
+            *buf_out = buf_in;
+        }
         
         return FILTER_OK;
     }
@@ -545,56 +584,73 @@
     /* 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) | hb_detect_comb(buf_in, width, height);
+    
     /* If yadif is not ready, store another ref and return FILTER_DELAY */
     if( pv->yadif_ready == 0 )
     {
         yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
         
         hb_buffer_copy_settings( pv->buf_settings, buf_in );
-
-        /* don't let 'work_loop' send a chapter mark upstream */
-        buf_in->new_chap  = 0;
 
+        /* don't let 'work_loop' send a chapter mark upstream */
+        buf_in->new_chap  = 0;
+
         pv->yadif_ready = 1;
         
         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;
-        
-        avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
-                        pix_fmt, width, height );
-        
-        yadif_filter( pv->pic_out.data, parity, tff, pv );
-
-        if( pv->mcdeint_mode >= 0 )
+        /* previous frame not interlaced - copy cached input frame to buf_out */
+        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 */
+#if 0
+        hb_log("combing detected, passing to yadif");
+#endif        
+        int frame;
+        for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )
         {
-            avpicture_fill( &pv->pic_in,  pv->buf_out[(frame^1)]->data, 
+            int parity = frame ^ tff ^ 1;
+            
+            avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data, 
                             pix_fmt, width, height );
-            
-            mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
-            
-            *buf_out = pv->buf_out[ (frame^1)];
+    
+            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 );
+    
+                mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
+    
+                *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 );
     
     /* Replace buffered settings with input buffer settings */
     hb_buffer_copy_settings( pv->buf_settings, buf_in );    
-
-    /* don't let 'work_loop' send a chapter mark upstream */
-    buf_in->new_chap  = 0;
 
+    /* don't let 'work_loop' send a chapter mark upstream */
+    buf_in->new_chap  = 0;
+
     return FILTER_OK;
 }
 
Index: libhb/scan.c
===================================================================
--- libhb/scan.c	(revision 1306)
+++ libhb/scan.c	(working copy)
@@ -286,6 +286,7 @@
     hb_list_t     * list_es, * list_raw;
     hb_libmpeg2_t * mpeg2;
     int progressive_count = 0;
+    int interlacing[10];
     
     buf_ps   = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
     list_es  = hb_list_init();
@@ -407,7 +408,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;               
             }
@@ -440,7 +441,18 @@
         }
 
         buf_raw = hb_list_item( list_raw, 0 );
-
+        
+        /* Check preview for interlacing artifacts */
+        if( hb_detect_comb(buf_raw, title->width, title->height))
+        {
+            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 );
 
@@ -493,7 +505,7 @@
                     break;
                 }
         }
-
+        
 skip_preview:
         while( ( buf_raw = hb_list_item( list_raw, 0 ) ) )
         {
@@ -506,12 +518,32 @@
     title->crop[1] = EVEN( title->crop[1] );
     title->crop[2] = EVEN( title->crop[2] );
     title->crop[3] = EVEN( title->crop[3] );
-
+    
     hb_log( "scan: %dx%d, %.3f fps, autocrop = %d/%d/%d/%d",
             title->width, title->height, (float) title->rate /
             (float) title->rate_base, title->crop[0], title->crop[1],
             title->crop[2], title->crop[3] );
+            
+    /* 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;
+    }
+    
     ret = 1;
     goto cleanup;
 
Index: libhb/common.h
===================================================================
--- libhb/common.h	(revision 1306)
+++ libhb/common.h	(working copy)
@@ -435,6 +435,8 @@
 
     /* Job template for this title */
     hb_job_t  * job;
+    
+    int         detected_interlacing;
 };
 
 
rhester
Veteran User
Posts: 2888
Joined: Tue Apr 18, 2006 10:24 pm

Re: Combing Detection

Post by rhester »

jbrjake wrote:I think I'm going to wrap this stuff in a (if job->vfr) conditional since vfr is the main time you're going to want to selectively deinterlace. But I'm open to a separate job->comb_detect or another parameter to the deinterlacer settings or whatever.
If you want to get fancy, you should do motion-adaptive in addition to decombing so you can get maximum vertical resolution from effectively "still" frames (matching field contents) and decomb only the areas that need it (and you can combine these techniques in a single frame). That's how the hardcore doom9'ers do it, and will work well for pure-video content. Nirvana would be combining both of these with the detelecine routine so it becomes completely adaptive to all types of content and truly becomes a fire-and-forget tool that doesn't require tuning (except in extreme edge cases).

Rodney
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

rhester wrote:If you want to get fancy, you should do motion-adaptive in addition to decombing so you can get maximum vertical resolution from effectively "still" frames (matching field contents) and decomb only the areas that need it (and you can combine these techniques in a single frame).
Yeah yeah. I get it. You're too busy so you want me to port everything DRIVE does to libhb ;P

Anyway, just give it time. Once this is in place, it'll be possible to use the simple comb detection in the preview frames to turn on the filtering for the job and then do more sophisticated stuff for the frame selection during the actual encoding, by leveraging more of yadif. I think. Not that I understand yadif at all, right now.
dynaflash
Veteran User
Posts: 3820
Joined: Thu Nov 02, 2006 8:19 pm

Re: Combing Detection

Post by dynaflash »

jbrjake wrote:Not that I understand yadif at all, right now.
Not that that has stopped you in the past :P
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Yadif in English

Post by jbrjake »

EDIT: The info in this post isn't entirely correct. Will have better descriptions up when I'm sure of them.

So I decided to spend a few hours today deciphering yadif. Not an easy task, since I don't know jack about deinterlacing, and the variable names are...less than helpful. The main function, filter_line, actually has these as variable names: w, x, c, d, e, j, b, and f

I ended up going through the filter line by line, commenting *everything*. Hopefully this will be useful to someone else, although it's probably too verbose for checking in.

I'd appreciate it people more versed in striding arrays, dealing with YUV data, and doing cross-frame comparisons could verify I'm not totally misinterpreting the code.

Hopefully, it'll be possible to pick and choose portions of yadif to use to improve comb detection during encoding, without running the time-consuming parts of yadif constantly.

• Buffering frames:

Code: Select all

static void yadif_store_ref( const uint8_t ** pic,
                             hb_filter_private_t * pv )
{
    /* Made room in slot 2 of the yadif-ref array
       for this current stored reference.          */
    memcpy( pv->yadif_ref[3],
            pv->yadif_ref[0],
            sizeof(uint8_t *)*3 );

    memmove( pv->yadif_ref[0],
             pv->yadif_ref[1],
             sizeof(uint8_t *)*3*3 );

    /* Iterate through color planes */
    int i;
    for( i = 0; i < 3; i++ )
    {
        /* The source is the frame the function was passed,
           the reference is where is where it's going to be stored,
           in the yadif_ref array's second slot, in the slot for
           the current color plane being examined.                  */
        const uint8_t * src = pic[i];
        uint8_t * ref = pv->yadif_ref[2][i];
        
        /* Width and height will halved for Cb and Cr,
           and the stride offset is the pixel size of
           the color planes already examined.         */
        int w = pv->width[i];
        int h = pv->height[i];
        int ref_stride = pv->yadif_ref_stride[i];
        
        /* Iterate through the plane's horizontal lines. */
        int y;
        for( y = 0; y < pv->height[i]; y++ )
        {
            /*  Copy the line to the reference. */
            memcpy(ref, src, w);
            
            /* Move on to the next line to copy. */
            src = (uint8_t*)src + w;
            ref = (uint8_t*)ref + ref_stride;
        }
    }
}
• Splitting up the buffered frames into planes for line-by-line filtering:

Code: Select all

static void yadif_filter( uint8_t ** dst,
                          int parity,
                          int tff,
                          hb_filter_private_t * pv )
{
    /* Iterate through color planes */
    int i;
    for( i = 0; i < 3; i++ )
    {
        /* Resolution is halved for Cb/Cr, and stride is
           an offset the size of the planes already covered. */
        int w = pv->width[i];
        int h = pv->height[i];
        int ref_stride = pv->yadif_ref_stride[i];
        
        /* Iterate through the plane's horizontal lines.*/
        int y;
        for( y = 0; y < h; y++ )
        {
            /* Treat alternating lines differently depending on parity. */
            if( (y ^ parity) &  1 )
            {
                /* Set up pointers referring to this line in the plane for
                   the previous, current, and next frames and a buffer for the output. */
                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];

                /* Do the hard part of comparing the three lines from times -1, 0, and 1. */
                yadif_filter_line( dst2, prev, cur, next, i, parity ^ tff, pv );
            }
            else
            {
                memcpy( &dst[i][y*w],
                        &pv->yadif_ref[1][i][y*ref_stride],
                        w * sizeof(uint8_t) );
            }
        }
    }
}
• Actually filtering lines by comparing the previous, current, and next frames.

Code: Select all

static void yadif_filter_line( uint8_t *dst,
                               uint8_t *prev,
                               uint8_t *cur,
                               uint8_t *next,
                               int plane,
                               int parity,
                               hb_filter_private_t * pv )
{
    /* Depending on parity, either look at the previous and current frames or current and next frames. */
    uint8_t *prev2 = parity ? prev : cur ;
    uint8_t *next2 = parity ? cur  : next;
    
    /* Width will be half Y's width for Cr and Cb */
    int w = pv->width[plane];
    
    /* Stride is the offset of the planes already covered.*/
    int refs = pv->yadif_ref_stride[plane];
    
    /* Iterate through the line's pixels from left to right. */
    int x;
    for( x = 0; x < w; x++)
    {
        /* Pixel in line above? */
        int c              = cur[-refs];
        
        /* Average of this pixel in this line in the frames before and after */
        int d              = (prev2[0] + next2[0])>>1;
        
        /* Pixel in line below? */
        int e              = cur[+refs];

        /* Energy difference of this pixel in this line in the frames before and after. */
        int temporal_diff0 = ABS(prev2[0] - next2[0]);

        /* Average of:
           the energy difference of the pixel in the line above in the frame before and the current frame, and
           the energy difference of the pixel in the line below in the frame before and the current frame.
           Over 8% of the thread's cycles are spent on this. */ 
        int temporal_diff1 = ( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) ) >> 1;

        /* Average of:
           the energy difference of the pixel in the line above in the frame after and the current frame, and
           the energy difference of the pixel in the line below in the frame after and the current frame.
           Over 9% of the thread's cycles are spent on this. */ 
        int temporal_diff2 = ( ABS(next[-refs] - c) + ABS(next[+refs] - e) ) >> 1;

        /* Use the greatest average temporal diff, be it between:
           the lines before and after,
           the lines above and below in previous and current,
           or the lines aove and below in the current and next.
           Over 4% of the thread's cycles are spent on this. */
        int diff           = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2);

        /* Spatial prediction is the average of the pixels above and below. */
        int spatial_pred   = (c+e)>>1;
        
        /*  The spatial score is the sum of the energy differences of:
            The pixel to the left in the line above minus the pixel to the left in the line below plus...
            The pixel in the line below minus the pixel in the line above, plus...
            The pixel to the right in the line above minus the pixel to the right in the line below...
            Minus one.
            Over 9% of the thread's cycles are spent on this. */
        int spatial_score  = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) +
                             ABS(cur[-refs+1] - cur[+refs+1]) - 1;


/* The score is the sum of the diferences of a bunch of pixels in different places. It's called 2-4 times,
    and uses the plurality of the thread's cycles -- 14% of the first nested call, 16% on the second..
   PASS 1: j = -1
   The pixel 2 to the left in the line above minus the pixel in the line below, plus...
   The pixel 1 to the left in the line above minus the pixel 1 to the right in the line below, plus...
   The pixel in the line above minus the pixel 2 to the right in the line below.
   If the spatial score is bigger,
        Replace it with this score and before doing PASS 2 do: 
        Spatial pred = average energy of the pixel 1 to the left in the line above and the pixel 1 to the right in the line below.
   PASS 2: j = -2 ONLY CALLED IF SPATIAL SCORE IS BIGGER THAN SCORE IN PASS 1
   The pixel 3 to the left in the line above minus the pixel 1 to the right in the line below, plus...
   The pixel 2 to the left in the line above minus the pixel 2 to the right in the line below, plus...
   The pixel 1 to the left in the line above minus the pixel 3 to the right in the line below.
   If the spatial score is bigger, 
        Replace it with this score and do:
        Spatial pred = average energy of the pixel 2 to the left in the line above and the pixel 2 to the right in the line below.
   PASS 3: j = 1
   The pixel in the line above minus the pixel 2 to the left in the line below, plus...
   The pixel 1 to the right in the line above minus the pixel 1 to the left in the line below, plus...
   The pixel 2 to the right in the line above minus the pixel in the line below.
   If the spatial score is bigger, 
        Replace it with this score and before doing PASS 4 do: 
        Spatial pred = average energy of the pixel 1 to the right in the line above and the pixel 1 to the left in the line below.
   PASS 4: j = 2 ONLY CALLED IF SPATIAL SCORE IS BIGGER THAN SCORE
   The pixel 1 to the right in the line above minus the pixel 3 to the left in the line below, plus...
   The pixel 2 to the right in the line above minus the pixel 2 to the left in the line below, plus...
   The pixel 3 to the right in the line above minus the pixel 1 to the left in the line below.
   If the spatial score is bigger, 
        Replace it with this score and do:
        Spatial pred = average energy of the pixel 2 to the right in the line above and the pixel 2 to the left in the line below. */
   
#define YADIF_CHECK(j)\
        {   int score = ABS(cur[-refs-1+j] - cur[+refs-1-j])\
                      + ABS(cur[-refs  +j] - cur[+refs  -j])\
                      + ABS(cur[-refs+1+j] - cur[+refs+1-j]);\
            if( score < spatial_score ){\
                spatial_score = score;\
                spatial_pred  = (cur[-refs  +j] + cur[+refs  -j])>>1;\

        YADIF_CHECK(-1) YADIF_CHECK(-2) }} }}
        YADIF_CHECK( 1) YADIF_CHECK( 2) }} }}

        if( pv->yadif_mode < 2 )
        {
            /* Spatial mode */
            
            /* Average energy of the pixel 2 lines above in the last frame and the pixel 2 lines above in the next frame. */
            int b = (prev2[-2*refs] + next2[-2*refs])>>1;
            
            /* Average energy of the pixel 2 lines below in the last frame and the pixel 2 lines below in the next frame. */
            int f = (prev2[+2*refs] + next2[+2*refs])>>1;
            
            /* Which is bigger?
               The difference between
                   d, The average of this pixel in this line in the frames before and after and
                   e, The pixel in the line below.
               Or the difference between
                   d, The average of this pixel in this line in the frames before and after and
                   c, The pixel in the line above.
               Or the smaller of
                   The difference between:
                        b. The average energy of the pixel 2 lines above in the last frame and the pixel 2 lines above in the next frame and
                        c, The pixel in the line above
                    And the difference between:
                        f, The average energy of the pixel 2 lines below in the last frame and the pixel 2 lines below in the next frame and
                        e, The pixel in the line below. */
            int max = MAX3(d-e, d-c, MIN(b-c, f-e));
            
            /* Which is smaller?
               The difference between
                   d, The average of this pixel in this line in the frames before and after and
                   e, The pixel in the line below.
               Or the difference between
                   d, The average of this pixel in this line in the frames before and after and
                   c, The pixel in the line above.
               Or the greater of
                   The difference between:
                        b. The average energy of the pixel 2 lines above in the last frame and the pixel 2 lines above in the next frame and
                        c, The pixel in the line above
                    And the difference between:
                        f, The average energy of the pixel 2 lines below in the last frame and the pixel 2 lines below in the next frame and
                        e, The pixel in the line below. */
            int min = MIN3(d-e, d-c, MAX(b-c, f-e));
            
            /* Set the dfference to the greatest of:
                The largest temporal diff
                The minimum spatial diff
                The largest spatial diff
                Nearly 17% of the thread's cycles are spent figuring this out. */
            diff = MAX3( diff, min, -max );
        }

        if( spatial_pred > d + diff )
        {
            /*  If the average energy of the pixel 1-2 to the left in the line above and the pixel 1-2 to the right in the line below is > than
                the average of this pixel in this line in the frames before and after plus the largest spatial or temporal diff...
                Then set the spatial pred. to the avg. of this pixel in the frames before and after plus the largest spatial or temporal diff  */
            spatial_pred = d + diff;
        }
        else if( spatial_pred < d - diff )
        {
            /* Otherwise set spatial pred. to the avg. of this pixel in the frames before and after minuz the largest spatial or temporal diff */
            spatial_pred = d - diff;
        }
        
        /* Use this new prediction as the output pixel. */
        dst[0] = spatial_pred;

        /* Move on to the next pixel. */
        dst++;
        cur++;
        prev++;
        next++;
        prev2++;
        next2++;
    }
}
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

After staring at the code for a couple of weeks, I've put together a wiki article diagramming yadif_filter_line:
http://trac.handbrake.fr/wiki/yadif

It's pretty much a visual guide to yadif's deinterlacing algorithm.

Still not sure I understand it entirely, but I've gotten feedback that the bright colors delight young children, so at least it's good for something ;>
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Thoughts on where to go from here with comb detection

Post by jbrjake »

1. Warn when a title is obviously interlaced because a lot of the previews show combing.

2. Do rough frame-by-frame combing detection, and route around the deinterlacer when the frame doesn't have obvious combing. This is fast, and works with ffmpeg (fast) deinterlacing as well as yadif.

Once step 2 is stable, it should included in the app as an automated filtering option, a rebranded VFR. It'd run VFR with pullup using -1 breaks* and frame-by-frame comb detection on every source. Or, it could be separated out from VFR as a new deinterlacing mode. Right now, locally, I'm uncommitted--I still have it running all the time.

3. Do comb detection on groups of, say, 4 lines at a time. This is pretty much what the current comb detection does, except it goes through every chunk of 4 lines in the frame at once. The difference would be linking the results to yadif_filter, so it'll only call yadif_filter_line when combing is detected in those 4 lines. This is slower, and only works with yadif, since ffmpeg deinterlacing works with whole frames. Of course, we could rip the algorithm out of ffmpeg, do it in libhb like we do yadif, and then there'd be precise enough control to only deinterlace specific lines of a frame. Might be a good idea for debugging, too...allows testing of this comb detection stuff without the timesink that is yadif.

4. Do pixel-by-pixel detection. In yadif_filter_line, only generate a prediction for the current pixel when there appears to be interlacing. A function would run on every frame to generate a table of what pixels to deinterlace, and yadif_filter_line would route around the heavy lifting for ones that didn't need it, and just pass through cur[0] as dst[0]. This seems like the best way to go about things, but requires some hard-core image analysis know-how I lack. Unless we just do the current comb detection on individual pixels ("Is the current pixel more similar to the one below, or the one two below?").

5. Even cooler would be what name99 suggested in a Ponies thread...going beyond the basic differences check for combing that the algorithm from 32detect uses. Instead, break things up into macroblocks for the spatial checks. Then...and this is the part that I think would be awesome...patch libmpeg2 to pass through everything it learns from decoding the frame, and use that to help decide:
name99 wrote:Detection requires us to define anomalous fields. This is, presumably, a pair of fields that we expect to be part of a single frame but they are not. This could presumably be done through a combination of
- defining an energy for each macroblock concentrated in the difference between even and odd lines. This could be something as simple as subtract each even luma line in an 8x8 from the corresponding odd line, sum the absolute diffs, and compare with the overall energy in the block. If we have enough of these at small values, or perhaps a few at large values, we flag the frame.
- using the various flags MPEG2 provides to encode interlaced blocks (eg the alternative IDCT pattern and the alternative macroblock motion stuff). (This of course requires the ability of the decoder to annotate the decoded frame that it then sends on to the encoder stage. But this doesn't have to be too difficult. Just add a new field in the decoded frame structure that can hold a block of hints, and have the decoder just sets these hints as it's doing it's thing. A bunch of stores that nothing else depends on, at the point where you parse the IDCT pattern type and macroblock motion stuff shouldn't slow down the code at all.
Anyway, that's beyond me, but...I hope someone pursues it :-)

In the mean time, I'm thinking about how to schedule 1 through 4. 1 could cause issues if it tries to detect on a preview that wasn't successfully generated (DVDReadBlocks Failed or whatever). 2 is simply a matter of tuning parameters to give decent performance with interlaced material without decimating picture quality on animation. 3 and 4 will require some experimentation. I've played around with them a little but I'm still cautious. It's tough to find ways to impinge on the yadif process without breaking things. Although, my previous attempts were prior to diagramming it....

So. If anyone is still following this, here's a diff for 1 and 2:
http://handbrake.pastebin.ca/930493

Code: Select all

Index: test/test.c
===================================================================
--- test/test.c	(revision 1331)
+++ 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 1331)
+++ 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 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 1331)
+++ 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);
+#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 1331)
+++ libhb/deinterlace.c	(working copy)
@@ -42,6 +42,8 @@
     int              yadif_parity;

     int              yadif_ready;

 

+    int              comb;

+

     uint8_t        * yadif_ref[4][3];

     int              yadif_ref_stride[3];

 

@@ -58,6 +60,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 +92,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 +100,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 +126,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 +155,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 +242,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 +276,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 +295,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 +466,9 @@
     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->yadif_ready    = 0;

     pv->yadif_mode     = YADIF_MODE_DEFAULT;

     pv->yadif_parity   = YADIF_PARITY_DEFAULT;

@@ -451,6 +564,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 +636,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 = ( hb_detect_comb(buf_in, width, height) );

+        

+        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 +678,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) | hb_detect_comb(buf_in, width, height);

+

     /* If yadif is not ready, store another ref and return FILTER_DELAY */

     if( pv->yadif_ready == 0 )

     {

@@ -559,32 +695,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 1331)
+++ 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))
+        {
+            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 1331)
+++ libhb/common.h	(working copy)
@@ -426,6 +426,7 @@
     int         rate;
     int         rate_base;
     int         crop[4];
+    int         detected_interlacing;
 
     uint32_t    palette[16];
 
It's rather similar to my last one. However, it comes after saintdev's vigorous Campaign Against Non-Uniform White Space, so it'll actually apply to the svn head. I've also modified the comb detection. I've gone back to transcode's default threshold of 9. Instead of returning true when 2 or more planes are above the threshold, I'm returning true when any of the three are above it -- same as transcode. I'd wanted something more sensitive, but rhester finally convinced me to give up. This gives me good results with relatively recent TV (interlaced Comedy Central shows from the late 90s like Strangers With Candy). It doesn't deinterlace anime to a horrific extent. Does a medicore job on older, lower quality footage like early Kids in the Hall episodes, where it misses just enough combing to be noticeable.

*Note: I've found that using the default break sensitivity for pulllup will drop too many frames from fully interlaced material. I think it gets tripped up when the camera is steady and nothing is moving for a couple of frames. Not seeing interlacing, it thinks the frames are progressive and starts to anticipate a pulldown pattern that doesn't exist. Setting -1 for breaks (--detelecine="1:1:4:4:-1:0") fixes that. Since it also, as van discovered, helps with PAL->NTSC Film->NTSC Video transfers, I'll probably make that the default soon enough.
Last edited by jbrjake on Thu Mar 06, 2008 5:10 pm, edited 2 times in total.
Reason: Updated patches to apply to latest SVN.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Ooops, didn't realize van's fix_aspect changes broke my scan.c changes. Patch above updated.
sdm
Bright Spark User
Posts: 194
Joined: Mon Feb 19, 2007 4:53 pm

Re: Combing Detection

Post by sdm »

jbrjake, your combing detection strategy seems very thorough and ambitious and awesome!

I applied the patch to the svn a couple of days ago that is supposed to implement parts 1 and 2.

I think I might be doing something wrong because if I encode with vfr and detelecine and deinterlace,
frames that aren't combed are not bypassing the deinterlacer.

These two examples show screengrabs with and without slow deinterlace:

Image
Image

Notice how the fine details are degraded by the deinterlace filter. This is something I'm trying to avoid.
(please disregard the posterization caused by .GIF compression)

If I don't turn on any deinterlace filter, I'm still ocasionally seeing some 100% combed frames at the cut between two scenes (upper field from one scene, combined with lower field of another scene in one frame) like this:

Image

Ideally the comb detection will catch these frames and deinterlace them, but leave non-combed alone.

Can you give me any suggestion as to what I'm doing wrong, or maybe its possible the comb detection isn't working properly?
Any thoughts?

thanks,
--sdm.
Last edited by sdm on Sat Mar 08, 2008 9:26 pm, edited 1 time in total.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Activity log, please. I need to know the order in which the filters were being applied, and with what parameters.

BTW, as a side-effect of all that time spent diagramming yadif, I figured out why it's decimating progressive frames, as you've demonstrated in so many screenshots. I had thought it did some kind of combing checks to detect inter-frame versus inter-field motion, but it doesn't. It always throws out half the lines of any frame it processes, and replaces them with a guess. The guess starts out as the average of the lines above and below, before being adjusted based on guessed motion. Further details on the wiki. So that's what all this comb detection talk is about -- finding ways to avoid yadif filtering whenever possible.
sdm
Bright Spark User
Posts: 194
Joined: Mon Feb 19, 2007 4:53 pm

Re: Combing Detection

Post by sdm »

I'm just so happy you're working on this!
btw, don't you think the screengrabs help illustrate things? I do.

--sdm.

Code: Select all

Here is the activity log:
[14:23:15] hb_init: checking cpu count
[14:23:15] hb_init: starting libhb thread
[14:23:15] thread b00c5000 started ("libhb")
[14:23:50] macgui: trying to open video_ts folder (parent directory chosen)
[14:23:50] hb_scan: path=/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts, title_index=0
[14:23:50] thread b0a45000 started ("scan")
[14:23:50] scan: trying to open with libdvdread
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[14:23:50] file is MPEG Transport Stream
[14:23:50] hb_ts_stream_find_pids - found the following PIDS
[14:23:50]     Video PIDS : 
[14:23:50]       0x44 (68)
[14:23:50]     Audio PIDS : 
[14:23:50]       0x45 (69)
[14:23:50] hb_sample_pts: pts 945945 at 5843040
[14:23:50] hb_sample_pts: pts 2861861 at 17547920
[14:23:50] hb_sample_pts: pts 4789789 at 29192828
[14:23:50] hb_sample_pts: pts 6492492 at 40847324
[14:23:50] hb_sample_pts: pts 8177177 at 52553144
[14:23:50] hb_sample_pts: pts 9960960 at 64167220
[14:23:50] hb_sample_pts: pts 11651651 at 75836944
[14:23:50] hb_sample_pts: pts 13249249 at 87506668
[14:23:50] hb_sample_pts: pts 14810810 at 99176580
[14:23:50] hb_sample_pts: pts 16417417 at 110839912
[14:23:50] hb_sample_pts: pts 18093093 at 122519600
[14:23:50] hb_sample_pts: pts 19759759 at 134230872
[14:23:50] hb_sample_pts: pts 21339339 at 145844948
[14:23:50] hb_sample_pts: pts 22978978 at 157547384
[14:23:50] hb_sample_pts: pts 24639639 at 169173116
[14:23:50] hb_sample_pts: pts 26390390 at 180834192
[14:23:50] transport stream pid 0x45 (type 0x81) is AC-3 audio id 0x80bd
[14:23:50] scan: decoding previews for title 1
[14:23:50] scan: preview 1
[14:23:50] scan: AC3, rate=48000Hz, bitrate=448000
[14:23:50] scan: preview 2
[14:23:50] scan: preview 3
[14:23:50] scan: preview 4
[14:23:50] scan: preview 5
[14:23:50] scan: preview 6
[14:23:50] scan: preview 7
[14:23:50] scan: preview 8
[14:23:50] scan: preview 9
[14:23:50] scan: preview 10
[14:23:50] scan: 10 previews, 720x480, 29.970 fps, autocrop = 0/2/8/10, aspect 4:3
[14:23:50] hb_stream_update_audio: id=80bd, lang=English (AC3) (2.0 ch), 3cc=eng, rate = 48000, bitrate = 448000, flags = 0x2 (2)
[14:23:50] scan: title (0) job->width:624, job->height:480
[14:23:50] thread b0a45000 exited ("scan")
[14:23:50] thread b0a45000 joined ("scan")
[14:23:50] libhb: scan thread found 1 valid title(s)
[14:24:43] thread b07bb000 started ("work")
[14:24:43] 1 job(s) to process
[14:24:43] starting job
[14:24:43]  + device /Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts
[14:24:43]  + title 1, chapter(s) 1 to 1
[14:24:43]  + 720x480 -> 704x480, crop 0/0/8/8
[14:24:43]  + filters
[14:24:43]    + Detelecine (pullup) (default settings)
[14:24:43]    + Deinterlace (ffmpeg or yadif/mcdeint) (0)
[14:24:43]  + video frame rate: variable (detected 29.970 fps)
[14:24:43]  + video quality 0.66
[14:24:43]  + PixelRatio: 1, width:704, height: 480
[14:24:43]  + encoder x264
[14:24:43]    + x264 options: ref=3:bframes=3:subq=6:me=umh:mixed-refs=1:direct=auto:weightb=1:brdo=1:bime=1:merange=24:analyse=all:8x8dct=1:no-fast-pskip=1
[14:24:43]  + audio 160 kbps, 48000 Hz
[14:24:43]  + encoder faac
[14:24:43]    + 80bd, English (AC3) (2.0 ch)
[14:24:43]      + Requested mixdown: Stereo (HB_AMIXDOWN_STEREO)
[14:24:43]      + Actual mixdown: Stereo (HB_AMIXDOWN_STEREO)
[14:24:43] thread b083d000 started ("reader")
[14:24:43]  + output: /Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days SlowerDeint.m4v
[14:24:43] thread b08bf000 started ("muxer")
[14:24:43] thread b0a45000 started ("MPEG-2 decoder (libmpeg2)")
[14:24:43] thread b0ac7000 started ("Renderer")
[14:24:43] encx264: keyint-min: 30, keyint-max: 300
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[14:24:43] encx264: encoding with stored aspect 8/9
[14:24:43] encx264: Encoding at constant RF 17.340000
[14:24:43] encx264: opening libx264 (pass 0)
x264 [info]: using SAR=8/9
x264 [info]: using cpu capabilities: MMX MMXEXT SSE SSE2 Cache64 
[14:24:43] thread b0b49000 started ("H.264/AVC encoder (libx264)")
No accelerated IMDCT transform found
[14:24:43] thread b0bcb000 started ("AC3 decoder")
[14:24:43] a52_syncinfo failed
[14:24:43] a52_syncinfo ok
[14:24:43] thread b0c4d000 started ("AAC encoder (libfaac)")
[14:24:43] sync: expecting 8859 video frames
[14:24:43] sync: first pts is 0
[14:24:43] Sync: Audio discontinuity (sequence: vid 205 aud 73) (pts 25210800 < 25300800 < 25390800)
[14:24:43] Sync: Audio joined Video after discontinuity at PTS 2880
[14:24:44] macgui: Using preset: music videos
[14:27:16] hb_ts_stream_decode - eof
[14:27:16] hb_stream_read - buffer after decode has zero length data
[14:27:16] reader: done
[14:27:16] thread b083d000 exited ("reader")
[14:27:22] sync: got 9036 frames, 8859 expected
[14:27:23] thread b0b49000 exited ("H.264/AVC encoder (libx264)")
[14:27:23] thread b0bcb000 exited ("AC3 decoder")
[14:27:23] thread b0a45000 exited ("MPEG-2 decoder (libmpeg2)")
[14:27:23] thread b0a45000 joined ("MPEG-2 decoder (libmpeg2)")
[14:27:23] thread b0c4d000 exited ("AAC encoder (libfaac)")
[14:27:23] thread b0ac7000 exited ("Renderer")
[14:27:23] thread b0ac7000 joined ("Renderer")
[14:27:23] render: lost time: 5453452 (1816 frames)
[14:27:23] render: gained time: 5452702 (7028 frames) (750 not accounted for)
[14:27:23] render: average dropped frame duration: 3003
[14:27:23] fifo_close: trashing 1 buffer(s)
[14:27:23] fifo_close: trashing 2 buffer(s)
[14:27:23] thread b0b49000 joined ("H.264/AVC encoder (libx264)")
x264 [info]: slice I:36    Avg QP:14.50  size: 29163  PSNR Mean Y:51.20 U:53.28 V:57.52 Avg:52.02 Global:51.60
x264 [info]: slice P:5605  Avg QP:17.34  size: 11807  PSNR Mean Y:48.42 U:51.14 V:54.50 Avg:49.31 Global:49.09
x264 [info]: slice B:1576  Avg QP:20.15  size:  6339  PSNR Mean Y:46.18 U:50.28 V:53.26 Avg:47.29 Global:47.14
x264 [info]: mb I  I16..4: 32.9% 52.3% 14.8%
x264 [info]: mb P  I16..4: 14.5% 15.0%  2.4%  P16..4: 38.3% 12.2%  4.6%  0.3%  0.1%    skip:12.6%
x264 [info]: mb B  I16..4:  1.0%  1.4%  0.4%  B16..8: 28.9%  2.7%  5.2%  direct:11.1%  skip:49.4%
x264 [info]: 8x8 transform  intra:47.3%  inter:67.1%
x264 [info]: direct mvs  spatial:99.9%  temporal:0.1%
x264 [info]: ref P  62.4% 22.9% 14.7%
x264 [info]: ref B  85.3% 14.7%
x264 [info]: SSIM Mean Y:0.9904206
x264 [info]: PSNR Mean Y:47.946 U:50.965 V:54.246 Avg:48.879 Global:48.591 kb/s:2565.30
[14:27:23] thread b0bcb000 joined ("AC3 decoder")
[14:27:23] thread b0c4d000 joined ("AAC encoder (libfaac)")
[14:27:23] thread b083d000 joined ("reader")
[14:27:23] mux: file size, 83412549 bytes
[14:27:23] mux: track 0, 77218324 bytes, 2049.86 kbps
[14:27:23] mux: track 1, 6024925 bytes, 159.94 kbps
[14:27:23] mux: overhead, 7.93 bytes per frame
[14:27:23] thread b08bf000 exited ("muxer")
[14:27:23] thread b08bf000 joined ("muxer")
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 0 buffer(s)
[14:27:23] fifo_close: trashing 5 buffer(s)
[14:27:23] Freed 57 buffers of size 512
[14:27:23] Freed 85 buffers of size 1024
[14:27:23] Freed 2048 buffers of size 2048
[14:27:23] Freed 0 buffers of size 4096
[14:27:23] Freed 0 buffers of size 8192
[14:27:23] Freed 130 buffers of size 16384
[14:27:23] Freed 0 buffers of size 32768
[14:27:23] Freed 193 buffers of size 518400
[14:27:23] Allocated 106491648 bytes of buffers on this pass and Freed 106491648 bytes, 0 bytes leaked
[14:27:23] thread b07bb000 exited ("work")
[14:27:23] thread b07bb000 joined ("work")
[14:27:23] libhb: work result = 0
<Mod- next time use

Code: Select all

 around Activity & Crash Logs>[/color]
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

I don't think you have the patch applied. It add a logging line to the end of the encoding process, when the deinterlacer is closed:

Code: Select all

deinterlacer: filtered 1000 | unfiltered 2000 | total 3000
...and I don't see that in your log.
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

So. Seems my patch above, and pastebin, don't really get along. Oh well. That can be dealt with, but first...

Dynaflash informed me that comb detection is screwing up credits at the end of progressive content. This makes sense -- it sees the serifs, thinks they're interlacing lines, calls the deinterlacer, and there you go...half the detail gone.

Eko has suggested to me the idea of using pullup's diagnostic information to know when something is telecined. Anything that pullup is sure is progressive is going to follow a 3:2:3:2:3:2 pattern for frame length after running through the filter, and interlaced material will go 2:2:2:2:2 steadily. He uses this in a script that works with mencoder. He tests a small portion of a film and decides whether or not to run pullup on the whole thing based on the results.

Of course it's possible to do far more interesting things working inside libhb, as opposed to with CLIs. By passing through the frame length as part of the buffer structure pullup outputs for every frame, that info can reach the rest of the lib. So the filter's caller, render.c, can keep track of what the outputs are. This means it can track whether or not it's got a 3:2 pattern going on, during the encode process. With this higher vantage point, it can then start including a more useful bit of info in the buffer struct -- whether or not the frame is part of a telecined sequence. Right now I just have it basing that decision on whether either the current or the previous frame had a length of 3.

Deinterlace.c is called after detelecine.c. So when it sends buffers to the comb detection function in hb.c, it passes along whether or not the frame is telecined. If it is, comb_detect skips its work and returns 0, and the deinterlacer in turn skips its work and passes the frame through untouched.

Hopefully, this will keep credits of progressive movies from being misidentified as something worth deinterlacing.

Now, back to this whole pastebin matter.
I still don't know what's up with that.

I've put the patch up on my 1and1 at http://ubiquit.us/handbrake/comb-detect ... ebin.patch but beware, it seems that trying to download that through Safari will mess up just like pastebin.

So do it at the command line. For example, if you're in the trunk source directory, you could do:

Code: Select all

curl http://ubiquit.us/handbrake/comb-detect-vs-pastebin.patch > comb-detect.diff
patch -p0 < comb-detect.diff
sdm
Bright Spark User
Posts: 194
Joined: Mon Feb 19, 2007 4:53 pm

Re: Combing Detection

Post by sdm »

Hi Jbrjake,

I applied the patch and it all went smooth. Thanks.

I'm curious how to get the comb detect to work.
if I turn on deinterlace in 'picture settings' (slow or slower), all frames are run through the deinterlacer

I found this in the activity:

Code: Select all

[17:27:49] deinterlacer: filtered 7218 | unfiltered 0 | total 7218
here is all the activity:

Code: Select all

[17:55:02] hb_init: checking cpu count
[17:55:03] hb_init: starting libhb thread
[17:55:03] thread b00c5000 started ("libhb")
[17:55:24] macgui: trying to open video_ts folder (parent directory chosen)
[17:55:24] hb_scan: path=/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts, title_index=0
[17:55:24] thread b07bb000 started ("scan")
[17:55:24] scan: trying to open with libdvdread
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[17:55:24] file is MPEG Transport Stream
[17:55:24] hb_ts_stream_find_pids - found the following PIDS
[17:55:24]     Video PIDS : 
[17:55:24]       0x44 (68)
[17:55:24]     Audio PIDS : 
[17:55:24]       0x45 (69)
[17:55:24] hb_sample_pts: pts 945945 at 5843040
[17:55:24] hb_sample_pts: pts 2861861 at 17547920
[17:55:24] hb_sample_pts: pts 4789789 at 29192828
[17:55:24] hb_sample_pts: pts 6492492 at 40847324
[17:55:24] hb_sample_pts: pts 8177177 at 52553144
[17:55:24] hb_sample_pts: pts 9960960 at 64167220
[17:55:24] hb_sample_pts: pts 11651651 at 75836944
[17:55:24] hb_sample_pts: pts 13249249 at 87506668
[17:55:24] hb_sample_pts: pts 14810810 at 99176580
[17:55:24] hb_sample_pts: pts 16417417 at 110839912
[17:55:24] hb_sample_pts: pts 18093093 at 122519600
[17:55:24] hb_sample_pts: pts 19759759 at 134230872
[17:55:24] hb_sample_pts: pts 21339339 at 145844948
[17:55:24] hb_sample_pts: pts 22978978 at 157547384
[17:55:24] hb_sample_pts: pts 24639639 at 169173116
[17:55:24] hb_sample_pts: pts 26390390 at 180834192
[17:55:24] transport stream pid 0x45 (type 0x81) is AC-3 audio id 0x80bd
[17:55:24] scan: decoding previews for title 1
[17:55:25] scan: preview 1
[17:55:25] scan: AC3, rate=48000Hz, bitrate=448000
[17:55:25] scan: preview 2
[17:55:25] scan: preview 3
[17:55:25] Interlacing detected in preview frame 2
[17:55:25] scan: preview 4
[17:55:25] scan: preview 5
[17:55:25] Interlacing detected in preview frame 4
[17:55:25] scan: preview 6
[17:55:25] Interlacing detected in preview frame 5
[17:55:25] scan: preview 7
[17:55:25] scan: preview 8
[17:55:25] scan: preview 9
[17:55:25] scan: preview 10
[17:55:25] scan: 10 previews, 720x480, 29.970 fps, autocrop = 0/2/8/10, aspect 4:3
[17:55:25] hb_stream_update_audio: id=80bd, lang=English (AC3) (2.0 ch), 3cc=eng, rate = 48000, bitrate = 448000, flags = 0x2 (2)
[17:55:25] scan: title (0) job->width:624, job->height:480
[17:55:25] thread b07bb000 exited ("scan")
[17:55:25] thread b07bb000 joined ("scan")
[17:55:25] libhb: scan thread found 1 valid title(s)
[17:56:07] thread b07bb000 started ("work")
[17:56:07] 1 job(s) to process
[17:56:07] starting job
[17:56:07]  + device /Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts
[17:56:07]  + title 1, chapter(s) 1 to 1
[17:56:07]  + 720x480 -> 702x478, crop 0/2/8/10
[17:56:07]  + filters
[17:56:07]    + Detelecine (pullup) (default settings)
[17:56:07]    + Deinterlace (ffmpeg or yadif/mcdeint) (2)
[17:56:07]  + video frame rate: variable (detected 29.970 fps)
[17:56:07]  + video quality 0.66
[17:56:07]  + PixelRatio: 1, width:702, height: 478
[17:56:07]  + encoder x264
[17:56:07]    + x264 options: ref=3:bframes=3:subq=6:me=umh:mixed-refs=1:direct=auto:weightb=1:brdo=1:bime=1:merange=24:analyse=all:8x8dct=1:no-fast-pskip=1
[17:56:07]  + audio 160 kbps, 48000 Hz
[17:56:07]  + encoder faac
[17:56:07]    + 80bd, English (AC3) (2.0 ch)
[17:56:07]      + Requested mixdown: Stereo (HB_AMIXDOWN_STEREO)
[17:56:07]      + Actual mixdown: Stereo (HB_AMIXDOWN_STEREO)
[17:56:07] thread b083d000 started ("reader")
[17:56:07]  + output: /Volumes/Funky Monkey/Video Temp/Movies/Soundgarden - Fell On Black Days.combdetect-slow deint.m4v
[17:56:07] thread b0941000 started ("muxer")
[17:56:07] thread b09c3000 started ("MPEG-2 decoder (libmpeg2)")
[17:56:07] thread b0a45000 started ("Renderer")
[17:56:07] encx264: keyint-min: 30, keyint-max: 300
[17:56:07] encx264: encoding with stored aspect 8/9
[17:56:07] encx264: Encoding at constant RF 17.340000
[17:56:07] encx264: opening libx264 (pass 0)
x264 [warning]: width or height not divisible by 16 (702x478), compression will suffer.
x264 [info]: using SAR=8/9
x264 [info]: using cpu capabilities: MMX MMXEXT SSE SSE2 Cache64 
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[17:56:07] thread b0b49000 started ("H.264/AVC encoder (libx264)")
No accelerated IMDCT transform found
[17:56:07] thread b0bcb000 started ("AC3 decoder")
[17:56:07] a52_syncinfo failed
[17:56:07] a52_syncinfo ok
[17:56:07] thread b0c4d000 started ("AAC encoder (libfaac)")
[17:56:07] sync: expecting 8859 video frames
[17:56:07] sync: first pts is 0
[17:56:07] Sync: Audio discontinuity (sequence: vid 78 aud 73) (pts 25210800 < 25300800 < 25390800)
[17:56:07] Sync: Audio joined Video after discontinuity at PTS 2880
[17:56:07] macgui: Using preset: music videos
[18:01:43] hb_ts_stream_decode - eof
[18:01:43] hb_stream_read - buffer after decode has zero length data
[18:01:43] reader: done
[18:01:43] thread b083d000 exited ("reader")
[18:01:55] sync: got 9036 frames, 8859 expected
[18:02:00] thread b09c3000 exited ("MPEG-2 decoder (libmpeg2)")
[18:02:00] thread b0a45000 exited ("Renderer")
[18:02:00] thread b0bcb000 exited ("AC3 decoder")
[18:02:00] thread b09c3000 joined ("MPEG-2 decoder (libmpeg2)")
[18:02:00] thread b0c4d000 exited ("AAC encoder (libfaac)")
[18:02:00] thread b0a45000 joined ("Renderer")
[18:02:00] mux: file size, 123429440 bytes
[18:02:00] render: lost time: 5453452 (1816 frames)
[18:02:00] mux: track 0, 117245305 bytes, 3117.18 kbps
[18:02:00] mux: track 1, 6015730 bytes, 159.94 kbps
[18:02:00] mux: overhead, 7.90 bytes per frame
[18:02:00] thread b0941000 exited ("muxer")
[18:02:00] render: gained time: 5452702 (7028 frames) (750 not accounted for)
[18:02:00] render: average dropped frame duration: 3003
[18:02:00] fifo_close: trashing 1 buffer(s)
[18:02:00] fifo_close: trashing 2 buffer(s)
[18:02:00] thread b0b49000 exited ("H.264/AVC encoder (libx264)")
[18:02:00] thread b0b49000 joined ("H.264/AVC encoder (libx264)")
x264 [info]: slice I:36    Avg QP:12.27  size: 31625  PSNR Mean Y:50.67 U:53.73 V:57.38 Avg:51.64 Global:51.28
x264 [info]: slice P:5633  Avg QP:13.31  size: 17875  PSNR Mean Y:48.22 U:51.28 V:54.14 Avg:49.17 Global:49.01
x264 [info]: slice B:1537  Avg QP:15.78  size: 10037  PSNR Mean Y:46.03 U:50.40 V:53.18 Avg:47.18 Global:47.08
x264 [info]: mb I  I16..4: 29.3% 56.6% 14.1%
x264 [info]: mb P  I16..4: 10.8% 21.9%  4.4%  P16..4: 31.2% 22.5%  8.2%  0.2%  0.1%    skip: 0.6%
x264 [info]: mb B  I16..4:  6.9%  7.0%  1.4%  B16..8: 39.8%  5.9% 11.6%  direct:11.4%  skip:15.9%
x264 [info]: 8x8 transform  intra:57.7%  inter:54.8%
x264 [info]: direct mvs  spatial:99.8%  temporal:0.2%
x264 [info]: ref P  59.8% 23.4% 16.9%
x264 [info]: ref B  78.2% 21.8%
x264 [info]: SSIM Mean Y:0.9917854
x264 [info]: PSNR Mean Y:47.767 U:51.107 V:53.954 Avg:48.755 Global:48.527 kb/s:3901.35
[18:02:00] thread b0bcb000 joined ("AC3 decoder")
[18:02:00] thread b0c4d000 joined ("AAC encoder (libfaac)")
[18:02:00] thread b083d000 joined ("reader")
[18:02:00] thread b0941000 joined ("muxer")
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 1 buffer(s)
[18:02:00] fifo_close: trashing 1 buffer(s)
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 0 buffer(s)
[18:02:00] fifo_close: trashing 27 buffer(s)
[18:02:00] deinterlacer: filtered 7219 | unfiltered 0 | total 7219
[18:02:00] Freed 58 buffers of size 512
[18:02:00] Freed 89 buffers of size 1024
[18:02:00] Freed 2048 buffers of size 2048
[18:02:00] Freed 0 buffers of size 4096
[18:02:00] Freed 0 buffers of size 8192
[18:02:00] Freed 130 buffers of size 16384
[18:02:00] Freed 0 buffers of size 32768
[18:02:00] Freed 203 buffers of size 518400
[18:02:00] Allocated 111680256 bytes of buffers on this pass and Freed 111680256 bytes, 0 bytes leaked
[18:02:00] thread b07bb000 exited ("work")
[18:02:00] thread b07bb000 joined ("work")
[18:02:00] libhb: work result = 0
HandBrake: OpenScripting.framework - scripting addition /Library/ScriptingAdditions/QXPScriptingAdditions.osax declares no loadable handlers.
if I don't turn on the deinterlace, I don't find any similar line in the activity

Code: Select all

[17:38:54] hb_init: checking cpu count
[17:38:54] hb_init: starting libhb thread
[17:38:54] thread b00c5000 started ("libhb")
[17:39:16] macgui: trying to open video_ts folder (parent directory chosen)
[17:39:16] hb_scan: path=/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts, title_index=0
[17:39:16] thread b0ac7000 started ("scan")
[17:39:16] scan: trying to open with libdvdread
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[17:39:16] file is MPEG Transport Stream
[17:39:16] hb_ts_stream_find_pids - found the following PIDS
[17:39:16]     Video PIDS : 
[17:39:16]       0x44 (68)
[17:39:16]     Audio PIDS : 
[17:39:16]       0x45 (69)
[17:39:16] hb_sample_pts: pts 945945 at 5843040
[17:39:16] hb_sample_pts: pts 2861861 at 17547920
[17:39:16] hb_sample_pts: pts 4789789 at 29192828
[17:39:16] hb_sample_pts: pts 6492492 at 40847324
[17:39:16] hb_sample_pts: pts 8177177 at 52553144
[17:39:16] hb_sample_pts: pts 9960960 at 64167220
[17:39:16] hb_sample_pts: pts 11651651 at 75836944
[17:39:16] hb_sample_pts: pts 13249249 at 87506668
[17:39:16] hb_sample_pts: pts 14810810 at 99176580
[17:39:16] hb_sample_pts: pts 16417417 at 110839912
[17:39:16] hb_sample_pts: pts 18093093 at 122519600
[17:39:16] hb_sample_pts: pts 19759759 at 134230872
[17:39:16] hb_sample_pts: pts 21339339 at 145844948
[17:39:16] hb_sample_pts: pts 22978978 at 157547384
[17:39:16] hb_sample_pts: pts 24639639 at 169173116
[17:39:16] hb_sample_pts: pts 26390390 at 180834192
[17:39:16] transport stream pid 0x45 (type 0x81) is AC-3 audio id 0x80bd
[17:39:16] scan: decoding previews for title 1
[17:39:16] scan: preview 1
[17:39:16] scan: AC3, rate=48000Hz, bitrate=448000
[17:39:16] scan: preview 2
[17:39:16] scan: preview 3
[17:39:16] Interlacing detected in preview frame 2
[17:39:16] scan: preview 4
[17:39:16] scan: preview 5
[17:39:16] Interlacing detected in preview frame 4
[17:39:16] scan: preview 6
[17:39:16] Interlacing detected in preview frame 5
[17:39:16] scan: preview 7
[17:39:16] scan: preview 8
[17:39:16] scan: preview 9
[17:39:16] scan: preview 10
[17:39:16] scan: 10 previews, 720x480, 29.970 fps, autocrop = 0/2/8/10, aspect 4:3
[17:39:16] hb_stream_update_audio: id=80bd, lang=English (AC3) (2.0 ch), 3cc=eng, rate = 48000, bitrate = 448000, flags = 0x2 (2)
[17:39:16] scan: title (0) job->width:624, job->height:480
[17:39:16] thread b0ac7000 exited ("scan")
[17:39:16] thread b0ac7000 joined ("scan")
[17:39:16] libhb: scan thread found 1 valid title(s)
[17:41:27] thread b0739000 started ("work")
[17:41:27] 1 job(s) to process
[17:41:27] starting job
[17:41:27]  + device /Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts
[17:41:27]  + title 1, chapter(s) 1 to 1
[17:41:27]  + 720x480 -> 702x478, crop 0/2/8/10
[17:41:27]  + filters
[17:41:27]    + Detelecine (pullup) (default settings)
[17:41:27]  + video frame rate: variable (detected 29.970 fps)
[17:41:27]  + video quality 0.66
[17:41:27]  + PixelRatio: 1, width:702, height: 478
[17:41:27]  + encoder x264
[17:41:27]    + x264 options: ref=3:bframes=3:subq=6:me=umh:mixed-refs=1:direct=auto:weightb=1:brdo=1:bime=1:merange=24:analyse=all:8x8dct=1:no-fast-pskip=1
[17:41:27]  + audio 160 kbps, 48000 Hz
[17:41:27]  + encoder faac
[17:41:27]    + 80bd, English (AC3) (2.0 ch)
[17:41:27]      + Requested mixdown: Stereo (HB_AMIXDOWN_STEREO)
[17:41:27]      + Actual mixdown: Stereo (HB_AMIXDOWN_STEREO)
[17:41:27] thread b07bb000 started ("reader")
[17:41:27]  + output: /Volumes/Funky Monkey/Video Temp/Movies/Soundgarden - Fell On Black Days combdetect.m4v
[17:41:27] thread b08bf000 started ("muxer")
[17:41:27] thread b0941000 started ("MPEG-2 decoder (libmpeg2)")
[17:41:27] thread b09c3000 started ("Renderer")
[17:41:27] encx264: keyint-min: 30, keyint-max: 300
GUI ERROR dialog: dvd: DVDOpen failed (/Volumes/Funky Monkey/Video Temp/Music Videos To Be Added/ Needs IVTC/Soundgarden - Fell On Black Days.ts)
[17:41:27] encx264: encoding with stored aspect 8/9
[17:41:27] encx264: Encoding at constant RF 17.340000
[17:41:27] encx264: opening libx264 (pass 0)
x264 [warning]: width or height not divisible by 16 (702x478), compression will suffer.
x264 [info]: using SAR=8/9
x264 [info]: using cpu capabilities: MMX MMXEXT SSE SSE2 Cache64 
[17:41:27] thread b0ac7000 started ("H.264/AVC encoder (libx264)")
No accelerated IMDCT transform found
[17:41:27] thread b0b49000 started ("AC3 decoder")
[17:41:27] a52_syncinfo failed
[17:41:27] a52_syncinfo ok
[17:41:27] thread b0c4d000 started ("AAC encoder (libfaac)")
[17:41:27] sync: expecting 8859 video frames
[17:41:27] sync: first pts is 0
[17:41:27] Sync: Audio discontinuity (sequence: vid 24 aud 73) (pts 25210800 < 25300800 < 25390800)
[17:41:27] Sync: Audio joined Video after discontinuity at PTS 2880
[17:41:27] macgui: Using preset: music videos
[17:46:43] hb_ts_stream_decode - eof
[17:46:43] hb_stream_read - buffer after decode has zero length data
[17:46:43] reader: done
[17:46:43] thread b07bb000 exited ("reader")
[17:46:53] sync: got 9036 frames, 8859 expected
[17:46:58] thread b0b49000 exited ("AC3 decoder")
[17:46:58] thread b0ac7000 exited ("H.264/AVC encoder (libx264)")
[17:46:58] thread b09c3000 exited ("Renderer")
[17:46:58] thread b0c4d000 exited ("AAC encoder (libfaac)")
[17:46:58] thread b0941000 exited ("MPEG-2 decoder (libmpeg2)")
[17:46:58] thread b0941000 joined ("MPEG-2 decoder (libmpeg2)")
[17:46:58] thread b09c3000 joined ("Renderer")
[17:46:58] render: lost time: 5453452 (1816 frames)
[17:46:58] render: gained time: 5452702 (7029 frames) (750 not accounted for)
[17:46:58] render: average dropped frame duration: 3003
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 2 buffer(s)
[17:46:58] thread b0ac7000 joined ("H.264/AVC encoder (libx264)")
[17:46:58] mux: file size, 133960521 bytes
[17:46:58] mux: track 0, 127770594 bytes, 3395.32 kbps
[17:46:58] mux: track 1, 6018734 bytes, 159.94 kbps
[17:46:58] mux: overhead, 8.03 bytes per frame
[17:46:58] thread b08bf000 exited ("muxer")
x264 [info]: slice I:36    Avg QP:12.31  size: 33747  PSNR Mean Y:51.19 U:53.89 V:57.53 Avg:52.12 Global:51.63
x264 [info]: slice P:5460  Avg QP:13.37  size: 19768  PSNR Mean Y:48.15 U:50.89 V:53.85 Avg:49.05 Global:48.89
x264 [info]: slice B:1714  Avg QP:15.82  size: 10872  PSNR Mean Y:46.06 U:49.93 V:52.77 Avg:47.14 Global:47.03
x264 [info]: mb I  I16..4: 26.0% 69.2%  4.8%
x264 [info]: mb P  I16..4: 10.6% 25.5%  2.7%  P16..4: 29.1% 23.0%  8.3%  0.2%  0.1%    skip: 0.6%
x264 [info]: mb B  I16..4:  7.5%  8.3%  1.0%  B16..8: 40.3%  7.3% 11.6%  direct: 9.2%  skip:14.8%
x264 [info]: 8x8 transform  intra:64.0%  inter:62.7%
x264 [info]: direct mvs  spatial:99.9%  temporal:0.1%
x264 [info]: ref P  61.2% 22.6% 16.2%
x264 [info]: ref B  79.5% 20.5%
x264 [info]: SSIM Mean Y:0.9915073
x264 [info]: PSNR Mean Y:47.670 U:50.681 V:53.609 Avg:48.611 Global:48.380 kb/s:4249.28
[17:46:58] thread b0b49000 joined ("AC3 decoder")
[17:46:58] thread b0c4d000 joined ("AAC encoder (libfaac)")
[17:46:58] thread b07bb000 joined ("reader")
[17:46:58] thread b08bf000 joined ("muxer")
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 1 buffer(s)
[17:46:58] fifo_close: trashing 1 buffer(s)
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 0 buffer(s)
[17:46:58] fifo_close: trashing 20 buffer(s)
[17:46:58] Freed 62 buffers of size 512
[17:46:58] Freed 97 buffers of size 1024
[17:46:58] Freed 2048 buffers of size 2048
[17:46:58] Freed 0 buffers of size 4096
[17:46:58] Freed 0 buffers of size 8192
[17:46:58] Freed 130 buffers of size 16384
[17:46:58] Freed 0 buffers of size 32768
[17:46:58] Freed 201 buffers of size 518400
[17:46:58] Allocated 110653696 bytes of buffers on this pass and Freed 110653696 bytes, 0 bytes leaked
[17:46:58] thread b0739000 exited ("work")
[17:46:58] thread b0739000 joined ("work")
[17:46:58] libhb: work result = 0
HandBrake: OpenScripting.framework - scripting addition /Library/ScriptingAdditions/QXPScriptingAdditions.osax declares no loadable handlers.
What do you think?
I've probably missed something simple.
cvk_b
Veteran User
Posts: 527
Joined: Sun Mar 18, 2007 2:11 am

Re: Combing Detection

Post by cvk_b »

ON:

Code: Select all

-V --detelecine="1:1:4:4:-1:0" --deinterlace="0:-1:-1:0:1"
Change that last 1 in deinterlace to 0 to turn it off.
Last edited by jbrjake on Sat Mar 15, 2008 12:29 am, edited 1 time in total.
Reason: correcting deinterlacing settings (missing colon)
jbrjake
Veteran User
Posts: 4805
Joined: Wed Dec 13, 2006 1:38 am

Re: Combing Detection

Post by jbrjake »

Oops, oh yeah...forgot to mention a key point:

In this patch, unlike my previous ones, the comb detection doesn't default to running all the time. I've folded it into the deinterlacing settings.

Unfortunately, this makes it currently inaccessible in the MacGui. You can only use it in the CLI, like so:

Code: Select all

--deinterlace="0:-1:-1:0:1"
That's with default parameters (otherwise there'd be 3 more numbers on the end). The settings now are:

Yadif mode : Parity : Mcdeint mode : Mcdeint quality : Comb detection : Equal color value : Different color value : Combing threshold

...with the defaults for those last three being 10, 30, and 9.

This lets people play around with the parameters, in case the sensitivity is off for a particular source.

Dynaflash generously offered to add to the patch, to make it an option in the MacGui with default settings. I stupidly refused, thinking it was too early and the default settings need testing. But if this is only going to get tested in the GUI, I guess I should reconsider that and beg dynaflash to add it ;>
dynaflash
Veteran User
Posts: 3820
Joined: Thu Nov 02, 2006 8:19 pm

Re: Combing Detection

Post by dynaflash »

No begging required. Here is the patch to the macgui so you can use the correct deinterlace settings for jbrjakes decomb patch.
Note: it replaces the standard "Slower" deinterlace setting with

Code: Select all

0:-1:-1:0:1
http://handbrake.pastebin.ca/943305

Code: Select all

Index: macosx/PictureController.mm
===================================================================
--- macosx/PictureController.mm	(revision 1336)
+++ macosx/PictureController.mm	(working copy)
@@ -125,7 +125,7 @@
     [fDeinterlacePopUp addItemWithTitle: @"None"];
     [fDeinterlacePopUp addItemWithTitle: @"Fast"];
     [fDeinterlacePopUp addItemWithTitle: @"Slow"];
-	[fDeinterlacePopUp addItemWithTitle: @"Slower"];
+	[fDeinterlacePopUp addItemWithTitle: @"Slower (DeComb)"];
     
 	/* Set deinterlaces level according to the integer in the main window */
 	[fDeinterlacePopUp selectItemAtIndex: fPictureFilterSettings.deinterlace];
Index: macosx/Controller.mm
===================================================================
--- macosx/Controller.mm	(revision 1336)
+++ macosx/Controller.mm	(working copy)
@@ -1631,7 +1631,9 @@
     else if ([fPictureController deinterlace] == 3)
     {
         /* Yadif (with spatial deinterlacing) */
-        hb_filter_deinterlace.settings = "0"; 
+        /* for decomb testing use 0:-1:-1:0:1 as per jbrjake */
+        //hb_filter_deinterlace.settings = "0";
+        hb_filter_deinterlace.settings = "0:-1:-1:0:1";  
         hb_list_add( job->filters, &hb_filter_deinterlace );            
     }
 
Post Reply