Video-Text-to-Text
Transformers
Safetensors
English
Chinese
moss_vl
feature-extraction
Base
Video-Understanding
Image-Understanding
MOSS-VL
OpenMOSS
multimodal
video
vision-language
custom_code
Instructions to use OpenMOSS-Team/MOSS-VL-Base-0708 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use OpenMOSS-Team/MOSS-VL-Base-0708 with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("OpenMOSS-Team/MOSS-VL-Base-0708", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Update video_processing_moss_vl.py
Browse files- video_processing_moss_vl.py +50 -22
video_processing_moss_vl.py
CHANGED
|
@@ -39,6 +39,39 @@ from transformers.video_utils import VideoMetadata, group_videos_by_shape, reord
|
|
| 39 |
logger = logging.get_logger(__name__)
|
| 40 |
|
| 41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
# -----------------------------------------------------------------------------
|
| 43 |
# Torchcodec video frame extraction utilities
|
| 44 |
# -----------------------------------------------------------------------------
|
|
@@ -198,7 +231,17 @@ def split_indices(indices: List[Union[int, float]], num_chunks: int) -> List[Lis
|
|
| 198 |
|
| 199 |
Returns:
|
| 200 |
List of index chunks.
|
|
|
|
|
|
|
|
|
|
| 201 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
chunk_size = len(indices) // num_chunks
|
| 203 |
chunks = []
|
| 204 |
for i in range(num_chunks - 1):
|
|
@@ -239,7 +282,7 @@ def decode_with_multithreading(indices: List[int], num_threads: int, video_path:
|
|
| 239 |
dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
|
| 240 |
"""
|
| 241 |
chunks = split_indices(indices, num_chunks=num_threads)
|
| 242 |
-
results = Parallel(n_jobs=
|
| 243 |
delayed(decode_sequentially)(chunk, video_path) for chunk in chunks
|
| 244 |
)
|
| 245 |
|
|
@@ -266,22 +309,7 @@ def decode_sequentially_timestamp(timestamp_list: List[float], video_path: str,
|
|
| 266 |
try:
|
| 267 |
metadata = decoder.metadata
|
| 268 |
|
| 269 |
-
|
| 270 |
-
if min_pts is None:
|
| 271 |
-
min_pts = 0.0
|
| 272 |
-
|
| 273 |
-
max_pts = None
|
| 274 |
-
if metadata.num_frames_from_content and metadata.average_fps:
|
| 275 |
-
max_pts = (metadata.num_frames_from_content - 1) / metadata.average_fps + min_pts
|
| 276 |
-
elif metadata.end_stream_seconds_from_content is not None:
|
| 277 |
-
max_pts = metadata.end_stream_seconds_from_content
|
| 278 |
-
else:
|
| 279 |
-
max_pts = metadata.duration_seconds
|
| 280 |
-
|
| 281 |
-
if max_pts is not None and max_pts > 0:
|
| 282 |
-
timestamp_list = [max(min_pts, min(t, max_pts)) for t in timestamp_list]
|
| 283 |
-
elif min_pts > 0:
|
| 284 |
-
timestamp_list = [max(min_pts, t) for t in timestamp_list]
|
| 285 |
|
| 286 |
return decoder.get_frames_played_at(timestamp_list)
|
| 287 |
finally:
|
|
@@ -301,7 +329,7 @@ def timestamp_decode_with_multithreading(timestamp_list: List[float], num_thread
|
|
| 301 |
dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
|
| 302 |
"""
|
| 303 |
chunks = split_indices(timestamp_list, num_chunks=num_threads)
|
| 304 |
-
results = Parallel(n_jobs=
|
| 305 |
delayed(decode_sequentially_timestamp)(chunk, video_path) for chunk in chunks
|
| 306 |
)
|
| 307 |
|
|
@@ -702,10 +730,9 @@ class MossVLVideoProcessor(BaseVideoProcessor):
|
|
| 702 |
|
| 703 |
if len(segment) == 1:
|
| 704 |
# Single frame at specified time
|
| 705 |
-
|
| 706 |
-
frame_batch = decoder.get_frames_played_at(
|
| 707 |
video_tensor = frame_batch.data
|
| 708 |
-
actual_timestamps = [timestamp]
|
| 709 |
sample_count = 1
|
| 710 |
else:
|
| 711 |
# Segment [start, end) - left-closed, right-open interval
|
|
@@ -724,6 +751,8 @@ class MossVLVideoProcessor(BaseVideoProcessor):
|
|
| 724 |
# Sample uniformly within [start, end), endpoint=False for left-closed right-open
|
| 725 |
actual_timestamps = np.linspace(start_time, end_time, target_frames, endpoint=False).tolist()
|
| 726 |
|
|
|
|
|
|
|
| 727 |
# Use multithreading for extraction
|
| 728 |
result = timestamp_decode_with_multithreading(actual_timestamps, self.num_extract_threads, video_path)
|
| 729 |
video_tensor = result["data"]
|
|
@@ -1129,4 +1158,3 @@ class MossVLVideoProcessor(BaseVideoProcessor):
|
|
| 1129 |
|
| 1130 |
|
| 1131 |
__all__ = ["MossVLVideoProcessor"]
|
| 1132 |
-
|
|
|
|
| 39 |
logger = logging.get_logger(__name__)
|
| 40 |
|
| 41 |
|
| 42 |
+
TORCHCODEC_TIMESTAMP_EPSILON = 1e-6
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def clamp_timestamps_for_torchcodec(timestamps: List[float], torchcodec_metadata) -> List[float]:
|
| 46 |
+
if not timestamps:
|
| 47 |
+
return timestamps
|
| 48 |
+
|
| 49 |
+
min_pts = torchcodec_metadata.begin_stream_seconds_from_content
|
| 50 |
+
if min_pts is None:
|
| 51 |
+
min_pts = 0.0
|
| 52 |
+
# TorchCodec can reject timestamps exactly equal to the reported stream
|
| 53 |
+
# begin due to tiny metadata/decoder precision differences.
|
| 54 |
+
safe_min_pts = min_pts + TORCHCODEC_TIMESTAMP_EPSILON
|
| 55 |
+
|
| 56 |
+
max_pts_candidates = []
|
| 57 |
+
if torchcodec_metadata.num_frames_from_content and torchcodec_metadata.average_fps:
|
| 58 |
+
max_pts_candidates.append(
|
| 59 |
+
(torchcodec_metadata.num_frames_from_content - 1) / torchcodec_metadata.average_fps + min_pts
|
| 60 |
+
)
|
| 61 |
+
if torchcodec_metadata.end_stream_seconds_from_content is not None:
|
| 62 |
+
# TorchCodec requires requested PTS to be strictly smaller than the content end.
|
| 63 |
+
max_pts_candidates.append(torchcodec_metadata.end_stream_seconds_from_content - TORCHCODEC_TIMESTAMP_EPSILON)
|
| 64 |
+
if not max_pts_candidates and torchcodec_metadata.duration_seconds is not None:
|
| 65 |
+
max_pts_candidates.append(torchcodec_metadata.duration_seconds - TORCHCODEC_TIMESTAMP_EPSILON)
|
| 66 |
+
|
| 67 |
+
if max_pts_candidates:
|
| 68 |
+
max_pts = max(safe_min_pts, min(max_pts_candidates))
|
| 69 |
+
return [max(safe_min_pts, min(float(t), max_pts)) for t in timestamps]
|
| 70 |
+
if safe_min_pts > 0:
|
| 71 |
+
return [max(safe_min_pts, float(t)) for t in timestamps]
|
| 72 |
+
return [float(t) for t in timestamps]
|
| 73 |
+
|
| 74 |
+
|
| 75 |
# -----------------------------------------------------------------------------
|
| 76 |
# Torchcodec video frame extraction utilities
|
| 77 |
# -----------------------------------------------------------------------------
|
|
|
|
| 231 |
|
| 232 |
Returns:
|
| 233 |
List of index chunks.
|
| 234 |
+
|
| 235 |
+
Raises:
|
| 236 |
+
ValueError: If indices is empty or num_chunks is not positive.
|
| 237 |
"""
|
| 238 |
+
if len(indices) == 0:
|
| 239 |
+
raise ValueError("indices must not be empty")
|
| 240 |
+
if num_chunks <= 0:
|
| 241 |
+
raise ValueError("num_chunks must be positive")
|
| 242 |
+
|
| 243 |
+
# Never create empty decode jobs when there are fewer frames than workers.
|
| 244 |
+
num_chunks = min(num_chunks, len(indices))
|
| 245 |
chunk_size = len(indices) // num_chunks
|
| 246 |
chunks = []
|
| 247 |
for i in range(num_chunks - 1):
|
|
|
|
| 282 |
dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
|
| 283 |
"""
|
| 284 |
chunks = split_indices(indices, num_chunks=num_threads)
|
| 285 |
+
results = Parallel(n_jobs=len(chunks), prefer="threads", verbose=0)(
|
| 286 |
delayed(decode_sequentially)(chunk, video_path) for chunk in chunks
|
| 287 |
)
|
| 288 |
|
|
|
|
| 309 |
try:
|
| 310 |
metadata = decoder.metadata
|
| 311 |
|
| 312 |
+
timestamp_list = clamp_timestamps_for_torchcodec(timestamp_list, metadata)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
|
| 314 |
return decoder.get_frames_played_at(timestamp_list)
|
| 315 |
finally:
|
|
|
|
| 329 |
dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
|
| 330 |
"""
|
| 331 |
chunks = split_indices(timestamp_list, num_chunks=num_threads)
|
| 332 |
+
results = Parallel(n_jobs=len(chunks), prefer="threads", verbose=0)(
|
| 333 |
delayed(decode_sequentially_timestamp)(chunk, video_path) for chunk in chunks
|
| 334 |
)
|
| 335 |
|
|
|
|
| 730 |
|
| 731 |
if len(segment) == 1:
|
| 732 |
# Single frame at specified time
|
| 733 |
+
actual_timestamps = clamp_timestamps_for_torchcodec([segment[0]], torchcodec_metadata)
|
| 734 |
+
frame_batch = decoder.get_frames_played_at(actual_timestamps)
|
| 735 |
video_tensor = frame_batch.data
|
|
|
|
| 736 |
sample_count = 1
|
| 737 |
else:
|
| 738 |
# Segment [start, end) - left-closed, right-open interval
|
|
|
|
| 751 |
# Sample uniformly within [start, end), endpoint=False for left-closed right-open
|
| 752 |
actual_timestamps = np.linspace(start_time, end_time, target_frames, endpoint=False).tolist()
|
| 753 |
|
| 754 |
+
actual_timestamps = clamp_timestamps_for_torchcodec(actual_timestamps, torchcodec_metadata)
|
| 755 |
+
|
| 756 |
# Use multithreading for extraction
|
| 757 |
result = timestamp_decode_with_multithreading(actual_timestamps, self.num_extract_threads, video_path)
|
| 758 |
video_tensor = result["data"]
|
|
|
|
| 1158 |
|
| 1159 |
|
| 1160 |
__all__ = ["MossVLVideoProcessor"]
|
|
|