Spaces:
Runtime error
Runtime error
import gradio as gr | |
from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration, TextIteratorStreamer | |
from transformers.image_utils import load_image | |
from threading import Thread | |
import time | |
import torch | |
import spaces | |
import cv2 | |
from pathlib import Path | |
from PIL import Image | |
import concurrent.futures | |
MODEL_ID = "Qwen/Qwen2.5-VL-7B-Instruct" # или "Qwen/Qwen2.5-VL-3B-Instruct" | |
processor = AutoProcessor.from_pretrained(MODEL_ID, trust_remote_code=True) | |
model = Qwen2_5_VLForConditionalGeneration.from_pretrained( | |
MODEL_ID, | |
trust_remote_code=True, | |
torch_dtype=torch.bfloat16 | |
).to("cuda").eval() | |
def extract_frame_at(video_path, frame_index): | |
""" | |
Извлекает кадр по указанному индексу. | |
""" | |
cap = cv2.VideoCapture(video_path) | |
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index) | |
ret, frame = cap.read() | |
cap.release() | |
if ret: | |
# Преобразуем BGR в RGB и возвращаем как PIL Image | |
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
return Image.fromarray(frame) | |
else: | |
return None | |
def extract_frames_parallel(video_path, interval=2.0): | |
""" | |
Извлекает кадры из видео с интервалом в секундах, выполняя запросы параллельно. | |
""" | |
cap = cv2.VideoCapture(video_path) | |
fps = cap.get(cv2.CAP_PROP_FPS) | |
if fps == 0: | |
fps = 25 # запасное значение | |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
cap.release() | |
frame_interval = int(fps * interval) | |
# Вычисляем номера кадров для извлечения | |
frame_indices = list(range(0, total_frames, frame_interval)) | |
frames = [] | |
# Параллельное извлечение кадров | |
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: | |
results = executor.map(lambda idx: extract_frame_at(video_path, idx), frame_indices) | |
for frame in results: | |
if frame is not None: | |
frames.append(frame) | |
return frames | |
def model_inference(input_dict, history): | |
text = input_dict["text"] | |
files = input_dict["files"] | |
images = [] | |
video_extensions = [".mp4", ".avi", ".mov", ".mkv"] | |
if files: | |
for file in files: | |
ext = Path(file).suffix.lower() | |
if ext in video_extensions: | |
try: | |
# Используем параллельное извлечение кадров с интервалом 2 секунды | |
frames = extract_frames_parallel(file, interval=2.0) | |
if frames: | |
images.extend(frames) | |
else: | |
gr.Error("Не удалось извлечь кадры из видео.") | |
return | |
except Exception as e: | |
gr.Error(f"Ошибка при обработке видеофайла: {e}") | |
return | |
else: | |
images.append(load_image(file)) | |
# Проверка входных данных | |
if text == "" and not images: | |
gr.Error("Пожалуйста, введите запрос и, опционально, прикрепите изображение/видео.") | |
return | |
if text == "" and images: | |
gr.Error("Пожалуйста, введите текстовый запрос вместе с изображением/видео.") | |
return | |
# Подготовка сообщений для модели | |
messages = [ | |
{ | |
"role": "user", | |
"content": [ | |
*[{"type": "image", "image": image} for image in images], | |
{"type": "text", "text": text}, | |
], | |
} | |
] | |
# Применяем шаблон чата и подготавливаем входные данные | |
prompt = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
inputs = processor( | |
text=[prompt], | |
images=images if images else None, | |
return_tensors="pt", | |
padding=True, | |
).to("cuda") | |
# Настраиваем стриминг вывода в реальном времени | |
streamer = TextIteratorStreamer(processor, skip_prompt=True, skip_special_tokens=True) | |
generation_kwargs = dict(inputs, streamer=streamer, max_new_tokens=1024) | |
# Запускаем генерацию в отдельном потоке | |
thread = Thread(target=model.generate, kwargs=generation_kwargs) | |
thread.start() | |
# Стримим вывод | |
buffer = "" | |
yield "Думаю..." | |
for new_text in streamer: | |
buffer += new_text | |
time.sleep(0.01) | |
yield buffer | |
# Примеры входных данных | |
examples = [ | |
[{"text": "Опиши документ?", "files": ["example_images/document.jpg"]}], | |
[{"text": "Что написано на изображении?", "files": ["example_images/math.jpg"]}], | |
[{"text": "О чем этот UI?", "files": ["example_images/s2w_example.png"]}], | |
[{"text": "Где происходят сильные засухи по диаграмме?", "files": ["example_images/examples_weather_events.png"]}], | |
# Пример с видео (убедитесь, что файл существует) | |
# [{"text": "Найди нужный объект в видео.", "files": ["example_videos/sample.mp4"]}], | |
] | |
demo = gr.ChatInterface( | |
fn=model_inference, | |
description="# **Qwen2.5-VL-7B-Instruct**\nТеперь видео обрабатываются параллельно для ускорения извлечения кадров.", | |
examples=examples, | |
textbox=gr.MultimodalTextbox(label="Запрос (текст + изображение/видео)", file_types=["image", "video"], file_count="multiple"), | |
stop_btn="Остановить генерацию", | |
multimodal=True, | |
cache_examples=False, | |
) | |
demo.launch(debug=True) | |