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. Domains: forum.doom9.org / forum.doom9.net / forum.doom9.se |
|
|
#1 | Link |
|
.
![]() Join Date: Oct 2001
Location: Germany
Posts: 7,860
|
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: 398
|
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 |
|
.
![]() Join Date: Oct 2001
Location: Germany
Posts: 7,860
|
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,411
|
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 |
|
.
![]() Join Date: Oct 2001
Location: Germany
Posts: 7,860
|
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 |
|
.
![]() Join Date: Oct 2001
Location: Germany
Posts: 7,860
|
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 | |
|
|