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. |
![]() |
#1 | Link |
Registered User
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) Code:
import misc clip = misc.ShiftLinesHorizontally(clip, shift=-24, ymin=468, ymax=479) 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 |
![]() |
![]() |
![]() |
#2 | Link |
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() |
![]() |
![]() |
![]() |
#4 | Link |
Registered User
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 ![]() (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. Last edited by Selur; 25th April 2025 at 12:12. |
![]() |
![]() |
![]() |
#5 | Link |
HeartlessS Usurer
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. |
![]() |
![]() |
![]() |
#6 | Link |
Registered User
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. |
![]() |
![]() |
![]() |
#9 | Link |
Registered User
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 Last edited by Selur; 26th April 2025 at 12:29. |
![]() |
![]() |
![]() |
#10 | Link |
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 ?') |
![]() |
![]() |
![]() |
#12 | Link |
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.
|
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
Display Modes | |
|
|