xpark.dataset.VideoExtractSubtitle#
- class xpark.dataset.VideoExtractSubtitle(*, mode: Literal['ocr', 'asr', 'embedded', 'auto'] = 'auto', subtitle_stream_index: int | None = None, asr_model: str = 'Systran/faster-whisper-large-v3', fps: int = 2, keyframes_only: bool = False, start_time: float = 0.0, end_time: float | None = None, region: tuple[float, float, float, float] = (0.78, 0.99, 0.05, 0.95), similarity_threshold: float = 0.8, ocr_mode: Literal['accurate', 'fast'] = 'accurate', engine: Literal['paddleocr', 'rapidocr'] = 'paddleocr', ocr_batch: int | None = None, frame_window: int = 60, filters: Sequence[Callable[[list[list[TextLayoutBox]]], list[list[TextLayoutBox]]]] | None = None, **ocr_kwargs)#
Extract subtitles from video.
Four extraction modes are available via
mode:"auto"(default): tries"embedded"first, and falls back to"ocr"for videos without a usable embedded subtitle track. Note: the fallback from OCR to ASR can be realized via a pipeline: VideoTextAreaRatio —> VideoExtractSubtitle (asr mode) / VideoExtractSubtitle (auto mode)."ocr": recognizes subtitle text burned into the video frames."asr": transcribes the spoken audio track into subtitles."embedded": extracts an existing text-based subtitle track already contained in the video (e.g.srt/ass/ssa/mov_text/webvtt). Image-based subtitle tracks (e.g. PGS) have no extractable text; use"ocr"or"auto"for those.
Input accepts a local path, a remote URI (
cos:///s3:///http(s)://), or raw video bytes.- Parameters:
mode – Extraction mode, one of
"auto"(default),"ocr","asr", or"embedded".subtitle_stream_index – [embedded/auto] Which subtitle stream to read when a video has more than one. Default
Nonepicks the first subtitle stream.asr_model – [asr] Speech recognition model to use. Default
"Systran/faster-whisper-large-v3".fps – [ocr/auto] Frame sampling rate in frames per second. A sampled frame identical to the previous one is not OCR’d again (its boxes are reused). Default
2.keyframes_only – [ocr/auto] Sample only from keyframes. Default
False.start_time – [ocr/auto] Start of the sampled time range in seconds. Default
0.0.end_time – [ocr/auto] End of the sampled time range in seconds. Default
Nonefor the full duration.region – [ocr/auto] Normalized
(top, bottom, left, right)band where subtitle text is expected. Default(0.78, 0.99, 0.05, 0.95).similarity_threshold – [ocr/auto] Similarity in
[0, 1]used to merge consecutive frames showing the same/similar subtitle text. Default0.8.ocr_mode – [ocr/auto] OCR accuracy mode,
"accurate"(default) or"fast".engine – [ocr/auto] OCR backend,
"paddleocr"(default) or"rapidocr".ocr_batch – [ocr/auto] Max number of frames fed to the OCR backend in a single call. Defaults to
64on GPU workers and4on CPU workers.frame_window – [ocr/auto] Decode and OCR in windows of this many seconds, so a long video never holds all its sampled frames in memory at once. Default
60.filters –
[ocr/auto] Post-process filters applied, in order, to all accumulated boxes before merging. Each filter maps
list[list[TextLayoutBox]] -> list[list[TextLayoutBox]](per-frame box lists, full-frame normalized). Filters run on every accumulated box before theregioncheck. DefaultNoneusesDEFAULT_FILTERS. Pass a custom sequence to replace the defaults (an empty sequence disables post-processing); usefilter_boxes()to turn a per-box predicate into a filter. Filters must be picklable for distributed runs.DEFAULT_FILTERSis:static_text_filter(): drop static overlay text (logos/watermarks) that persists across most frames.small_text_filter(): drop boxes shorter than 3% of the frame height.normalize_text_filter(): normalize text with Unicode NFKC (full-width to half-width).auto_region_filter(): drop boxes outside the auto-detected subtitle band. Must run last: watermarks and buttons would otherwise pollute the band’s frequency stats.
**ocr_kwargs – [ocr/auto] Extra params forwarded to the OCR backend.
Examples
from xpark.dataset import VideoExtractSubtitle, from_items from xpark.dataset.expressions import col ds = from_items([{"video": "example.mp4"}]) # Default: embedded subtitle track if present, on-screen OCR otherwise. ds = ds.with_column( "subtitle", VideoExtractSubtitle() .options(num_workers={"CPU": 1}, batch_size=1) .with_column(col("video")), ) # Hardcoded (burned-in) subtitles via OCR only. ds = ds.with_column( "subtitle", VideoExtractSubtitle(mode="ocr") .options(num_workers={"CPU": 1}, batch_size=1) .with_column(col("video")), ) # Spoken subtitles via ASR. ds = ds.with_column( "subtitle", VideoExtractSubtitle(mode="asr") .options(num_workers={"GPU": 1}, batch_size=1) .with_column(col("video")), ) # Existing subtitle track already muxed into the container. ds = ds.with_column( "subtitle", VideoExtractSubtitle(mode="embedded") .options(num_workers={"CPU": 1}, batch_size=1) .with_column(col("video")), )
Methods
__call__(videos)Call self as a function.
options(**kwargs)with_column(videos)- __call__(videos: pa.ChunkedArray) pa.Array#
Call self as a function.
- options(**kwargs: Unpack[ExprUDFOptions]) Self#
- with_column(videos: pa.ChunkedArray) pa.Array#