Solaria1.0 / app.py
CronosSa's picture
Update app.py
e50141b verified
Raw History Blame Contribute Delete
12.5 kB
import gradio as gr
import spaces
import torch
import tempfile
import numpy as np
from PIL import Image
from marigoldv2_inference import (
BASE_MODEL_URI,
MODEL_URI,
MarigoldV2,
)
from segmentation import (
gerar_mascara,
)
from pointcloud import (
depth_to_pointcloud,
save_pointcloud_ply,
)
# =====================================================
# CONFIGURAÇÃO
# =====================================================
model = None
# =====================================================
# CARREGAR MARIGOLD V2
# =====================================================
def carregar_modelo():
global model
if model is None:
device = torch.device(
"cuda"
if torch.cuda.is_available()
else "cpu"
)
print(
f"Carregando Marigold V2 em {device}...",
flush=True
)
model = MarigoldV2(
BASE_MODEL_URI,
MODEL_URI,
device
)
print(
"Marigold V2 carregado.",
flush=True
)
return model
# =====================================================
# ETAPA 1 — SEGMENTAÇÃO
# =====================================================
def preparar_mascara(imagem):
if imagem is None:
return (
None,
"Envie uma imagem."
)
try:
print(
"Iniciando segmentação...",
flush=True
)
mascara = gerar_mascara(
imagem
)
# -------------------------------------------------
# GARANTE QUE A MÁSCARA SEJA PIL
# -------------------------------------------------
if not isinstance(
mascara,
Image.Image
):
mascara = Image.fromarray(
np.asarray(mascara)
)
mascara = mascara.convert("L")
print(
"Segmentação concluída.",
flush=True
)
return (
mascara,
"Objeto identificado com sucesso."
)
except Exception as e:
print(
f"ERRO NA SEGMENTAÇÃO: {e}",
flush=True
)
return (
None,
f"Erro na segmentação:\n{str(e)}"
)
# =====================================================
# RECORTAR OBJETO
# =====================================================
def criar_imagem_do_objeto(
imagem,
mascara
):
# -------------------------------------------------
# GARANTE QUE A IMAGEM É PIL
# -------------------------------------------------
if not isinstance(
imagem,
Image.Image
):
imagem = Image.fromarray(
np.asarray(imagem)
)
imagem = imagem.convert("RGB")
# -------------------------------------------------
# GARANTE QUE A MÁSCARA É PIL
# -------------------------------------------------
if not isinstance(
mascara,
Image.Image
):
mascara = Image.fromarray(
np.asarray(mascara)
)
mascara = mascara.convert("L")
# -------------------------------------------------
# MESMO TAMANHO
# -------------------------------------------------
mascara = mascara.resize(
imagem.size,
Image.Resampling.LANCZOS
)
# -------------------------------------------------
# LOCALIZA O OBJETO
# -------------------------------------------------
bbox = mascara.getbbox()
if bbox is None:
raise ValueError(
"Não foi possível encontrar "
"o objeto na máscara."
)
# -------------------------------------------------
# TAMANHO DA IMAGEM
# -------------------------------------------------
largura, altura = imagem.size
# -------------------------------------------------
# MARGEM AO REDOR DO OBJETO
# -------------------------------------------------
margem_x = int(
largura * 0.03
)
margem_y = int(
altura * 0.03
)
x1 = max(
0,
bbox[0] - margem_x
)
y1 = max(
0,
bbox[1] - margem_y
)
x2 = min(
largura,
bbox[2] + margem_x
)
y2 = min(
altura,
bbox[3] + margem_y
)
# -------------------------------------------------
# RECORTA
# -------------------------------------------------
imagem_cortada = imagem.crop(
(
x1,
y1,
x2,
y2
)
)
mascara_cortada = mascara.crop(
(
x1,
y1,
x2,
y2
)
)
# -------------------------------------------------
# FUNDO PRETO
# -------------------------------------------------
fundo = Image.new(
"RGB",
imagem_cortada.size,
(0, 0, 0)
)
# -------------------------------------------------
# MANTÉM SOMENTE O OBJETO
# -------------------------------------------------
objeto = Image.composite(
imagem_cortada,
fundo,
mascara_cortada
)
return (
objeto,
mascara_cortada
)
# =====================================================
# ETAPA 2 — MARIGOLD + POINT CLOUD
# =====================================================
@spaces.GPU(duration=180)
def gerar_3d(
imagem,
mascara
):
if imagem is None:
return (
None,
None,
None,
"Envie uma imagem."
)
if mascara is None:
return (
None,
None,
None,
"A máscara do objeto não foi gerada."
)
try:
# =================================================
# 1 — PREPARAR OBJETO
# =================================================
print(
"Preparando objeto...",
flush=True
)
objeto, mascara_objeto = (
criar_imagem_do_objeto(
imagem,
mascara
)
)
print(
f"Objeto preparado: {objeto.size}",
flush=True
)
# =================================================
# 2 — CARREGAR MODELO
# =================================================
print(
"Carregando Marigold V2...",
flush=True
)
modelo = carregar_modelo()
# =================================================
# 3 — PROFUNDIDADE
# =================================================
print(
"Calculando profundidade do objeto...",
flush=True
)
resultados = modelo(
objeto
)
print(
"Resultado do Marigold recebido.",
flush=True
)
# =================================================
# 4 — DEPTH VISUAL
# =================================================
depth_image = resultados.get(
"Depth"
)
# =================================================
# 5 — RAW DEPTH
# =================================================
raw_depth = resultados.get(
"raw_depth"
)
if raw_depth is None:
raise ValueError(
"O Marigold não retornou "
"'raw_depth'."
)
print(
"DEBUG raw_depth:",
type(raw_depth),
getattr(
raw_depth,
"shape",
None
),
flush=True
)
print(
"DEBUG mascara:",
type(mascara_objeto),
mascara_objeto.size,
flush=True
)
# =================================================
# 6 — NUVEM DE PONTOS
# =================================================
print(
"Gerando nuvem de pontos...",
flush=True
)
points, colors = (
depth_to_pointcloud(
raw_depth,
objeto,
mask=mascara_objeto,
stride=4
)
)
print(
f"Nuvem criada: {len(points)} pontos.",
flush=True
)
# =================================================
# 7 — SALVAR PLY
# =================================================
arquivo_ply = tempfile.NamedTemporaryFile(
suffix=".ply",
delete=False
)
arquivo_ply.close()
save_pointcloud_ply(
arquivo_ply.name,
points,
colors
)
print(
f"PLY salvo em: {arquivo_ply.name}",
flush=True
)
# =================================================
# 8 — STATUS
# =================================================
status = (
"Processamento concluído!\n\n"
f"Tamanho do objeto: {objeto.size}\n"
f"Pontos 3D: {len(points):,}\n\n"
"Fluxo utilizado:\n"
"✓ Segmentação\n"
"✓ Recorte do objeto\n"
"✓ Marigold V2\n"
"✓ Máscara aplicada\n"
"✓ Nuvem de pontos"
)
return (
mascara_objeto,
depth_image,
arquivo_ply.name,
status
)
except Exception as e:
print(
"ERRO NA GERAÇÃO 3D:",
repr(e),
flush=True
)
return (
None,
None,
None,
f"Erro durante a geração 3D:\n\n{str(e)}"
)
# =====================================================
# INTERFACE
# =====================================================
with gr.Blocks(
title="Solaria 1.0 - 2D para 3D"
) as app:
gr.Markdown(
"""
# 🚀 Solaria 1.0
## Imagem 2D → Objeto → Profundidade → 3D
O Solaria identifica o objeto principal,
remove o fundo, calcula a profundidade
com Marigold V2 e cria uma nuvem de pontos
somente do objeto.
"""
)
# =================================================
# IMAGEM + MÁSCARA
# =================================================
with gr.Row():
with gr.Column():
imagem_input = gr.Image(
type="pil",
label="Imagem 2D"
)
with gr.Column():
mascara_output = gr.Image(
label="Máscara do objeto",
type="pil"
)
# =================================================
# BOTÃO
# =================================================
botao = gr.Button(
"Gerar 3D",
variant="primary"
)
# =================================================
# PROFUNDIDADE
# =================================================
depth_output = gr.Image(
label="Mapa de profundidade"
)
# =================================================
# VISUALIZAÇÃO 3D
# =================================================
gr.Markdown(
"""
## Visualização 3D
"""
)
modelo_3d = gr.Model3D(
label="Nuvem de pontos 3D",
display_mode="point_cloud"
)
# =================================================
# STATUS
# =================================================
resultado = gr.Textbox(
label="Status",
lines=8
)
# =================================================
# FLUXO
# =================================================
etapa_mascara = botao.click(
fn=preparar_mascara,
inputs=imagem_input,
outputs=[
mascara_output,
resultado
]
)
etapa_mascara.then(
fn=gerar_3d,
inputs=[
imagem_input,
mascara_output
],
outputs=[
mascara_output,
depth_output,
modelo_3d,
resultado
]
)
# =====================================================
# INICIAR
# =====================================================
app.queue().launch()