Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -33,6 +33,7 @@ from astropy.io import fits
|
|
| 33 |
from astropy.visualization import make_lupton_rgb
|
| 34 |
from matplotlib.patches import Ellipse
|
| 35 |
|
|
|
|
| 36 |
from galaxy_ellipse import measure_ellipse
|
| 37 |
|
| 38 |
IMG_SIZE = 320
|
|
@@ -179,14 +180,20 @@ def predict_prob(rgb: np.ndarray, use_tta: bool) -> float:
|
|
| 179 |
|
| 180 |
# --- Figure ---
|
| 181 |
|
| 182 |
-
def render(rgb: np.ndarray, geo: dict):
|
| 183 |
-
"""Two-panel figure: enhanced RGB | enhanced RGB + ellipse overlay.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
fig, axes = plt.subplots(1, 2, figsize=(8, 4.2))
|
| 185 |
-
axes[0].imshow(
|
| 186 |
-
axes[0].set_title(
|
| 187 |
axes[0].axis("off")
|
| 188 |
|
| 189 |
-
axes[1].imshow(
|
| 190 |
axes[1].set_title("Imagen + elipse")
|
| 191 |
axes[1].axis("off")
|
| 192 |
if geo.get("status") == "ok":
|
|
@@ -209,21 +216,23 @@ def render(rgb: np.ndarray, geo: dict):
|
|
| 209 |
|
| 210 |
# --- Gradio callback ---
|
| 211 |
|
| 212 |
-
def classify(text: str, layer: str, pixscale: float, use_tta: bool):
|
| 213 |
try:
|
| 214 |
ra, dec, label = resolve_coords(text)
|
| 215 |
except Exception as exc:
|
| 216 |
-
return None, f"No pude resolver la entrada: {exc}"
|
| 217 |
|
| 218 |
try:
|
| 219 |
rgb = fetch_rgb(ra, dec, layer, float(pixscale))
|
| 220 |
except Exception as exc:
|
| 221 |
-
return None, f"No pude bajar el cutout: {exc}"
|
| 222 |
|
|
|
|
|
|
|
| 223 |
prob = predict_prob(rgb, use_tta)
|
| 224 |
pred = int(prob >= THR)
|
| 225 |
geo = measure_ellipse(rgb)
|
| 226 |
-
fig = render(rgb, geo)
|
| 227 |
|
| 228 |
ring_txt = (
|
| 229 |
f"{geo['ring_radius']:.0f} px (candidato exploratorio)"
|
|
@@ -247,7 +256,14 @@ def classify(text: str, layer: str, pixscale: float, use_tta: bool):
|
|
| 247 |
f"_La predicción es una sugerencia de apoyo, no la verdad. La elipse verde es el "
|
| 248 |
f"ajuste de la galaxia; el anillo naranja punteado es un candidato exploratorio._"
|
| 249 |
)
|
| 250 |
-
return fig, summary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
|
| 252 |
|
| 253 |
EXAMPLES = [
|
|
@@ -264,6 +280,8 @@ with gr.Blocks(title="Anillos en galaxias — Legacy Survey") as demo:
|
|
| 264 |
"**URL del visor** de Legacy Survey. Bajo el cutout grz, lo proceso igual que en "
|
| 265 |
"el entrenamiento y corro el modelo (ConvNeXt-Base / Zoobot)."
|
| 266 |
)
|
|
|
|
|
|
|
| 267 |
with gr.Row():
|
| 268 |
with gr.Column(scale=2):
|
| 269 |
inp = gr.Textbox(label="Objeto / ra,dec / URL", placeholder="NGC 7796")
|
|
@@ -274,13 +292,23 @@ with gr.Blocks(title="Anillos en galaxias — Legacy Survey") as demo:
|
|
| 274 |
pixscale = gr.Number(value=0.262, label="Pixscale (arcsec/px)")
|
| 275 |
tta = gr.Checkbox(value=False, label="TTA (6 vistas, más lento y estable)")
|
| 276 |
btn = gr.Button("Clasificar", variant="primary")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
gr.Examples(EXAMPLES, inputs=[inp])
|
| 278 |
with gr.Column(scale=3):
|
| 279 |
out_img = gr.Plot(label="Imagen + elipse")
|
| 280 |
out_md = gr.Markdown()
|
| 281 |
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
|
| 286 |
if __name__ == "__main__":
|
|
|
|
| 33 |
from astropy.visualization import make_lupton_rgb
|
| 34 |
from matplotlib.patches import Ellipse
|
| 35 |
|
| 36 |
+
import enhance
|
| 37 |
from galaxy_ellipse import measure_ellipse
|
| 38 |
|
| 39 |
IMG_SIZE = 320
|
|
|
|
| 180 |
|
| 181 |
# --- Figure ---
|
| 182 |
|
| 183 |
+
def render(rgb: np.ndarray, geo: dict, mode: str = "Original", gray: bool = False):
|
| 184 |
+
"""Two-panel figure: enhanced RGB | enhanced RGB + ellipse overlay.
|
| 185 |
+
|
| 186 |
+
The enhancement is purely visual; the ellipse geometry was measured on the
|
| 187 |
+
original Lupton RGB, so it still lines up after any realce.
|
| 188 |
+
"""
|
| 189 |
+
shown = enhance.apply(rgb, mode, gray)
|
| 190 |
+
title = "Imagen (Lupton grz)" if mode == "Original" else f"Imagen — {mode}"
|
| 191 |
fig, axes = plt.subplots(1, 2, figsize=(8, 4.2))
|
| 192 |
+
axes[0].imshow(shown)
|
| 193 |
+
axes[0].set_title(title)
|
| 194 |
axes[0].axis("off")
|
| 195 |
|
| 196 |
+
axes[1].imshow(shown)
|
| 197 |
axes[1].set_title("Imagen + elipse")
|
| 198 |
axes[1].axis("off")
|
| 199 |
if geo.get("status") == "ok":
|
|
|
|
| 216 |
|
| 217 |
# --- Gradio callback ---
|
| 218 |
|
| 219 |
+
def classify(text: str, layer: str, pixscale: float, use_tta: bool, mode: str, gray: bool):
|
| 220 |
try:
|
| 221 |
ra, dec, label = resolve_coords(text)
|
| 222 |
except Exception as exc:
|
| 223 |
+
return None, f"No pude resolver la entrada: {exc}", None, None
|
| 224 |
|
| 225 |
try:
|
| 226 |
rgb = fetch_rgb(ra, dec, layer, float(pixscale))
|
| 227 |
except Exception as exc:
|
| 228 |
+
return None, f"No pude bajar el cutout: {exc}", None, None
|
| 229 |
|
| 230 |
+
# Prediction and geometry use the ORIGINAL Lupton RGB; the realce is only
|
| 231 |
+
# for display and is re-applied (without re-running the model) on change.
|
| 232 |
prob = predict_prob(rgb, use_tta)
|
| 233 |
pred = int(prob >= THR)
|
| 234 |
geo = measure_ellipse(rgb)
|
| 235 |
+
fig = render(rgb, geo, mode, gray)
|
| 236 |
|
| 237 |
ring_txt = (
|
| 238 |
f"{geo['ring_radius']:.0f} px (candidato exploratorio)"
|
|
|
|
| 256 |
f"_La predicción es una sugerencia de apoyo, no la verdad. La elipse verde es el "
|
| 257 |
f"ajuste de la galaxia; el anillo naranja punteado es un candidato exploratorio._"
|
| 258 |
)
|
| 259 |
+
return fig, summary, rgb, geo
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def rerender(rgb, geo, mode: str, gray: bool):
|
| 263 |
+
"""Re-apply a realce to the already-fetched image (no model re-run)."""
|
| 264 |
+
if rgb is None or geo is None:
|
| 265 |
+
return None
|
| 266 |
+
return render(rgb, geo, mode, gray)
|
| 267 |
|
| 268 |
|
| 269 |
EXAMPLES = [
|
|
|
|
| 280 |
"**URL del visor** de Legacy Survey. Bajo el cutout grz, lo proceso igual que en "
|
| 281 |
"el entrenamiento y corro el modelo (ConvNeXt-Base / Zoobot)."
|
| 282 |
)
|
| 283 |
+
st_rgb = gr.State(None)
|
| 284 |
+
st_geo = gr.State(None)
|
| 285 |
with gr.Row():
|
| 286 |
with gr.Column(scale=2):
|
| 287 |
inp = gr.Textbox(label="Objeto / ra,dec / URL", placeholder="NGC 7796")
|
|
|
|
| 292 |
pixscale = gr.Number(value=0.262, label="Pixscale (arcsec/px)")
|
| 293 |
tta = gr.Checkbox(value=False, label="TTA (6 vistas, más lento y estable)")
|
| 294 |
btn = gr.Button("Clasificar", variant="primary")
|
| 295 |
+
mode = gr.Radio(
|
| 296 |
+
enhance.MODES, value="Original", label="Realce (visual, no afecta la predicción)"
|
| 297 |
+
)
|
| 298 |
+
gray = gr.Checkbox(value=False, label="Escala de grises (B/N)")
|
| 299 |
gr.Examples(EXAMPLES, inputs=[inp])
|
| 300 |
with gr.Column(scale=3):
|
| 301 |
out_img = gr.Plot(label="Imagen + elipse")
|
| 302 |
out_md = gr.Markdown()
|
| 303 |
|
| 304 |
+
inputs = [inp, layer, pixscale, tta, mode, gray]
|
| 305 |
+
outputs = [out_img, out_md, st_rgb, st_geo]
|
| 306 |
+
btn.click(classify, inputs, outputs)
|
| 307 |
+
inp.submit(classify, inputs, outputs)
|
| 308 |
+
|
| 309 |
+
# Changing the realce re-renders the already-fetched image instantly.
|
| 310 |
+
mode.change(rerender, [st_rgb, st_geo, mode, gray], out_img)
|
| 311 |
+
gray.change(rerender, [st_rgb, st_geo, mode, gray], out_img)
|
| 312 |
|
| 313 |
|
| 314 |
if __name__ == "__main__":
|