adilkh26's picture
Update app.py
9409115 verified
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
@spaces.GPU
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)