1 2
7 8 9
Player (66)
Joined: 4/21/2011
Posts: 232
StreetFighter2 Ok, I think I'm done. Looks at the motion vectors to and from in the forward and backwards direction.
function HalfWink(clip c, int "ml"){
    ml = Default(ml, 100)
    c

    #e e e
    # o o o
    #The clip is made of even/odd frames and the simplest way to reduce
    #framerate would be to just take the even frames. But some clips have
    #blinking that alternates frame to frame. That could make a "transparent"
    #object solid or disappear entirely.

    #E   e   E
    #  o I o   o
    #This function takes every other even frame and a frame interpolated
    #from the odd frames. There are even times when it is appropriate to
    #use some of the skipped even frame.

    e0=selectevery(4,0)
    e2=selectevery(4,2)
    OddBlend=selectodd.convertfps(c).selectevery(4,1)
    interleave(e0,overlay(OddBlend,e2,mask=BlinkMask(c,ml=ml)))
}

function BlinkMask(clip c, int "ml"){
    ml = Default(ml, 100)
    c.ConvertToYv12.colorYUV(autogain=true)

    #oo is the part of the odd frame that is moving.
    #If it isn't moving it doesn't need to be replaced.

    OddMoving=selectodd.mt_motion(thT=255).selectodd

    #Use mvtools to detect motion.
    #If the object is blinking it will show crazy high motion since it doesn't
    #exist frame to frame.

    super=MSuper()
    fvec =MAnalyse(super, isb=false, truemotion=false)
    bvec =MAnalyse(super, isb=true , truemotion=false)
    fmask=Mmask(last,fvec,kind=1,ml=ml).mt_binarize
    bmask=Mmask(last,bvec,kind=1,ml=ml).mt_binarize

    #mask is the part of the even frame that IS moving in both the forward and
    #backwards direction. If it isn't moving in BOTH directions then it isn't
    #blinking and we CAN!!! use it.

    #f   f   f   f
    #  b   b   b   b

    masks=interleave(fmask,bmask)

    #f b f b f b f b

    tf=mt_logic(masks.selectevery(2,1),masks.selectevery(2,2),mode="or").mt_expand.mt_expand

    #f(bf)(bf)(bf)(bf)(b

    Blinking=mt_logic(tf.selectevery(4,1),tf.selectevery(4,2),mode="and")

    #       >2<

    NotBlinking=Blinking.mt_invert

    mt_logic(OddMoving,NotBlinking,mode="and").mt_expand.mt_expand
}
Player (137)
Joined: 9/18/2007
Posts: 389
This is looking pretty good for the fight video. Using this one with the RoadRunner clip, this still causes many defects to the original clip.
 o = AVISource("t:\roadrunner_fast_and_flickering_section_noaudio.avi")

 return stackvertical( stackhorizontal(o.selecteven, o.selectodd), \
	stackhorizontal( halfwink(o), halfwink(o.deleteframe(0)) ))
result for frame 213 In my opinion, the Halfwink code is looking better than TASblend right now. It just needs a little more finetuning to get perfect results. Edit: the fine-tuning from Vit produces a result without any visible artifacts when played back at Youtube, here is the Roadrunner clip with the code from nanoglyth and vit Edit2: The code gives significantly worse results if the sprite is visible on odd frames and invisible on even frames, almost the same clip again.
creaothceann
He/Him
Editor
Joined: 4/7/2005
Posts: 1874
Location: Germany
nanogyth wrote:
What is the arm-pumping? Part of the running animation?
It's when Samus points up or down every other frame. ("arm pumping" may be just my term) EDIT: I just encoded the Super Metroid test video above with HalfWink, and it looks very good (except for the blinking stars; I'd suggest an encoder would use the normal TASBlend for these sections). So, would anybody object to have these functions be made official as TASBlend_blur and TASBlend_HalfWink? (names not final)
Lex
Joined: 6/25/2007
Posts: 732
Location: Vancouver, British Columbia, Canada
partyboy1a wrote:
Edit: the fine-tuning from Vit produces a result without any visible artifacts when played back at Youtube, here is the Roadrunner clip with the code from nanoglyth and vit
It looks like all this hard work has paid off. Great job, nanogyth and vit!
Player (66)
Joined: 4/21/2011
Posts: 232
I'm currently working on some visualization to help figure out why artifacts happen. The results are worse than previous because the masks are so sharp.
FFVideoSource("sm.avi")
ConvertToRGB32
HalfWink(debug=true)

