Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > VapourSynth

Reply
 
Thread Tools Search this Thread Display Modes
Old 24th April 2025, 15:22   #1  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
ShiftLinesHorizontally not working as intended

A user got a clip where I wanted to shift some lines horizontally.
So I wrote the following script:
Code:
import vapoursynth as vs
core = vs.core

def ShiftLinesHorizontally(clip: vs.VideoNode, shift: int, ymin: int, ymax: int) -> vs.VideoNode:
    # Validate clip format and subsampling
    if clip.format.color_family != vs.YUV or clip.format.subsampling_w != 0 or clip.format.subsampling_h != 0:
        raise ValueError("ShiftLinesHorizontalRange: only YUV444 input is supported.")
    
    # Ensure ymin and ymax are within valid range
    if ymin < 0 or ymin >= clip.height:
        raise ValueError(f"ShiftLinesHorizontalRange: ymin ({ymin}) is out of range.")
    if ymax < ymin or ymax >= clip.height:
        raise ValueError(f"ShiftLinesHorizontalRange: ymax ({ymax}) is out of range.")
    
    # If no shift is needed, return original clip
    if shift == 0:
        return clip

    width = clip.width
    height = clip.height

    # Create shifted version of just the target lines
    mid = clip.std.CropAbs(width=width, height=ymax-ymin+1, left=0, top=ymin)
    black = core.std.BlankClip(mid, width=abs(shift), height=mid.height, color=[0, 128, 128])
    
    if shift > 0:
        shifted_mid = core.std.StackHorizontal([black, mid.std.CropRel(right=shift)])
    else:
        shifted_mid = core.std.StackHorizontal([mid.std.CropRel(left=-shift), black])
    
    shifted_mid = shifted_mid.resize.Point(width=width, height=mid.height)

    # Build the output clip by stacking:
    # 1. Lines above ymin (unchanged)
    # 2. Shifted lines (ymin to ymax)
    # 3. Lines below ymax (unchanged)
    parts = []
    
    if ymin > 0:
        parts.append(clip.std.CropAbs(width=width, height=ymin, left=0, top=0))
    
    parts.append(shifted_mid)
    
    if ymax < height - 1:
        parts.append(clip.std.CropAbs(width=width, height=height-ymax-1, left=0, top=ymax+1))
    
    return core.std.StackVertical(parts)
and called it using:
Code:
import misc
clip = misc.ShiftLinesHorizontally(clip, shift=-24, ymin=468, ymax=479)
But something is wrong.
The 12 line I wanted are shifted (hurray), but the 12 lines above them got shifted too, and I don't see where I got it wrong.


=> does anybody see where my mistake is?

Cu Selur
__________________
Hybrid here in the forum, homepage, its own forum
Selur is offline   Reply With Quote
Old 25th April 2025, 00:33   #2  |  Link
_Al_
Registered User
 
Join Date: May 2011
Posts: 372
I don't know about posted script, but resize can shift nicely, positive or negative, then using overlay:
Code:
import vapoursynth as vs
from vapoursynth import core
import havsfunc
clip = ...

HORIZONTAL_SHIFT = 24
Y = 468
STRIP_HEIGHT = 12

shifted = clip.resize.Bicubic(src_left=HORIZONTAL_SHIFT)
masks = []
masks.append(core.std.BlankClip(clip, height=Y))
masks.append(core.std.BlankClip(clip, height=STRIP_HEIGHT, color=(255,128,128)))
try:
    masks.append(core.std.BlankClip(clip, height=clip.height-(Y+STRIP_HEIGHT)))
except vs.Error:
    pass
mask = core.std.StackVertical(masks)
clip = havsfunc.Overlay(base=clip, overlay=shifted, mask=mask)   
clip.set_output()
_Al_ is offline   Reply With Quote
Old 25th April 2025, 11:15   #3  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
Thanks, I'll try that tomorrow and report back.
(still interested in what I did wrong)

Cu Selur
__________________
Hybrid here in the forum, homepage, its own forum
Selur is offline   Reply With Quote
Old 25th April 2025, 11:51   #4  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
using, whole script https://pastebin.com/rQgHyftP and like you suggested:
Code:
def ShiftLinesHorizontally(clip: vs.VideoNode, shift: int, ymin: int, ymax: int) -> vs.VideoNode:
  # Validate clip format and subsampling
  if clip.format.color_family != vs.YUV or clip.format.subsampling_w != 0 or clip.format.subsampling_h != 0:
      raise ValueError("ShiftLinesHorizontalRange: only YUV444 input is supported.")
  
  # Ensure ymin and ymax are within valid range
  if ymin < 0 or ymin >= clip.height:
      raise ValueError(f"ShiftLinesHorizontalRange: ymin ({ymin}) is out of range.")
  if ymax < ymin or ymax >= clip.height:
      raise ValueError(f"ShiftLinesHorizontalRange: ymax ({ymax}) is out of range.")
  
  # If no shift is needed, return original clip
  if shift == 0:
      return clip
  # shifted version
  shift_height = ymax-ymin+1
  shifted = clip.resize.Bicubic(src_left=shift)
  # masking
  masks = []
  masks.append(core.std.BlankClip(clip, height=ymin))
  masks.append(core.std.BlankClip(clip, height=shift_height, color=(255,128,128)))
  
  try:
      masks.append(core.std.BlankClip(clip, height=clip.height-(ymin+shift_height)))
  except vs.Error:
      pass
  mask = core.std.StackVertical(masks)
  clip = Overlay(base=clip, overlay=shifted, mask=mask, opacity=1)   
  return clip
