Files
2025-05-06 11:05:30 +08:00

176 lines
5.5 KiB
Python

from transformers import (
Qwen2_5_VLForConditionalGeneration,
AutoProcessor
)
# import torch
from pathlib import Path
import folder_paths
import os
# from .logger import logger
from .utils import calculate_file_hash, strip_path
# .\python_embeded\python.exe -m pip install qwen_vl_utils
from qwen_vl_utils import process_vision_info
# https://huggingface.co/OpenGVLab/VideoChat-R1_7B/tree/main
# https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite
class ModelLoader:
def __init__(self):
self.model_checkpoint = None
self.model = None
self.processor = None
# self.device = (
# torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
# )
# self.bf16_support = (
# torch.cuda.is_available()
# and torch.cuda.get_device_capability(self.device)[0] >= 8
# )
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": (["VideoChat-R1_7B", ],{"default": "VideoChat-R1_7B"},),
}
}
RETURN_TYPES = ("MODEL", "PROCESSOR")
RETURN_NAMES = ("model", "processor")
FUNCTION = "load_model"
CATEGORY = "VideoChatWrapper"
def load_model(self, model):
model_id = f"OpenGVLab/{model}"
# put downloaded model to model/videochat dir
self.model_checkpoint = os.path.join(
folder_paths.models_dir, "videochat", os.path.basename(model_id)
)
if not os.path.exists(self.model_checkpoint):
from huggingface_hub import snapshot_download
snapshot_download(
repo_id=model_id,
local_dir=self.model_checkpoint,
local_dir_use_symlinks=False,
)
if self.model is None:
self.model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
self.model_checkpoint,
torch_dtype="auto",
device_map="auto",
# attn_implementation="flash_attention_2"
)
self.processor = AutoProcessor.from_pretrained(self.model_checkpoint)
return self.model, self.processor
class VideoSummary:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("MODEL",),
"processor": ("PROCESSOR",),
"video_path": ("STRING", ),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "video_summary"
CATEGORY = "VideoChatWrapper"
def video_summary(self, model, processor, video_path):
question = "Describe this video in detail."
messages = [
{
"role": "user",
"content": [
{
"type": "video",
"video": video_path,
"max_pixels": 360 * 420,
"fps": 1.0,
},
{"type": "text", "text": f"""{question}
"""},
],
}
]
#In Qwen 2.5 VL, frame rate information is also input into the model to align with absolute time.
# Preparation for inference
text = processor.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
image_inputs, video_inputs, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
inputs = processor(
text=[text],
images=image_inputs,
videos=video_inputs,
padding=True,
return_tensors="pt",
**video_kwargs,
)
inputs = inputs.to("cuda")
# Inference
generated_ids = model.generate(**inputs, max_new_tokens=512)
generated_ids_trimmed = [
out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
return (output_text,)
video_extensions = ['webm', 'mp4', 'mkv', 'mov']
def load_video(**kwargs):
kwargs['video'] = strip_path(kwargs['video'])
# print("video_path: ", kwargs['video'])
return (kwargs['video'],)
class LoadVideoUpload:
@classmethod
def INPUT_TYPES(s):
input_dir = folder_paths.get_input_directory()
files = []
for f in os.listdir(input_dir):
if os.path.isfile(os.path.join(input_dir, f)):
file_parts = f.split('.')
if len(file_parts) > 1 and (file_parts[-1].lower() in video_extensions):
files.append(f)
return {"required": {
"video": (sorted(files),),
},
}
CATEGORY = "VideoChatWrapper"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("video_path",)
FUNCTION = "load_video"
def load_video(self, **kwargs):
kwargs['video'] = folder_paths.get_annotated_filepath(strip_path(kwargs['video']))
return load_video(**kwargs)
@classmethod
def IS_CHANGED(s, video, **kwargs):
image_path = folder_paths.get_annotated_filepath(video)
return calculate_file_hash(image_path)
@classmethod
def VALIDATE_INPUTS(s, video):
if not folder_paths.exists_annotated_filepath(video):
return "Invalid video file: {}".format(video)
return True