function HalfWink(clip c, bool "debug"){
    debug=default(debug,false)

    #The clip is made of even/odd frames and the simplest way to reduce
    #framerate would be to just take the even frames. But some clips have
    #blinking/shaking that alternates frame to frame. That could make a
    #"transparent" object solid or disappear entirely. Or lock a shaking
    #scene in a single position.

    #e e e
    # o^o o
    #Every other even frame is taken as is. The other even frame is used
    #as much as it can be. A mask is made of the blinking and the shaking.
    #Those parts of the frame are simply replaced with data from one of the
    #odd frames. NO BLENDING!!!

    e0=c.selectevery(4,0)
    o1=c.selectevery(4,1)
    e2=c.selectevery(4,2)
    o3=c.selectevery(4,3)

    e2fix=overlay(e2,o1,mask=BlinkShake(c))

    DebugMask=stackvertical(\
        stackhorizontal(o1,e2,o3),\
        stackhorizontal(Shaking(c,debug=true),e2fix,Blinking(c,debug=true)))

    return debug ? DebugMask : interleave(e0,e2fix)
}

function BlinkShake(clip c){
    return mt_logic(Blinking(c),Shaking(c),mode="or")
}

function Shaking(clip c, bool "debug"){
    debug=default(debug,false)
    c=c.ConvertToYv12 #.colorYUV(autogain=true)

    #Shaking is the background moving at a regular rate.

    #E   e   E
    #  o ^ o   o

    move=c.mt_motion
    oe=move.selectevery(4,2)
    eo=move.selectevery(4,3)
    oo=c.selectodd.mt_motion.selectodd

    #Motion will be seen between oe AND eo, AND NOT oo

    Shaking=mt_logic(mt_logic(oe,eo,mode="and"),oo.mt_invert,mode="and")

    DebugMask=mergeRGB(oe,eo,oo.mt_invert)
    return debug ? DebugMask : Shaking
}

function Blinking(clip c, bool "debug"){
    debug=default(debug,false)
    c=c.ConvertToYv12 #.colorYUV(autogain=true)

    #Blinking is an object that appears/disappears from the scene.
    #If the object is blinking it will show crazy high motion since it doesn't
    #exist frame to frame. Use mvtools to detect motion.
    #Look for motion to and from. The vectors aren't symetric because it is
    #easier to match the background than an object.
    #The motion must be forward AND backward or it isn't a one-frame blink.

    super=MSuper(c)
    fvec =MAnalyse(super, isb=false, truemotion=false)
    bvec =MAnalyse(super, isb=true , truemotion=false)
    fmask=Mmask(c,fvec,kind=1).mt_binarize
    bmask=Mmask(c,bvec,kind=1).mt_binarize

    #f   f   f   f
    #  b   b   b   b

    #f b f b f b f b
    #f(bf)(bf)(bf)(bf)(b
    #       >2<

    masks=interleave(fmask,bmask)

    To=masks.selectevery(2,1)
    From=masks.selectevery(2,2)
    ToFrom=mt_logic(To,From,mode="or")

    tfForward=ToFrom.selectevery(4,1)
    tfBackward=ToFrom.selectevery(4,2)
    Blinking=mt_logic(tfForward,tfBackward,mode="and")

    DebugMask=mergeRGB(tfForward,tfBackward,blankclip(tfForward))
    return debug ? DebugMask : Blinking
}
Vit
Joined: 9/7/2011
Posts: 2
nanogyth: A note from the avisynth world. I think you may have misunderstood the masks you are using. MMask with kind=1 gives you a "SAD mask" of the analyzed motion (Sum of Absolute Differences). This is not an indication of what is moving. This mask indicates which parts of the image had poor motion matches with the (previous or next) frame. You can get a poor motion match on a moving or a stationary object. Poor motion matches are caused by objects appearing or disappearing or by objects changing their shape, look or color radically from one frame to the next. Can also occur when there are small objects moving in front of larger ones creating two speeds of motion in the same area (the ropes in the Roadrunner clip). You can get masks showing the motion by using different settings for "kind". But typically the SAD mask is most important because you can't trust motion information if the match is poor. Another important detail: The forward vectors hold the motion from the previous frame into the current one, so the forward SAD mask shows the parts of the current frame that could not be matched with the previous frame. Similarly, the backward SAD mask shows the parts of the current frame that could not be matched with the next frame. This is extremely counter-intuitive, but definitely how it works (check the docs on the MAnalyse "isb" setting). The approach you are taking is correct, merging forward and backward masks. However, I think you will be able to refine it more effectively if you understand what MVTools is telling you (and what it isn't). I have made some refinements to my variant, but I still need to work on the problem noted by partyboy1a when objects are invisible on even frames rather than odd.
Player (66)
Joined: 4/21/2011
Posts: 232
Here is another version, hopefully useful to someone. So, we can see in the odd to odd transition there are pixel changes, but only complex motion in the platform.
function momix(clip c){

    #E   e   E
    #  o ^ o   o
    o1=c.selectevery(4,1)
    e2=c.selectevery(4,2)
    o3=c.selectevery(4,3)

    move=c.moveit
    oddMove=c.selectodd.moveit

    #e e e
    # o^o o
    oe=move.selectevery(4,1)
    eo=move.selectevery(4,2)
    oo=oddMove.selecteven

    return stackvertical(\
        stackhorizontal(oe, e2, eo),\
        stackhorizontal(o1, oo, o3))
}

