3cf8b21fea
Completa la promoción del flujo imagen->3D al registry (grupo de capacidad img-to-3d), extraído de la app img_to_3d_webapp. - remove_background_py_datascience (nueva): elimina el fondo con cascada rembg/U2Net -> OpenCV GrabCut -> umbral NumPy, compone el objeto sobre gris neutro y devuelve image + mask + engine. Impura, nunca lanza. Adaptada de backend/bg_removal.py con firma de ruta (image_path) y salida dict, demo CLI JSON-serializable. - depth_to_relief_glb_py_datascience (v1.1.0): añade el parámetro opcional mask para recortar la malla de relieve al objeto (descarta las caras del fondo), cerrando la cadena con remove_background. Aditivo (mask=None = comportamiento previo), fiel al original de backend/depth.py. - docs/capabilities/img-to-3d.md: incorpora remove_background como paso 0 (pre-proceso), actualiza el flujo a 3 pasos encadenados, la tabla de funciones (4), el ejemplo end-to-end con mask y las deps (rembg/opencv). - docs/capabilities/INDEX.md: conteo del grupo 3 -> 4. Las dos funciones ya presentes (estimate_image_depth, depth_to_relief_glb) y el pipeline build_relief_glb_from_image fueron promovidas en una ronda previa. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
5.8 KiB
Markdown
90 lines
5.8 KiB
Markdown
---
|
|
name: remove_background
|
|
kind: function
|
|
lang: py
|
|
domain: datascience
|
|
version: "1.0.0"
|
|
purity: impure
|
|
signature: "def remove_background(image_path: str, engine: str = 'auto') -> dict"
|
|
description: "Elimina el fondo de una imagen con cascada de motores (rembg/U2Net -> OpenCV GrabCut -> umbral NumPy), compone el objeto sobre fondo gris neutro y devuelve image+mask+engine. Paso de pre-proceso del flujo img->3D (grupo img-to-3d): su mask alimenta depth_to_relief_glb para recortar la malla de relieve al objeto."
|
|
tags: [img-to-3d, datascience, background-removal, segmentation, rembg, grabcut, opencv, computer-vision, mask]
|
|
uses_functions: []
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: "error_go_core"
|
|
imports: []
|
|
params:
|
|
- name: image_path
|
|
desc: "Ruta a la imagen de entrada. Cualquier formato que PIL.Image.open abra (jpg, png, webp, RGBA...). Si no existe o no es imagen valida, se devuelve status error. Un PNG RGBA ya recortado se reaprovecha en modo auto (passthrough:alpha)."
|
|
- name: engine
|
|
desc: "Motor de segmentacion. 'auto' (default) prueba en cascada rembg:u2net -> opencv:grabcut -> threshold:border y NUNCA falla (cae al umbral NumPy puro sin deps externas). Forzar uno: 'rembg' (red neuronal U2Net, mejor calidad, deps pesadas), 'grabcut' (OpenCV, rectangulo central), 'threshold' (distancia al color medio de los bordes, NumPy puro, objeto centrado). Si se fuerza un motor y no esta disponible/falla o produce mascara degenerada -> status error."
|
|
output: "dict. Exito: {status:'ok', image: PIL.Image RGB del objeto compuesto sobre fondo gris neutro (127,127,127), mask: ndarray HxW uint8 (0..255, 255=objeto), engine: str del motor usado ('rembg:u2net' | 'opencv:grabcut' | 'threshold:border' | 'passthrough:alpha'), height:int, width:int, fg_fraction: float (fraccion de pixeles objeto, redondeada a 4 decimales)}. Error: {status:'error', error:str} (ruta invalida, motor desconocido, motor forzado no disponible/fallido, o ningun motor produjo una mascara valida). No lanza nunca. El demo CLI (__main__) imprime un resumen JSON sin el ndarray ni la imagen y, si se pasa out_dir, guarda rgb.png + mask.png."
|
|
tested: false
|
|
tests: []
|
|
test_file_path: ""
|
|
file_path: "python/functions/datascience/remove_background.py"
|
|
source_file: "apps/img_to_3d_webapp/backend/bg_removal.py"
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```python
|
|
# Requiere un venv con pillow + numpy (rembg/opencv solo si fuerzas esos motores; el umbral es NumPy puro).
|
|
# Import PLANO al modulo: el paquete datascience.__init__ arrastra deps de otros dominios
|
|
# (bs4, duckdb...) que no estan en ese venv. Ver Gotchas.
|
|
import sys
|
|
sys.path.insert(0, "python/functions/datascience")
|
|
from remove_background import remove_background
|
|
|
|
res = remove_background("apps/img_to_3d_webapp/samples/cats.jpg", engine="auto")
|
|
assert res["status"] == "ok"
|
|
print(res["engine"]) # p.ej. "rembg:u2net" (o "opencv:grabcut" / "threshold:border")
|
|
print(res["height"], res["width"]) # p.ej. 1024 768
|
|
print(res["mask"].shape, res["mask"].dtype) # (1024, 768) uint8 (255=objeto)
|
|
assert 0.0 < res["fg_fraction"] < 1.0
|
|
# res["mask"] (ndarray HxW uint8) alimenta depth_to_relief_glb para recortar la malla al objeto.
|
|
# res["image"] es el objeto compuesto sobre gris neutro, listo para estimar profundidad.
|
|
```
|
|
|
|
Lanzable como demo (imprime resumen JSON, sin serializar el ndarray; guarda PNGs si das out_dir):
|
|
|
|
```bash
|
|
./fn run remove_background_py_datascience apps/img_to_3d_webapp/samples/cats.jpg auto /tmp/cut
|
|
# {"status": "ok", "engine": "rembg:u2net", "height": 1024, "width": 768,
|
|
# "fg_fraction": 0.4123, "rgb_path": "/tmp/cut/rgb.png", "mask_path": "/tmp/cut/mask.png"}
|
|
```
|
|
|
|
## Cuando usarla
|
|
|
|
Como pre-proceso ANTES de estimar profundidad en el flujo img->3D: aislar el objeto evita que el
|
|
modelo de profundidad estire el fondo plano, y la `mask` permite recortar la malla de relieve al
|
|
objeto (se pasa a `depth_to_relief_glb`). Tambien para segmentacion de primer plano generica
|
|
cuando necesitas separar un objeto de su fondo y componerlo sobre un color neutro (recortes para
|
|
catalogos, datasets, miniaturas).
|
|
|
|
## Gotchas
|
|
|
|
- **Impura**: segun el motor carga modelos neuronales y lee disco. `rembg`/`onnxruntime` (~170MB)
|
|
DESCARGA el modelo U2Net la primera vez a su cache (`~/.u2net/`), requiere red en esa primera
|
|
carga; `opencv-python` para GrabCut; el umbral (`threshold:border`) es NumPy puro sin deps externas.
|
|
- **Estado de proceso**: `_REMBG_SESSION` cachea la sesion rembg a nivel de modulo para no recargar
|
|
los pesos en cada llamada. Es estado mutable compartido del proceso y ocupa RAM hasta que el
|
|
interprete muere.
|
|
- **engine='auto' nunca lanza**: prueba rembg -> grabcut -> threshold y siempre cae al umbral NumPy
|
|
puro si los anteriores no estan disponibles o fallan. Forzar un motor concreto SI puede devolver
|
|
status error (motor no instalado, fallo, o mascara degenerada).
|
|
- **Mascara degenerada**: si la fraccion de objeto resulta `< 0.01` o `> 0.995` la mascara se
|
|
descarta (casi todo fondo o casi todo objeto) y en modo auto se prueba el siguiente motor.
|
|
- **threshold:border es de baja calidad**: asume objeto centrado con los bordes de la imagen siendo
|
|
fondo (calcula la distancia al color medio de los bordes). Es el fallback de ultimo recurso.
|
|
- **passthrough:alpha**: si la imagen ya viene recortada (PNG RGBA con alfa por debajo de 128) se
|
|
reutiliza su canal alfa como mascara, SOLO en modo auto. Si fuerzas un motor concreto se respeta
|
|
esa eleccion e ignora el alfa existente.
|
|
- **Import plano**: importa el modulo directo (`sys.path` a `python/functions/datascience` +
|
|
`from remove_background import remove_background`), NO `from datascience import ...`. El
|
|
`datascience.__init__` carga todo el dominio (scrapers con bs4, duckdb...) con deps ajenas a esta
|
|
funcion que romperian el import del paquete en el venv de vision.
|
|
- Nunca lanza: errores (ruta invalida, motor forzado no disponible, OOM) vuelven como
|
|
`{status:'error', error:str}`.
|