Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import vapoursynth as vs
- import numpy as np
- from typing import Optional
- core = vs.core
- def vapoursynth_to_numpy(frame: vs.VideoFrame) -> np.ndarray:
- """Convert a Vapoursynth frame to a numpy array."""
- planes = []
- for i in range(frame.format.num_planes):
- plane = np.array(frame[i], copy=False)
- planes.append(plane)
- return np.dstack(planes) if len(planes) > 1 else planes[0]
- def numpy_to_vapoursynth(array: np.ndarray, reference_frame: vs.VideoFrame) -> vs.VideoFrame:
- """Convert a numpy array to a Vapoursynth frame."""
- if array.ndim == 2:
- array = array[..., np.newaxis]
- # Create a new writable frame
- new_frame = core.std.BlankClip(
- width=reference_frame.width,
- height=reference_frame.height,
- format=reference_frame.format.id,
- length=1
- ).get_frame(0)
- # Ensure the frame is writable
- new_frame = new_frame.copy()
- for i in range(new_frame.format.num_planes):
- plane_data = array[..., i]
- if not plane_data.flags['C_CONTIGUOUS']:
- plane_data = np.ascontiguousarray(plane_data)
- # Get plane dimensions
- width = new_frame.width if i == 0 else new_frame.width >> new_frame.format.subsampling_w
- height = new_frame.height if i == 0 else new_frame.height >> new_frame.format.subsampling_h
- # Reshape if necessary
- if plane_data.shape != (height, width):
- plane_data = plane_data.reshape((height, width))
- # Create a new numpy array for the plane
- dest_plane = np.array(new_frame[i], copy=False)
- np.copyto(dest_plane, plane_data)
- return new_frame
- def test_numpy_conversion(clip: vs.VideoNode, strict: bool = True) -> vs.VideoNode:
- """Test the numpy conversion roundtrip."""
- def _test(n: int, f: vs.VideoFrame) -> vs.VideoFrame:
- try:
- arr = vapoursynth_to_numpy(f)
- new_frame = numpy_to_vapoursynth(arr, f)
- for i in range(f.format.num_planes):
- if not np.array_equal(np.array(f[i]), np.array(new_frame[i])):
- msg = f"Frame {n} plane {i} mismatch!"
- if strict:
- raise ValueError(msg)
- print("Warning:", msg)
- return f.text.Text("Conversion Error").get_frame(0)
- return new_frame
- except Exception as e:
- if strict:
- raise
- print(f"Error processing frame {n}: {str(e)}")
- return f.text.Text("Error").get_frame(0)
- return clip.std.ModifyFrame(clip, _test)
- def AutoFixLineBorders(clip: vs.VideoNode, threshold: int = 24, bordersize: int = 4, debug: bool = False) -> vs.VideoNode:
- if clip.format.id != vs.YUV444P8:
- raise ValueError("AutoFixLineBorders: Only YUV444P8 input supported.")
- def shift_lines(n: int, f: vs.VideoFrame) -> vs.VideoFrame:
- arr = vapoursynth_to_numpy(f)
- if arr.ndim == 2:
- arr = arr[..., np.newaxis]
- y = arr[..., 0]
- u = arr[..., 1]
- v = arr[..., 2]
- height, width = y.shape
- new_y = np.copy(y)
- new_u = np.copy(u)
- new_v = np.copy(v)
- for row in range(height):
- # Zähle die dunklen Pixel am Anfang der Zeile
- count = 0
- for col in range(width):
- if y[row, col] <= threshold:
- count += 1
- else:
- break
- detected_bordersize = count
- if detected_bordersize > 60:
- detected_bordersize = 0
- shift = detected_bordersize - bordersize
- if shift == 0:
- continue # Keine Verschiebung nötig
- if debug:
- # Nur debug Markierung
- new_y[row, :] = 128 # Grau
- new_u[row, :] = 255 # Lila/Orange
- new_v[row, :] = 128
- continue
- if shift > 0:
- # Nach links verschieben, Rechts auffüllen mit Schwarz
- new_y[row, :-shift] = y[row, shift:]
- new_y[row, -shift:] = 16 # Y=16 (schwarz)
- new_u[row, :-shift] = u[row, shift:]
- new_u[row, -shift:] = 128 # U=128 (neutral)
- new_v[row, :-shift] = v[row, shift:]
- new_v[row, -shift:] = 128 # V=128 (neutral)
- # shift < 0 würde bedeuten "nach rechts" – das brauchst du NICHT!
- result = np.dstack((new_y, new_u, new_v)).astype(np.uint8)
- return numpy_to_vapoursynth(result, f)
- return clip.std.ModifyFrame(clips=clip, selector=shift_lines)
Advertisement
Add Comment
Please, Sign In to add comment