function moveIt(clip c,int "ml"){
    ml=default(ml,100)
    c=c.ConvertToYv12 #.colorYUV(autogain=true)

    super=MSuper(c)
    fvec =MAnalyse(super, isb=false, truemotion=false)
    bvec =MAnalyse(super, isb=true , truemotion=false)
    fmask=Mmask(c,fvec,kind=1,ml=ml).mt_binarize
    bmask=Mmask(c,bvec,kind=1,ml=ml).mt_binarize

    To=fmask.trim(1,0)
    From=bmask
    move=c.mt_motion(thT=255).trim(1,0)

    return mergeRGB(to,move,from)
}
Player (66)
Joined: 4/21/2011
Posts: 232
Vit wrote:
Poor motion matches are caused by objects appearing or disappearing or by objects changing their shape, look or color radically from one frame to the next.
Yes, that was exactly what I was using it to look for. The problem was that it matched a whole lot of other stuff too, lol.
Player (137)
Joined: 9/18/2007
Posts: 389
Does it actually help to have all layers separated from each other? Here is the RoadRunner clip with all layers separated from each other, and all possible combinations of them. You should use the following script to view it:
clip = AVIsource("allinone-noaudio.avi").deleteframe(0)

function split(clip c, bool horizontal, string layername, int hlevel) {
	c1 = subtitle(c.selectrangeevery(2400,1200), layername + " off", x=hlevel*20, y=hlevel*20 )
	c2 = subtitle(c.trim(1200,0).selectrangeevery(2400,1200), layername + " on" , x=hlevel*20, y=hlevel*20)
	return horizontal ? stackhorizontal(c1, c2) : stackvertical(c1, c2)
}

return clip.split(true, "sprites", 4).split(true,"bg4", 3).split(true,"bg3",2).split(false,"bg2",1).split(false,"bg1",0)
Note: While you can make reliable assumptions on the order of the background layers, this is not true for the sprite layer, see frame 433 for an example. It should be possible to create separate encodes for every sprite priority (3,2,1,0)... Well, they would take a very long time, because you need to create a savestate for each frame of the final video, and you need to load a savestate for each frame and for each priority... The order of the layers should be no problem, because it is possible to test all possible versions automatically, but it would still be necessary to render all the "sprite layers" separately...
creaothceann
He/Him
Editor
Joined: 4/7/2005
Posts: 1874
Location: Germany
partyboy1a wrote:
Does it actually help to have all layers separated from each other?
No, because it's not possible to stitch them back together exactly like the hardware does.
Player (137)
Joined: 9/18/2007
Posts: 389
Nonetheless, here is the blinking sprites marker for Snes9x (first version). If you think it can be useful, I'll go improve it until it works as it should. The code is very emulator specific, but not game specific.
Player (66)
Joined: 4/21/2011
Posts: 232
Would it be possible to tell the emulator to make all the blinking sprites be visible on the even frames? (or blink at a on-on-off-off cadence?)
Player (66)
Joined: 4/21/2011
Posts: 232
Here is a conceptually simpler idea, using Vit's excellent deinterlacer, qtgmc, to create the blended frames.
ConvertToRGB32
PointResize(Width * 2, Height * 2)

