# 下載VideoSubFinder, 改path_VideoSubFinder
# 裝CUDA 12.9
# pip install paddlepaddle-gpu -i https://www.paddlepaddle.org.cn/packages/nightly/cu129/
# pip install paddleocr
import os
import glob
import subprocess
import time
import argparse
import numpy as np
from PIL import Image
from paddleocr import PaddleOCR
path_VideoSubFinder = r"D:\work\workspace\SubtitleOCR\VideoSubFinder"
fn_VideoSubFinder = "VideoSubFinderWXW_intel.exe"
path_VideoSubFinder_exe = fr"{path_VideoSubFinder}\{fn_VideoSubFinder}"
path_sub_images = fr"{path_VideoSubFinder}\RGBImages"
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--image', required=False, help='image file path')
parser.add_argument('-v', '--video', required=False, help='video file path')
parser.add_argument('-te', '--sub_top', type=float, default=0.15, help='top of subtitle, percent offset of height, from bottom. (default=0.15)')
parser.add_argument('-be', '--sub_bottom', type=float, default=0.05, help='bottom of subtitle, percent offset of height, from bottom. (default=0.05)')
parser.add_argument('-mw', '--ocr_min_width', type=int, default=100, help='ocr: minimum text width. (default=100)')
parser.add_argument('-mh', '--ocr_min_height', type=int, default=50, help='ocr: minimum text height. (default=50)')
args = parser.parse_args()
print("Initializing PaddleOCR...")
ocr = PaddleOCR(
text_detection_model_name="PP-OCRv5_server_det",
text_recognition_model_name="PP-OCRv5_server_rec",
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
)
def do_ocr(img_path):
try:
img = np.array(Image.open(img_path).convert('RGB'))
except Exception as e:
print(f"[ERROR] Failed to open image {img_path}: {e}")
return ""
result = ocr.predict(img)
for res in result:
if 'rec_texts' not in res or 'rec_polys' not in res:
continue
txts, polys = res['rec_texts'], res['rec_polys']
kept_txts = []
for txt, poly in zip(txts, polys):
xs = [p[0] for p in poly]
ys = [p[1] for p in poly]
width, height = max(xs) - min(xs), max(ys) - min(ys)
if height >= args.ocr_min_height and width >= args.ocr_min_width:
kept_txts.append(txt)
if kept_txts:
return " ".join(kept_txts)
return ""
# If only an image is provided, do single OCR and exit
if args.image and not args.video:
print(f"Performing OCR on image: {args.image}")
txt = do_ocr(args.image)
print(txt)
print("Single image OCR complete.")
exit(0)
if not args.video:
print("[ERROR] Missing required argument: -v/--video when not using -i/--image only.")
exit(1)
# Step 1: Run VideoSubFinder
print("Step 1: Running VideoSubFinder...")
params = [path_VideoSubFinder_exe, '-uc', '-c', '-r', '-i', args.image or args.video, '-te', str(args.sub_top), '-be', str(args.sub_bottom)]
proc = subprocess.Popen(params)
print("Waiting for VideoSubFinder to complete...")
flag_first = True
path_sub_images_jpeg = os.path.join(path_sub_images, '*.jpeg')
while proc.poll() is None:
time.sleep(3)
jpegs = glob.glob(path_sub_images_jpeg)
if jpegs:
print(jpegs[-1], flush=True)
if flag_first:
os.startfile(jpegs[0])
flag_first = False
print("VideoSubFinder finished.")
# Step 2: Get subtitle image files
print("Step 2: Collecting subtitle images...")
imgs = sorted(glob.glob(os.path.join(path_sub_images, '*.jpeg')))
print(f"Found {len(imgs)} subtitle images.")
# Step 3: OCR each subtitle image and generate VTT
print("Step 3: Performing OCR and writing .vtt file...")
output_vtt = f"{args.video}.vtt"
with open(output_vtt, 'w', encoding='utf-8') as f:
f.write("WEBVTT\n\n")
for img in imgs:
base = os.path.basename(img)
parts = base.split('__')
if len(parts) < 2:
continue
start, end = parts[0], parts[1].split('_000000')[0]
start = start.replace('_', ':', 2).replace('_', '.')
end = end.replace('_', ':', 2).replace('_', '.')
txt = do_ocr(img)
if not txt:
continue
print(f"[{time.strftime('%H%M%S')}] {start} --> {end}: {txt}")
f.write(f"{start} --> {end}\n{txt}\n\n")
print(f"Subtitle extraction complete: {output_vtt}")