after gemini
This commit is contained in:
commit
6c18827b93
2 files changed
+141
No files matched your search
@@ -0,0 +1,46 @@
|
|||||||
|
import subprocess
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
offset_seconds = -0.1918081918081918
|
||||||
|
offset_ms = offset_seconds * 1000
|
||||||
|
preview_length = 10
|
||||||
|
# 0.1 entspricht 1/10 der Geschwindigkeit
|
||||||
|
speed_factor = 0.1
|
||||||
|
|
||||||
|
filter_complex = (
|
||||||
|
f"[1:v]scale=640:-1[left_vid];"
|
||||||
|
f"[0:v]scale=640:-1,hflip,drawtext="
|
||||||
|
f"fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:"
|
||||||
|
f"text='Offset\\: {offset_ms:.2f}ms':"
|
||||||
|
f"x=10:y=10:fontsize=24:fontcolor=white:box=1:boxcolor=black@0.5[right_vid];"
|
||||||
|
f"[left_vid][right_vid]hstack=inputs=2"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Schritt 1: Video rendern
|
||||||
|
cmd_ffmpeg = [
|
||||||
|
"ffmpeg", "-y",
|
||||||
|
"-itsoffset", f"{offset_seconds:.6f}", "-i", video_b,
|
||||||
|
"-i", video_a,
|
||||||
|
"-filter_complex", filter_complex,
|
||||||
|
"-t", str(preview_length),
|
||||||
|
"-c:v", "libx264",
|
||||||
|
"-crf", "18",
|
||||||
|
"-preset", "fast",
|
||||||
|
"sync_preview.mp4"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Schritt 2: MPV starten
|
||||||
|
cmd_mpv = [
|
||||||
|
"mpv",
|
||||||
|
f"--speed={speed_factor}",
|
||||||
|
"--title=Sync-Check (1/10 Speed)",
|
||||||
|
"sync_preview.mp4"
|
||||||
|
]
|
||||||
|
|
||||||
|
print("Rendere Vorschau...")
|
||||||
|
subprocess.run(cmd_ffmpeg)
|
||||||
|
|
||||||
|
print("Starte MPV...")
|
||||||
|
subprocess.run(cmd_mpv)
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import subprocess
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
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"
|
||||||
|
|
||||||
|
FPS_NATIVE = 24000/1001
|
||||||
|
WIDTH = 160
|
||||||
|
HEIGHT = int((WIDTH/16)*9)
|
||||||
|
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):
|
||||||
|
proc = subprocess.Popen([
|
||||||
|
"ffmpeg", "-loglevel", "error", "-i", video,
|
||||||
|
"-vf", f"scale={WIDTH}:{HEIGHT},format=gray",
|
||||||
|
"-f", "rawvideo", "-"
|
||||||
|
], stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
frame_size = WIDTH * HEIGHT
|
||||||
|
hist_signal = []
|
||||||
|
while True:
|
||||||
|
data = proc.stdout.read(frame_size)
|
||||||
|
if not data or len(data) < frame_size: break
|
||||||
|
frame = np.frombuffer(data, dtype=np.uint8)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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(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)
|
||||||
|
|
||||||
|
# 3. Zentrieren für bessere Korrelation
|
||||||
|
sig_a -= np.mean(sig_a)
|
||||||
|
sig_b -= np.mean(sig_b)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
filtered_corr = corr[search_range]
|
||||||
|
filtered_lags = lags[search_range]
|
||||||
|
|
||||||
|
best_idx = np.argmax(filtered_corr)
|
||||||
|
best_lag_v = filtered_lags[best_idx]
|
||||||
|
|
||||||
|
# Der Offset berechnet sich nun aus dem virtuellen Lag und der virtuellen FPS
|
||||||
|
best_offset_seconds = best_lag_v / FPS_VIRTUAL
|
||||||
|
|
||||||
|
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=(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("Correlation")
|
||||||
|
plt.legend()
|
||||||
|
plt.grid(True)
|
||||||
|
plt.show()
|
||||||
Reference in new issue
Block a user