Spaces:
Running
Running
Upload 4 files
Browse files- Dockerfile +1 -1
- app.py +0 -113
- bot.py +1 -1
- requirements.txt +0 -1
Dockerfile
CHANGED
|
@@ -21,7 +21,7 @@ FROM python:3.10-slim
|
|
| 21 |
|
| 22 |
WORKDIR /app
|
| 23 |
|
| 24 |
-
RUN apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0 wget
|
| 25 |
&& rm -rf /var/lib/apt/lists/*
|
| 26 |
|
| 27 |
COPY --from=tgbotapi-builder /usr/local/bin/telegram-bot-api /usr/local/bin/telegram-bot-api
|
|
|
|
| 21 |
|
| 22 |
WORKDIR /app
|
| 23 |
|
| 24 |
+
RUN apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0 wget \
|
| 25 |
&& rm -rf /var/lib/apt/lists/*
|
| 26 |
|
| 27 |
COPY --from=tgbotapi-builder /usr/local/bin/telegram-bot-api /usr/local/bin/telegram-bot-api
|
app.py
CHANGED
|
@@ -9,7 +9,6 @@ import easyocr
|
|
| 9 |
import torch
|
| 10 |
import requests
|
| 11 |
import vtracer
|
| 12 |
-
import pydiffvg
|
| 13 |
from PIL import Image
|
| 14 |
from fastapi import FastAPI, File, UploadFile, HTTPException, Form
|
| 15 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -21,8 +20,6 @@ from basicsr.archs.rrdbnet_arch import RRDBNet
|
|
| 21 |
from realesrgan import RealESRGANer
|
| 22 |
from gfpgan import GFPGANer
|
| 23 |
|
| 24 |
-
pydiffvg.set_use_gpu(False)
|
| 25 |
-
|
| 26 |
app = FastAPI(title="ZGrafic API")
|
| 27 |
|
| 28 |
# Permite que tu página en GitHub Pages (u otro origen) llame a este servidor.
|
|
@@ -711,113 +708,3 @@ async def vectorize(file: UploadFile = File(...)):
|
|
| 711 |
"X-Vectorize-Type": "ilustracion" if is_flat_illustration else "foto-detallada",
|
| 712 |
},
|
| 713 |
)
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
# ---- /vectorize-ai (experimental: optimización tipo LIVE con pydiffvg) ----
|
| 717 |
-
|
| 718 |
-
def random_path(num_segments, canvas_w, canvas_h, seed_point=None):
|
| 719 |
-
"""Una forma cerrada de curvas Bézier, arrancando en un punto al
|
| 720 |
-
azar (o uno dado) — la optimización después la va a ir moviendo y
|
| 721 |
-
deformando hasta que se parezca a una región real de la imagen."""
|
| 722 |
-
num_control_points = torch.zeros(num_segments, dtype=torch.int32) + 2
|
| 723 |
-
points = []
|
| 724 |
-
p0 = seed_point if seed_point is not None else torch.rand(2) * torch.tensor([canvas_w, canvas_h])
|
| 725 |
-
points.append(p0)
|
| 726 |
-
radius = max(canvas_w, canvas_h) * 0.08
|
| 727 |
-
for _ in range(num_segments):
|
| 728 |
-
p1 = p0 + (torch.rand(2) - 0.5) * radius
|
| 729 |
-
p2 = p1 + (torch.rand(2) - 0.5) * radius
|
| 730 |
-
p3 = p2 + (torch.rand(2) - 0.5) * radius
|
| 731 |
-
points += [p1, p2, p3]
|
| 732 |
-
p0 = p3
|
| 733 |
-
points_t = torch.stack(points)
|
| 734 |
-
return pydiffvg.Path(
|
| 735 |
-
num_control_points=num_control_points,
|
| 736 |
-
points=points_t,
|
| 737 |
-
is_closed=True,
|
| 738 |
-
stroke_width=torch.tensor(0.0),
|
| 739 |
-
)
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
def vectorize_with_live(pil_img, num_paths=80, num_iter=250):
|
| 743 |
-
"""Versión simplificada del enfoque de LIVE: arranca con formas al
|
| 744 |
-
azar y las va ajustando con descenso de gradiente hasta que, todas
|
| 745 |
-
juntas, se parezcan a la imagen original."""
|
| 746 |
-
canvas_w, canvas_h = pil_img.size
|
| 747 |
-
target = torch.from_numpy(np.array(pil_img).astype(np.float32) / 255.0)
|
| 748 |
-
if target.shape[2] == 3:
|
| 749 |
-
alpha = torch.ones((canvas_h, canvas_w, 1))
|
| 750 |
-
target = torch.cat([target, alpha], dim=2)
|
| 751 |
-
|
| 752 |
-
# Colores de partida: tomamos colores reales de la imagen (en vez de
|
| 753 |
-
# colores al azar), para que arranque más cerca del resultado final.
|
| 754 |
-
small = pil_img.copy()
|
| 755 |
-
small.thumbnail((64, 64))
|
| 756 |
-
palette_source = np.array(small).reshape(-1, 3)
|
| 757 |
-
|
| 758 |
-
shapes, shape_groups = [], []
|
| 759 |
-
for i in range(num_paths):
|
| 760 |
-
seed_point = torch.rand(2) * torch.tensor([canvas_w, canvas_h])
|
| 761 |
-
path = random_path(num_segments=3, canvas_w=canvas_w, canvas_h=canvas_h, seed_point=seed_point)
|
| 762 |
-
shapes.append(path)
|
| 763 |
-
|
| 764 |
-
color_rgb = palette_source[np.random.randint(0, len(palette_source))] / 255.0
|
| 765 |
-
fill_color = torch.tensor([color_rgb[0], color_rgb[1], color_rgb[2], 1.0], requires_grad=True)
|
| 766 |
-
group = pydiffvg.ShapeGroup(
|
| 767 |
-
shape_ids=torch.tensor([i]),
|
| 768 |
-
fill_color=fill_color,
|
| 769 |
-
)
|
| 770 |
-
shape_groups.append(group)
|
| 771 |
-
|
| 772 |
-
points_vars = [s.points.requires_grad_() for s in shapes]
|
| 773 |
-
color_vars = [g.fill_color.requires_grad_() for g in shape_groups]
|
| 774 |
-
|
| 775 |
-
points_optim = torch.optim.Adam(points_vars, lr=1.0)
|
| 776 |
-
color_optim = torch.optim.Adam(color_vars, lr=0.02)
|
| 777 |
-
|
| 778 |
-
for _ in range(num_iter):
|
| 779 |
-
points_optim.zero_grad()
|
| 780 |
-
color_optim.zero_grad()
|
| 781 |
-
|
| 782 |
-
scene_args = pydiffvg.RenderFunction.serialize_scene(canvas_w, canvas_h, shapes, shape_groups)
|
| 783 |
-
img = pydiffvg.RenderFunction.apply(canvas_w, canvas_h, 2, 2, 0, None, *scene_args)
|
| 784 |
-
|
| 785 |
-
loss = (img - target).pow(2).mean()
|
| 786 |
-
loss.backward()
|
| 787 |
-
|
| 788 |
-
points_optim.step()
|
| 789 |
-
color_optim.step()
|
| 790 |
-
|
| 791 |
-
for g in shape_groups:
|
| 792 |
-
g.fill_color.data.clamp_(0.0, 1.0)
|
| 793 |
-
|
| 794 |
-
svg_path = tempfile.NamedTemporaryFile(suffix=".svg", delete=False).name
|
| 795 |
-
pydiffvg.save_svg(svg_path, canvas_w, canvas_h, shapes, shape_groups)
|
| 796 |
-
with open(svg_path, "r") as f:
|
| 797 |
-
return f.read()
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
@app.post("/vectorize-ai")
|
| 801 |
-
async def vectorize_ai(file: UploadFile = File(...)):
|
| 802 |
-
if not file.content_type or not file.content_type.startswith("image/"):
|
| 803 |
-
raise HTTPException(status_code=400, detail="El archivo debe ser una imagen.")
|
| 804 |
-
|
| 805 |
-
input_bytes = await file.read()
|
| 806 |
-
|
| 807 |
-
try:
|
| 808 |
-
pil_img = Image.open(io.BytesIO(input_bytes)).convert("RGB")
|
| 809 |
-
|
| 810 |
-
# Achicamos bastante: esto optimiza pixel por pixel en CPU, una
|
| 811 |
-
# imagen grande sería demasiado lenta.
|
| 812 |
-
MAX_SIDE = 200
|
| 813 |
-
w, h = pil_img.size
|
| 814 |
-
if max(w, h) > MAX_SIDE:
|
| 815 |
-
ratio = MAX_SIDE / max(w, h)
|
| 816 |
-
pil_img = pil_img.resize((int(w * ratio), int(h * ratio)), Image.LANCZOS)
|
| 817 |
-
|
| 818 |
-
svg_content = vectorize_with_live(pil_img)
|
| 819 |
-
|
| 820 |
-
except Exception as e:
|
| 821 |
-
raise HTTPException(status_code=500, detail=f"No se pudo vectorizar la imagen: {e}")
|
| 822 |
-
|
| 823 |
-
return Response(content=svg_content, media_type="image/svg+xml")
|
|
|
|
| 9 |
import torch
|
| 10 |
import requests
|
| 11 |
import vtracer
|
|
|
|
| 12 |
from PIL import Image
|
| 13 |
from fastapi import FastAPI, File, UploadFile, HTTPException, Form
|
| 14 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 20 |
from realesrgan import RealESRGANer
|
| 21 |
from gfpgan import GFPGANer
|
| 22 |
|
|
|
|
|
|
|
| 23 |
app = FastAPI(title="ZGrafic API")
|
| 24 |
|
| 25 |
# Permite que tu página en GitHub Pages (u otro origen) llame a este servidor.
|
|
|
|
| 708 |
"X-Vectorize-Type": "ilustracion" if is_flat_illustration else "foto-detallada",
|
| 709 |
},
|
| 710 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bot.py
CHANGED
|
@@ -27,7 +27,7 @@ BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
|
|
| 27 |
REMOVE_BG_URL = "https://zgrafic-remove-bg-api.hf.space/remove-bg"
|
| 28 |
ENHANCE_URL = "https://zgrafic-remove-bg-api.hf.space/enhance"
|
| 29 |
VECTORIZE_URL = "https://zgrafic-remove-bg-api.hf.space/vectorize"
|
| 30 |
-
VECTORIZE_AI_URL = "https://zgrafic-
|
| 31 |
|
| 32 |
FREE_USES_PER_DAY = 10
|
| 33 |
STARS_PER_IMAGE = 10
|
|
|
|
| 27 |
REMOVE_BG_URL = "https://zgrafic-remove-bg-api.hf.space/remove-bg"
|
| 28 |
ENHANCE_URL = "https://zgrafic-remove-bg-api.hf.space/enhance"
|
| 29 |
VECTORIZE_URL = "https://zgrafic-remove-bg-api.hf.space/vectorize"
|
| 30 |
+
VECTORIZE_AI_URL = "https://zgrafic-enhance-photo-api.hf.space/vectorize-ai"
|
| 31 |
|
| 32 |
FREE_USES_PER_DAY = 10
|
| 33 |
STARS_PER_IMAGE = 10
|
requirements.txt
CHANGED
|
@@ -18,4 +18,3 @@ realesrgan==0.3.0
|
|
| 18 |
gfpgan==1.3.8
|
| 19 |
firebase-admin==6.5.0
|
| 20 |
vtracer==0.6.15
|
| 21 |
-
git+https://github.com/ANaka/easydiffvg.git
|
|
|
|
| 18 |
gfpgan==1.3.8
|
| 19 |
firebase-admin==6.5.0
|
| 20 |
vtracer==0.6.15
|
|
|