interleave(selectevery(4,0),halfwink)

function halfwink(clip c){
    c=c.convertToYuy2(matrix="PC.601")
    inter=c.selectodd.separatefields.trim(1,0).weave
    deinter=inter.qtgmc.selectevery(4,1)
    return deinter.ConvertToRGB32( matrix="PC.601" )
}
Player (66)
Joined: 4/21/2011
Posts: 232
http://www.youtube.com/watch?v=d03CnBzfK-o http://www.youtube.com/watch?v=4td9d64eYJk http://www.youtube.com/watch?v=cz8lJcufRdE
clip_a=FFVideoSource("sm/sm.avi")
clip_b=ffvideosource("sm/1-sf2ceua-unaltered.mp4")
clip_c=directshowsource("sm/roadrunner_fast_and_flickering_section_noaudio.avi")
clip_d=ffvideosource("sm/#2223 - cpadolf's SNES Super Metroid ''any%, glitched''.avi")

clip_b
assumefps(60)
PointResize(Width * 2, Height * 2)
convertToyv12

i2=smoothOverlay(selectevery(4,1),halfwink,motionMask)

interleave(selectevery(4,0),i2)

#PointResize(Width / 2, Height / 2)

function smoothOverlay(clip a, clip b, clip mask){
    step1=overlay(a,b,mask=mask.mt_expand.mt_expand,opacity=.5)
    return overlay(step1,b,mask=mask)
}

function motionMask(clip c){
    pc=c.mt_motion(thT=255)
    oepc=pc.selectevery(4,2)
    eopc=pc.selectevery(4,3)
    oopc=c.selectodd.mt_motion(thT=255).selectevery(2,1)

    mask=mt_logic(oepc, eopc, mode="or")
    return mt_logic(mask, oopc, mode="or")
}

