Skip to content

Instantly share code, notes, and snippets.

@shizukachan
Last active October 31, 2017 01:51
Show Gist options
  • Save shizukachan/6374ea615d0925ccd59c3e5cc8595016 to your computer and use it in GitHub Desktop.
Save shizukachan/6374ea615d0925ccd59c3e5cc8595016 to your computer and use it in GitHub Desktop.
C027 1080p-as-1080i capture "fix" script
#CaptureFix v3
LoadPlugin("R:\ffms2-libav.dll")
LoadPlugin("R:\interp\svpflow1.dll")
LoadPlugin("R:\interp\svpflow2.dll")
LoadPlugin("R:\interp\decomb.dll")
LoadPlugin("R:\interp\TDeint.dll")
Import("R:\interp\InterFrame2.avsi")
#if you're capturing yv12, you're doing it wrong. Use 4:2:2 capture. The C027 captures TFF.
mew = ffvideosource("Nights of Azure 2 G.mp4")
meow = mew.AssumeFPS(30000,1001).ConvertToYV16().AssumeTFF().SeparateFields()
#SHIT FRAME HANDLING BEGIN
#The C027 has a horrible habit of failing to capture some frames in 1080i (and even 720p) mode, where it just fails to
#capture a field and basically writes zeroes to the 0th line, which shows up as dark green in YUV. Everything else in the field
#is basically from a previous buffer, so it causes visible judder. Eeeew. I choose to motion interpolate this missing field.
#Usually this green line is between 320 and 448 pixels long; I've never seen anything less than 256, so we'll test the
#last 256 lines of the frame for YUV(0,0). If you're using a compressed capture, you'll have to use a lossiness threshold, below.
Even = meow.SelectEvery(2,0)
Odd = meow.SelectEvery(2,1)
clipped = Even.Crop(1664,0,256,1).ConvertToYV16()
Reference = BlankClip(Even,pixel_type="YV16",color_yuv=0,width=256,height=1)
Bad = BlankClip(Even,pixel_type="YV16",color=$ff0000)
EvenEven = Even.SelectEvery(2,0)
EvenOdd = Even.SelectEvery(2,1)
ClipEE = clipped.SelectEvery(2,0)
ClipEO = clipped.SelectEvery(2,1)
#hack to obtain slightly better chroma resolution for Interframe (which only supports YV12) - this way, we don't completely lose our chroma resolution
#InterpEE = EvenEven.ConvertToYV12(interlaced=False).Interframe(GPU=false,Cores=1,FrameDouble=true).ConvertToYV16(interlaced=False).SelectEvery(2,1)
#InterpEO = BlankClip(EvenOdd,length=1)+EvenOdd.ConvertToYV12(interlaced=False).Interframe(GPU=false,Cores=1,FrameDouble=true).ConvertToYV16(interlaced=False).SelectEvery(2,1)
InterpEE = EvenEven.PointResize(1920,1080).ConvertToYV12(interlaced=False).Interframe(GPU=false,Cores=1,FrameDouble=true).ConvertToYV16(interlaced=False).PointResize(1920,540).SelectEvery(2,1)
InterpEO = BlankClip(EvenOdd,length=1)+EvenOdd.PointResize(1920,1080).ConvertToYV12(interlaced=False).Interframe(GPU=false,Cores=1,FrameDouble=true).ConvertToYV16(interlaced=False).PointResize(1920,540).SelectEvery(2,1)
#lossiness threshold for lossy captures. A sane value is 12 at x264 4:2:2 crf=17.
#For lossless encodes, equals 0 works.
EvenEvenFixed = ConditionalFilter(ClipEE,InterpEO,EvenEven, "LumaDifference(ClipEE,Reference)+ChromaUDifference(ClipEE,Reference)+ChromaVDifference(ClipEE,Reference)","lessthan","12")
EvenOddFixed = ConditionalFilter(ClipEO,InterpEE,EvenOdd, "LumaDifference(ClipEO,Reference)+ChromaUDifference(ClipEO,Reference)+ChromaVDifference(ClipEO,Reference)","lessthan","12")
EvenFixed = Interleave(EvenEvenFixed,EvenOddFixed)
T=Interleave(EvenFixed,Odd).Weave().ConvertToYUY2()
#SHIT FRAME HANDLING END
#This source should now be (nearly) equal to any proper interlaced capture; so much work to fix the C027's bugs.
#If you're actually working with a 60i source, this is where you'd split your script off to process the video your way.
#Everything after this point is for conversion of 30p-as-60i to progressive 30p.
#DEINTERLACE INTO PROGRESSIVE (for actual interlaced frames!)
#We can't use FPSDivisor=2 because it will just return frames interpolated from even fields.
#These are the SHIT FRAMES which we fixed via motion interpolation, but why bother when
#we can just use odd frames (fields) from QTGMC and interpolate from those instead.
Q=T.QTGMC(preset="Medium", ediThreads=8).SelectEvery(2,1)
#'DEINTERLACE' INTO PROGRESSIVE (for actual progressive frames!)
#Sometimes Telecide gets it wrong, so we actually want to have a deinterlacer after it when it makes a mistake.
#U=T.Telecide(show=True, post=1, vthresh=64) #for debugging
U=T.Telecide(show=False, post=0, vthresh=64)
#CATCH COMBED FRAMES
#Telecide's field matching is great, but sometimes miscategorizes combed frames. We'll ignore Telecide's hints and use
#TDeint to detect and deinterlace combed frames instead.
#For quality, we can use QTGMC to deinterlace; for speed, remove edeint=Q to use TDeint's internal deinterlacer instead.
V=U.TDeint(mode=0, clip2=T, hints=False, full=false, map=0,
\ field=0, edeint=Q, mthreshL=3,
\ cthresh=7, blockx=32, blocky=32, MI=224)
#Line 0: TDeint mode of operation
# Not much to say here, set map=1 or map=2 for debugging. Must use clip2=T because of field matching.
#Line 1: Deinterlacing configuration
# Ensure the bottom field is kept, and the top field is interpolated. Set mthreshL low because QTGMC is quite good,
# and combing artifacts are worse than over-deinterlacing. This is particularly true for the few-interlaced-frames case.
#Line 2: Combed frame detection
# Residual combing is usually encode lossiness, SHIT FRAMES, or Telecide fails. Raise cthresh to 7 to mitigate encode
# lossiness triggering the combed frame detector. Deinterlacing only produces better results in the last two cases.
return V.ConvertToYV12()
@shizukachan
Copy link
Author

The C027 card is pretty awful at capturing 1080i; SHIT FRAMES are only a subset of its artifacts. Some of the other ones are a horizontal left/right shift, which is much more difficult to detect programmatically.

Telecide turns 30p-in-60i content back into progressive frames.
TDeint catches any real 60i-in-60i content and deinterlaces it. This isn't the highest quality deinterlacer simply because we don't expect to see too much 60i content. Most of the time, this is trophy popups from the PS4's OS, which is composited at 60 fps over game render output, or SHIT FRAMES with interpolated fields.

Don't use this for games that actually run at 60 fps on the PS4; for that, use QTGMC full rate output.

@shizukachan
Copy link
Author

Updated to use QTGMC instead. You can no longer use this script for real time capture fixing :(

Ironically, using QTGMC output means we no longer need to worry about SHIT FRAMES anymore, assuming they only affect top fields; instead of temporally interpolating using motion interpolating, we spatially interpolate using the presumably good bottom fields of the SHIT FRAMES, using QTGMC. This usually produces a sufficiently good result, much better than the previous method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment