Spaces:
Runtime error
Runtime error
Commit
·
29a9d45
1
Parent(s):
690d40a
Atualiza processamento de imagens e simplifica código
Browse filesMelhorou o processo de manipulação de imagens ao permitir a entrada através de URL, base64 ou caminho local de forma mais eficiente. Removeu parâmetros de configuração desnecessários para o processador, facilitando a inicialização. A validação de mensagens foi aprimorada para garantir que apenas formatos válidos sejam aceitos, evitando erros. Essa refatoração torna o código mais limpo e robusto, melhorando a manutenabilidade e a legibilidade.
app.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
from fastapi import FastAPI, Body
|
2 |
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
|
3 |
-
from qwen_vl_utils import process_vision_info
|
4 |
import torch
|
5 |
from typing import List, Dict, Union
|
6 |
import base64
|
@@ -10,13 +9,13 @@ from io import BytesIO
|
|
10 |
|
11 |
app = FastAPI()
|
12 |
|
|
|
13 |
model = Qwen2VLForConditionalGeneration.from_pretrained("Qwen/Qwen2-VL-2B-Instruct", torch_dtype="auto", device_map="auto")
|
14 |
-
|
15 |
-
max_pixels = 1280 * 28 * 28
|
16 |
-
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-2B-Instruct", min_pixels=min_pixels, max_pixels=max_pixels)
|
17 |
|
18 |
def process_image(image_data: str) -> Image.Image:
|
19 |
-
|
|
|
20 |
response = requests.get(image_data)
|
21 |
response.raise_for_status()
|
22 |
img = Image.open(BytesIO(response.content))
|
@@ -31,44 +30,37 @@ def process_image(image_data: str) -> Image.Image:
|
|
31 |
async def predict(messages: List[Dict[str, Union[str, List[Dict[str, str]]]]] = Body(...)):
|
32 |
texts = []
|
33 |
image_inputs = []
|
34 |
-
video_inputs = []
|
35 |
|
|
|
36 |
for message in messages:
|
37 |
content = message.get("content")
|
38 |
if isinstance(content, str):
|
39 |
texts.append(processor.apply_chat_template(content, tokenize=False, add_generation_prompt=True))
|
40 |
elif isinstance(content, list):
|
41 |
for item in content:
|
42 |
-
if
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
image_inputs.append(image)
|
48 |
else:
|
49 |
raise ValueError(f"Formato inválido para o item: {item}")
|
50 |
else:
|
51 |
raise ValueError(f"Formato inválido para o conteúdo: {content}")
|
52 |
|
53 |
-
|
54 |
-
raise ValueError("Nenhuma imagem fornecida para processamento.")
|
55 |
-
|
56 |
-
print(f"Imagens processadas: {image_inputs}")
|
57 |
-
|
58 |
inputs = processor(
|
59 |
text=texts,
|
60 |
-
images=
|
61 |
-
videos=video_inputs,
|
62 |
padding=True,
|
63 |
return_tensors="pt"
|
64 |
-
)
|
65 |
-
inputs = inputs.to("cpu")
|
66 |
|
|
|
67 |
generated_ids = model.generate(**inputs, max_new_tokens=128)
|
68 |
-
generated_ids_trimmed = [
|
69 |
-
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
|
70 |
-
]
|
71 |
output_texts = processor.batch_decode(
|
72 |
-
|
|
|
|
|
73 |
)
|
74 |
-
return {"response": output_texts}
|
|
|
1 |
from fastapi import FastAPI, Body
|
2 |
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
|
|
|
3 |
import torch
|
4 |
from typing import List, Dict, Union
|
5 |
import base64
|
|
|
9 |
|
10 |
app = FastAPI()
|
11 |
|
12 |
+
# Carrega o modelo e o processor
|
13 |
model = Qwen2VLForConditionalGeneration.from_pretrained("Qwen/Qwen2-VL-2B-Instruct", torch_dtype="auto", device_map="auto")
|
14 |
+
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-2B-Instruct")
|
|
|
|
|
15 |
|
16 |
def process_image(image_data: str) -> Image.Image:
|
17 |
+
"""Processa uma imagem a partir de URL, base64 ou caminho local."""
|
18 |
+
if image_data.startswith(("http://", "https://")):
|
19 |
response = requests.get(image_data)
|
20 |
response.raise_for_status()
|
21 |
img = Image.open(BytesIO(response.content))
|
|
|
30 |
async def predict(messages: List[Dict[str, Union[str, List[Dict[str, str]]]]] = Body(...)):
|
31 |
texts = []
|
32 |
image_inputs = []
|
|
|
33 |
|
34 |
+
# Processa as mensagens recebidas
|
35 |
for message in messages:
|
36 |
content = message.get("content")
|
37 |
if isinstance(content, str):
|
38 |
texts.append(processor.apply_chat_template(content, tokenize=False, add_generation_prompt=True))
|
39 |
elif isinstance(content, list):
|
40 |
for item in content:
|
41 |
+
if item.get("type") == "text":
|
42 |
+
texts.append(processor.apply_chat_template(item["text"], tokenize=False, add_generation_prompt=True))
|
43 |
+
elif item.get("type") == "image":
|
44 |
+
image = process_image(item["image"])
|
45 |
+
image_inputs.append(image)
|
|
|
46 |
else:
|
47 |
raise ValueError(f"Formato inválido para o item: {item}")
|
48 |
else:
|
49 |
raise ValueError(f"Formato inválido para o conteúdo: {content}")
|
50 |
|
51 |
+
# Prepara inputs para o modelo
|
|
|
|
|
|
|
|
|
52 |
inputs = processor(
|
53 |
text=texts,
|
54 |
+
images=image_inputs if image_inputs else None, # Passa as imagens se houver
|
|
|
55 |
padding=True,
|
56 |
return_tensors="pt"
|
57 |
+
).to("cpu")
|
|
|
58 |
|
59 |
+
# Gera as respostas
|
60 |
generated_ids = model.generate(**inputs, max_new_tokens=128)
|
|
|
|
|
|
|
61 |
output_texts = processor.batch_decode(
|
62 |
+
generated_ids[:, inputs.input_ids.shape[-1]:],
|
63 |
+
skip_special_tokens=True,
|
64 |
+
clean_up_tokenization_spaces=False
|
65 |
)
|
66 |
+
return {"response": output_texts}
|