Added all functionality to cut_to_length
This commit is contained in:
1 parent
6c1687d6bc
commit
cb56886a4b
5 files changed
+235
-149
No files matched your search
+57
-66
@@ -1,104 +1,95 @@
|
||||
import subprocess
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import sys
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
# --- Setup ---
|
||||
video_a = "/home/honney/projects/mkv/Saga of Tanya the Evil/Season 1/Saga Of Tanya The Evil - S01E01 - The Devil Of The Rhine.mkv"
|
||||
video_b = "/home/honney/projects/mkv/Saga of Tanya the Evil (2017-2017) [imdbid-tt6455986]/Season 01/Saga of Tanya the Evil S01E001.mkv"
|
||||
video_a = "/home/honney/projects/mkv/Anohana - The Flower We Saw That Day - S01 (BD 1080p HEVC Opus) [derpie]/[derpie] Anohana - The Flower We Saw That Day - S01E01 (BD 1080p HEVC Opus) [Dual Audio].mkv"
|
||||
video_b = "/home/honney/projects/mkv/AnoHana The Flower We Saw That Day (2011-2011) [imdbid-tt1913273]/Season 01/AnoHana The Flower We Saw That Day S01E001.mkv"
|
||||
|
||||
FPS_NATIVE = 24000/1001
|
||||
WIDTH = 160
|
||||
HEIGHT = int((WIDTH/16)*9)
|
||||
FPS = 24000/1001
|
||||
MAX_OFFSET_SECONDS = 2 # ±2 Sekunden Suche
|
||||
MAX_OFFSET_SECONDS = 1
|
||||
|
||||
# FAKTOR FÜR PRÄZISION:
|
||||
# 2 = Doppelte FPS für die Rechnung (Sub-Frame)
|
||||
# 10 = Zehnfache Präzision (reicht für Mikrosekunden-Sync)
|
||||
UPSAMPLE_FACTOR = 10
|
||||
FPS_VIRTUAL = FPS_NATIVE * UPSAMPLE_FACTOR
|
||||
|
||||
def extract_hist_signal(video):
|
||||
"""Extrahiert pro Frame einen Histogrammvektor unter Nutzung von VA-API (AMD GPU)."""
|
||||
# Wir nutzen vaapi für Hardware-Dekodierung und Scaling
|
||||
proc = subprocess.Popen([
|
||||
"ffmpeg",
|
||||
"-loglevel", "error",
|
||||
"-hwaccel", "vaapi", # Nutze VA-API Hardware-Beschleunigung
|
||||
"-hwaccel_device", "/dev/dri/renderD128", # Pfad zur RX 7900 XTX
|
||||
"-hwaccel_output_format", "vaapi",
|
||||
"-i", video,
|
||||
"-vf", (
|
||||
f"fps={FPS}," # Framerate Anpassung
|
||||
f"scale_vaapi=w={WIDTH}:h={HEIGHT}:format=gray8," # GPU-Scaling + Grayscale
|
||||
"hwdownload,format=gray8" # Zurück in den RAM schieben
|
||||
),
|
||||
"-f", "rawvideo",
|
||||
"-"
|
||||
"ffmpeg", "-loglevel", "error", "-i", video,
|
||||
"-vf", f"scale={WIDTH}:{HEIGHT},format=gray",
|
||||
"-f", "rawvideo", "-"
|
||||
], stdout=subprocess.PIPE)
|
||||
|
||||
frame_size = WIDTH * HEIGHT
|
||||
hist_signal = []
|
||||
|
||||
# Der restliche Loop bleibt gleich, da die Histogramm-Berechnung
|
||||
# (np.histogram) weiterhin auf der CPU via NumPy erfolgt.
|
||||
while True:
|
||||
data = proc.stdout.read(frame_size)
|
||||
if not data or len(data) < frame_size:
|
||||
break
|
||||
if not data or len(data) < frame_size: break
|
||||
frame = np.frombuffer(data, dtype=np.uint8)
|
||||
hist, _ = np.histogram(frame, bins=32, range=(0, 255))
|
||||
hist_signal.append(hist / np.sum(hist))
|
||||
|
||||
hist, _ = np.histogram(frame, bins=64, range=(0, 255))
|
||||
hist_signal.append(hist / (np.sum(hist) + 1e-6))
|
||||
proc.wait()
|
||||
return np.array(hist_signal)
|
||||
|
||||
print(f"Extracting signals (Native FPS: {FPS_NATIVE:.4f})...")
|
||||
a_raw = extract_hist_signal(video_a)
|
||||
b_raw = extract_hist_signal(video_b)
|
||||
|
||||
print("Extracting signal of video A")
|
||||
a = extract_hist_signal(video_a)
|
||||
# 1. In 1D-Signal umwandeln (Helligkeits-Schnitt)
|
||||
sig_a_raw = np.mean(a_raw, axis=1)
|
||||
sig_b_raw = np.mean(b_raw, axis=1)
|
||||
|
||||
print("Extracting signal of video B")
|
||||
b = extract_hist_signal(video_b)
|
||||
# 2. UPSAMPLING (Die "doppelte FPS" Logik)
|
||||
# Wir erstellen eine glatte Kurve durch die Frames und tasten sie feiner ab
|
||||
def upsample_signal(sig, factor):
|
||||
x = np.arange(len(sig))
|
||||
f = interp1d(x, sig, kind='cubic') # Kubische Interpolation für weiche Übergänge
|
||||
x_new = np.linspace(0, len(sig) - 1, len(sig) * factor)
|
||||
return f(x_new)
|
||||
|
||||
print("Calculating cross correlation")
|
||||
print(f"Upsampling signals by factor {UPSAMPLE_FACTOR}...")
|
||||
sig_a = upsample_signal(sig_a_raw, UPSAMPLE_FACTOR)
|
||||
sig_b = upsample_signal(sig_b_raw, UPSAMPLE_FACTOR)
|
||||
|
||||
max_lag_frames = int(MAX_OFFSET_SECONDS * FPS)
|
||||
n_a, n_bins = a.shape
|
||||
n_b, _ = b.shape
|
||||
# 3. Zentrieren für bessere Korrelation
|
||||
sig_a -= np.mean(sig_a)
|
||||
sig_b -= np.mean(sig_b)
|
||||
|
||||
lags = np.arange(-max_lag_frames, max_lag_frames + 1)
|
||||
corr = []
|
||||
print("Calculating high-precision cross correlation...")
|
||||
corr = np.correlate(sig_a, sig_b, mode='full')
|
||||
lags = np.arange(-len(sig_b) + 1, len(sig_a))
|
||||
|
||||
for lag in lags:
|
||||
if lag < 0:
|
||||
# Offset negativ → Video B vor Video A
|
||||
sub_a = a[:lag] # bis lag Frames von Video A
|
||||
sub_b = b[-lag:] # von -lag bis Ende von Video B
|
||||
else:
|
||||
sub_a = a[lag:] # ab lag bis Ende von Video A
|
||||
sub_b = b[:n_b - lag] # bis n_b - lag von Video B
|
||||
# Suchbereich einschränken (jetzt basierend auf virtueller FPS)
|
||||
max_lag_v = int(MAX_OFFSET_SECONDS * FPS_VIRTUAL)
|
||||
search_range = (lags >= -max_lag_v) & (lags <= max_lag_v)
|
||||
|
||||
# Frames angleichen
|
||||
min_len = min(len(sub_a), len(sub_b))
|
||||
if min_len == 0:
|
||||
corr.append(0)
|
||||
continue
|
||||
sub_a = sub_a[:min_len]
|
||||
sub_b = sub_b[:min_len]
|
||||
filtered_corr = corr[search_range]
|
||||
filtered_lags = lags[search_range]
|
||||
|
||||
corr.append(np.sum(sub_a * sub_b))
|
||||
best_index = np.argmax(filtered_corr)
|
||||
best_lag_v = filtered_lags[best_index]
|
||||
|
||||
corr = np.array(corr)
|
||||
best_index = np.argmax(corr)
|
||||
best_lag = lags[best_index]
|
||||
# Der Offset berechnet sich nun aus dem virtuellen Lag und der virtuellen FPS
|
||||
best_offset_seconds = best_lag_v / FPS_VIRTUAL
|
||||
|
||||
print("Best offset frames:", best_lag)
|
||||
print("Best offset seconds:", best_lag / FPS)
|
||||
|
||||
sys.exit()
|
||||
print("-" * 30)
|
||||
print(f"Best virtual lag: {best_lag_v} units")
|
||||
print(f"Resulting frames: {best_lag_v / UPSAMPLE_FACTOR:.3f}")
|
||||
print(f"Best offset seconds: {best_offset_seconds:.12f}")
|
||||
print("-" * 30)
|
||||
|
||||
# Plot
|
||||
plt.figure(figsize=(10, 5))
|
||||
plt.plot(lags / FPS, corr, marker='o')
|
||||
plt.axvline(best_lag / FPS, color='r', linestyle='--', label=f"Best offset = {best_lag / FPS:.3f}s")
|
||||
plt.figure(figsize=(12, 5))
|
||||
plt.plot(filtered_lags / FPS_VIRTUAL, filtered_corr)
|
||||
plt.axvline(best_offset_seconds, color='r', linestyle='--', label=f"Peak: {best_offset_seconds:.6f}s")
|
||||
plt.title(f"High-Precision Sync (Upsampled x{UPSAMPLE_FACTOR})")
|
||||
plt.xlabel("Offset (seconds)")
|
||||
plt.ylabel("Cross correlation")
|
||||
plt.title("Video cross correlation (Histogram-based)")
|
||||
plt.ylabel("Correlation")
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.show()
|
||||
Reference in new issue
Block a user