I get the same effect:

(last few lines get shifted, but so do the lines above)

Cu Selur

Ps.: I also tried first saving a temp clip, after the deinterlacing, and using that as source, but the result is the same.
PPs.: Alternative link to source.
__________________
Hybrid here in the forum, homepage, its own forum

Last edited by Selur; 25th April 2025 at 12:12.
Selur is offline   Reply With Quote
Old 25th April 2025, 14:27   #5  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,165
I dont speak Parseltongue (Python/Vapoursynth), but your clip [TFF] seems to require different shift for even/odd field.
I would expect better result if doing field shifts prior to deinterlace.

I could not see any logic problems in your script [tryin' to understand logic], perhaps it is the differing shift that is causing/contributing to
your suspected problem. [shift after QTGMC deinterlace]

EDIT: [clickme to see independently shifted fields <not deinterlaced>]


EDIT: Is there an Avisynth version of ShiftLinesHorizontally(), I did search but neither D9 nor Google (ShiftLinesHorizontally Site:forum.doom9.org)
found an equivalent (not even in this thread), feels like a JMac698 function.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 25th April 2025 at 17:52.
StainlessS is offline   Reply With Quote
Old 25th April 2025, 15:48   #6  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
The function is something I came up with.
I was planning to use this function in a broader context later, where I would shift different lines differently, but since my simple approach didn't work as expected, I assume I'm missing something obvious.
__________________
Hybrid here in the forum, homepage, its own forum
Selur is offline   Reply With Quote
Old 26th April 2025, 01:38   #7  |  Link
_Al_
Registered User
 
Join Date: May 2011
Posts: 372
I downloaded source. It is only bottom 6 lines, not 12. They wiggle left, right.
If using 12 lines, like you posted it shifts good 6 lines to the left.
_Al_ is offline   Reply With Quote
Old 26th April 2025, 09:03   #8  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
AAAAARRRRGGGGH,... you are right. So easy.
Thanks!
__________________
Hybrid here in the forum, homepage, its own forum
Selur is offline   Reply With Quote
Old 26th April 2025, 12:12   #9  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
Since I couldn't get the whole idea working with plain Vapoursynth I ended up using also numpy.
Script: https://pastebin.com/0qe12xVH
numpyhelper.py: https://pastebin.com/Zau8pGsj (can probably be improved to be faster => https://pastebin.com/zZP8Sxbe)
Not perfect, but way more stable:


Cu Selur
__________________
Hybrid here in the forum, homepage, its own forum

Last edited by Selur; 26th April 2025 at 12:29.
Selur is offline   Reply With Quote
Old 26th April 2025, 19:49   #10  |  Link
Chortos-2
Registered User
 
Join Date: Mar 2012
Location: Rīga, Latvia
Posts: 8
FYI when you really do just want to shift some lines, you can do it in one line using akarin Expr:
Code:
clip.akarin.Expr('Y 476 >= x[24,0]:c x ?')
Chortos-2 is offline   Reply With Quote
Old 27th April 2025, 07:15   #11  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
Nice. Thanks for the info!
So AutoFixLineBorders in numpyhelper could be rewritten to not use numpy? That would be nice and probably a lot faster!
__________________
Hybrid here in the forum, homepage, its own forum

Last edited by Selur; 27th April 2025 at 07:38.
Selur is offline   Reply With Quote
Old 27th April 2025, 13:49   #12  |  Link
Chortos-2
Registered User
 
Join Date: Mar 2012
Location: Rīga, Latvia
Posts: 8
From a quick glance at the code, it seems to dynamically compute the amount of faded columns in each row, right? It’s not impossible to do that in Expr (for a fixed input resolution), but it wouldn’t be pretty, because Expr doesn’t have loops/jumps. NumPy is probably your best bet besides building a native-code plugin.
Chortos-2 is offline   Reply With Quote
Old 27th April 2025, 13:52   #13  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,651
Yes, it checks every line. Thanks for the info, so for now it is numpy.
__________________
Hybrid here in the forum, homepage, its own forum
Selur is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 16:29.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2025, vBulletin Solutions Inc.