function halfwink(clip c){
    inter=c.convertToyuy2.selectodd.separatefields.trim(1,0).weave
    deinter=inter.qtgmc.selectevery(4,1)
    return deinter.convertToyv12
}
Vit
Joined: 9/7/2011
Posts: 2
QTGMC does many things, but blending frames not yet (next version will actually, but that's some way off). You're feeding it duplicated frames, from an interlacing point of view. That will, by chance, create frames that are very slightly nudged towards the mid-way blend. But only very slightly. So unfortunately your result is barely any different than
#interleave(selectevery(4,0),selectevery(4,3))
with the uneven movement that creates.
Player (66)
Joined: 4/21/2011
Posts: 232
lol so I made a really-slow somewhat-blurry. selectevery(4,0,3)
Senior Moderator
Joined: 8/4/2005
Posts: 5777
Location: Away
The method just above doesn't show a significant improvement over simply reducing framerate to 20 fps in my opinion. At least there you don't have to deal with sprites being stuck in one phase of a flicker, or blending artifacts from previous examples. For me, the 33/66 blend still gives the most visually pleasing result on the Super Metroid sample. :\
Warp wrote:
Edit: I think I understand now: It's my avatar, isn't it? It makes me look angry.
Player (137)
Joined: 9/18/2007
Posts: 389
Someone suggested to blend the sprite layer only. This is how it's looking if you do that: Link to video difference between left and right is basically rightsource=leftsource.deleteframe(0) code included in the video. Another strange thing: viewing this one in different screen sizes causes changes to the flicker frequency...
Site Admin, Skilled player (1254)
Joined: 4/17/2010
Posts: 11475
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
partyboy1a wrote:
Another strange thing: viewing this one in different screen sizes causes changes to the flicker frequency...
The reason is embedded player. Looks okay on YT itself in any resolution.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Player (137)
Joined: 9/18/2007
Posts: 389
nanogyth wrote:
Would it be possible to tell the emulator to make all the blinking sprites be visible on the even frames? (or blink at a on-on-off-off cadence?)
It should be relatively easy to remove sprites -- sending them to x=255 would be enough. But it's a lot more difficult to add sprites on the same way. You would need to keep track of all the RAM values AND all the registers which can have an influence on the result... You can give that a try if you really want to. If there is an easy way to reconstruct the complete image from the different layers (well, with separate layers for sprites priority 0,1,2,3), then you actually can add sprites wherever you want, and it will look exactly as it should. This will fail whenever some kind of special effect is applied to the image afterwards (like water...) EDIT:: here is the roadrunner clip made from the lua script to kill blinking sprites. (same with better resolution). This will only work if the sprites are visible on even frames at the moment. If you take a closer look, you will see that not all the flickering was detected. I think this can only be done by some sort of statistics: If two sprites are drawn in a direct sequence, and they are close to each other, and this happens very often with exactly the same alignment, then the sprites belong together. If there is a small difference between them at least once (for example: sprite1.x - sprite2.x = 7 in the first frame, but 5 in the second), then they do definitely not belong together. The much bigger problem will be to add sprites... Well, and killing sprites may cause desyncs, which means that a savestate must be loaded for every frame.
Post subject: perfect flicker conversion achieved!
Player (137)
Joined: 9/18/2007
Posts: 389
I managed to create the absolutely pixel-perfect result for the roadrunner clip. I made the same for Super Metroid, but it's not as perfect as the other one.
Active player (279)
Joined: 4/30/2009
Posts: 791
Do you think this could work for Alisia Dragoon?
Player (137)
Joined: 9/18/2007
Posts: 389
Well, if it's a SNES game, you can try the current sprites killer script. During the "first pass", it records all sprite data. the "second pass" starts when you press numpad+ (+frameadvance if emulation is paused). Record the avi as usual before script execution, and record another avi for the second pass, then adjust the avisynth script accordingly. I think it can work for other emulators -- if the emulator allows you to manipulate the OAM. The OAM read and write part is absolutely emulator dependent of course.
Player (66)
Joined: 4/21/2011
Posts: 232
This does some stuff right, like catching stuff that is blinking at a 2 frame rate. But mostly it looks worse.
clip_c
assumefps(60)
converttorgb32

e0=selectevery(4,0)
o1=selectevery(4,1)
e2=selectevery(4,2)
o3=selectevery(4,3)

mask=ng_mask(o1,e2,o3)

i2 = layer(o1,o3.mask(mask))

interleave(e0,i2)

function ng_mask(clip a, clip b, clip c){
    a=a.converttoyv24
    b=b.converttoyv24
    c=c.converttoyv24

    dd=dad(a,b,c)

    y=dd.converttoy8
    u=dd.uToY8
    v=dd.vToY8

    uv=mt_lutxy(u,v,"x y + 2 /")
    mask=mt_lutxy(y,uv,"x y + 256 > 255 0 ?")
    return mask.converttorgb32
}

function dad(clip a, clip b, clip c){
    ab=ab_dif(a,b)
    bc=ab_dif(b,c)
    return mt_lutxy(ab,bc,"x y - 2 / 128 +") #(x-y)/2 +128
}

function ab_dif(clip a, clip b){
    return mt_lutxy(a,b,expr="x y - abs")
}
Player (137)
Joined: 9/18/2007
Posts: 389
The good thing is that we now have a reference for a perfect video (if you agree that the "perfect result" video I posted really gives the very best what we could make out of 30fps). Comparing those two, I have seen way too many destructions to the "background" (the platforms from which the bird jumps off). I had a short look at the Gens sourcecode... It seems to be very easy to implement a Lua function "getSprite(spritenum)", and even "setSprite(spritenum,spr)" should be no problem, as Gens seems to use a struct which includes exactly all the necessary data. I think it would even be relatively easy to create a function like "copysprite" and "pastesprite" for it in such a way that it also copies the graphics. When we have such functionality, I could test the current code with Sonic. I think it will work with it, because it would identify the whole background as one "group".
1 2
7 8 9