|
|
import cv2 |
|
|
import os |
|
|
import shutil |
|
|
import random |
|
|
from tqdm import tqdm |
|
|
|
|
|
[os.system(c) for c in ["clear", "cls", "color a"]] |
|
|
|
|
|
DEFAULT_DYNAMIC_INTERVALS: list[list[float]] = [ |
|
|
[0.00, 9.44], [9.44, 12.07], [19.54, 22.04], [29.50, 31.14], |
|
|
[39.50, 41.14], [49.57, 50.55], [59.44, 61.14], |
|
|
] |
|
|
|
|
|
POLA_INTERVAL: list[int] = [10, 10] |
|
|
|
|
|
REAL_VIDEO_PATH = "assets/ppt_real.mp4" |
|
|
REV_VIDEO_PATH = "assets/ppt_reverse.mp4" |
|
|
OUTPUT_DIR = "results" |
|
|
|
|
|
def get_video_duration(video_path: str) -> int | None: |
|
|
""" |
|
|
Membaca durasi file video menggunakan OpenCV dan mengembalikannya |
|
|
sebagai integer dalam detik. |
|
|
(TQDM dihilangkan dari sini untuk mempercepat pembacaan metadata awal) |
|
|
""" |
|
|
if not os.path.exists(video_path): |
|
|
print(f"Error: File video tidak ditemukan di {video_path}") |
|
|
return 0 |
|
|
|
|
|
cap = cv2.VideoCapture(video_path) |
|
|
if not cap.isOpened(): |
|
|
print(f"Error: Tidak dapat membuka file video {video_path}") |
|
|
return None |
|
|
|
|
|
fps = cap.get(cv2.CAP_PROP_FPS) |
|
|
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
|
|
cap.release() |
|
|
|
|
|
if fps == 0 or frame_count == 0: |
|
|
print("Error: Tidak dapat mengambil FPS atau jumlah frame.") |
|
|
return None |
|
|
|
|
|
return int(frame_count / fps) |
|
|
|
|
|
def extract_video_segment(input_path: str, output_path: str, start_sec: float, end_sec: float): |
|
|
""" |
|
|
Memotong segmen video dari input_path berdasarkan start_sec dan end_sec, |
|
|
lalu menyimpannya ke output_path dengan progress bar. |
|
|
""" |
|
|
cap = cv2.VideoCapture(input_path) |
|
|
if not cap.isOpened(): |
|
|
print(f"Error membuka video: {input_path}") |
|
|
return |
|
|
|
|
|
fps = cap.get(cv2.CAP_PROP_FPS) |
|
|
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
|
|
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
|
|
fourcc = cv2.VideoWriter_fourcc(*'avc1') |
|
|
|
|
|
writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) |
|
|
|
|
|
start_frame = int(start_sec * fps) |
|
|
end_frame = int(end_sec * fps) |
|
|
total_frames_to_write = end_frame - start_frame |
|
|
|
|
|
if total_frames_to_write <= 0: |
|
|
print(f"Warning: Tidak ada frame untuk diekstrak untuk segmen {output_path}") |
|
|
cap.release() |
|
|
writer.release() |
|
|
return |
|
|
|
|
|
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame) |
|
|
|
|
|
segment_pbar_desc = os.path.basename(output_path) |
|
|
with tqdm(total=total_frames_to_write, desc=f"Mengekstrak {segment_pbar_desc}", unit="frame", leave=False) as pbar: |
|
|
current_frame = start_frame |
|
|
while current_frame < end_frame: |
|
|
ret, frame = cap.read() |
|
|
if not ret: |
|
|
break |
|
|
writer.write(frame) |
|
|
pbar.update(1) |
|
|
current_frame += 1 |
|
|
|
|
|
cap.release() |
|
|
writer.release() |
|
|
|
|
|
if len(os.listdir(OUTPUT_DIR)) > 0: |
|
|
exit() if "n" in input("Lanjutkan proses? [N] ").lower() else "" |
|
|
shutil.rmtree(OUTPUT_DIR) |
|
|
|
|
|
os.makedirs(OUTPUT_DIR, exist_ok=True) |
|
|
print(f"Direktori output dipastikan ada: '{OUTPUT_DIR}'") |
|
|
|
|
|
REAL_VIDEO_DURATION = get_video_duration(REAL_VIDEO_PATH) or 0 |
|
|
REV_VIDEO_DURATION = get_video_duration(REV_VIDEO_PATH) or 0 |
|
|
|
|
|
LONG_DURATION = max(REAL_VIDEO_DURATION, REV_VIDEO_DURATION) |
|
|
print(f"Durasi video terpanjang (LONG_DURATION): {LONG_DURATION} detik") |
|
|
|
|
|
dynamicIntervals: list[list[float]] = DEFAULT_DYNAMIC_INTERVALS.copy() |
|
|
lastSecond = dynamicIntervals[-1][1] if dynamicIntervals else 0.0 |
|
|
|
|
|
with tqdm(total=LONG_DURATION, initial=lastSecond, desc="Membuat Interval", unit="s") as pbar: |
|
|
while lastSecond < LONG_DURATION: |
|
|
previous_lastSecond = lastSecond |
|
|
lastInterval = dynamicIntervals[-1] |
|
|
|
|
|
step = random.uniform(5.0, 10.0) |
|
|
new_start_time: float = lastInterval[0] + POLA_INTERVAL[0] |
|
|
new_end_time: float = lastInterval[1] + POLA_INTERVAL[1] |
|
|
|
|
|
if new_start_time >= LONG_DURATION: |
|
|
break |
|
|
|
|
|
lastSecond = min(new_end_time, LONG_DURATION) |
|
|
|
|
|
dynamicIntervals.append([ |
|
|
new_start_time, lastSecond |
|
|
]) |
|
|
|
|
|
increment = lastSecond - previous_lastSecond |
|
|
pbar.update(increment) |
|
|
|
|
|
reverseDynamicIntervals: list[list[float]] = [] |
|
|
for start, end in dynamicIntervals: |
|
|
rev_start = round(LONG_DURATION - end, 2) |
|
|
rev_end = round(LONG_DURATION - start, 2) |
|
|
reverseDynamicIntervals.append([rev_start, rev_end]) |
|
|
|
|
|
print(f"Total {len(dynamicIntervals)}, {len(reverseDynamicIntervals)} interval dinamis dibuat.") |
|
|
|
|
|
print("\nMemproses segmen video REAL...") |
|
|
for idx, interval in enumerate(tqdm(dynamicIntervals, desc="Video REAL")): |
|
|
start_time, end_time = interval |
|
|
output_filename = os.path.join(OUTPUT_DIR, f"real_{idx}.mp4") |
|
|
extract_video_segment(REAL_VIDEO_PATH, output_filename, start_time, end_time) |
|
|
|
|
|
print("\nMemproses segmen video REVERSE...") |
|
|
for idx, interval in enumerate(tqdm(reverseDynamicIntervals, desc="Video REVERSE")): |
|
|
start_time, end_time = interval |
|
|
output_filename = os.path.join(OUTPUT_DIR, f"reverse_{idx}.mp4") |
|
|
extract_video_segment(REV_VIDEO_PATH, output_filename, start_time, end_time) |
|
|
|
|
|
print("\n\nSemua proses ekstraksi video telah selesai.") |