File size: 6,098 Bytes
4763a01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

import io
 
import spaces
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from PIL import Image
import torch
import gradio as gr
from huggingface_hub import hf_hub_download
 
from river_model import load_trained_model, build_processor, WATER_CLASS_ID
 
 
 
# Configuration
 
REPO_ID       = "beaunix/river-segmentation"
CKPT_FILENAME = "best_model.pt"
 
# Cyberpunk HUD palette - navy + turquoise edition
BG        = "#000000"
PANEL     = "#050912"
NAVY      = "#0A1C40"
TURQUOISE = "#00E5D4"
LIGHTBLUE = "#5FD8FF"
SILVER    = "#C0C0C0"
GRID      = "#10182a"
 
WATER_COLOR_RGB = (95, 216, 255)   # lightblue, for the mask overlay
BG_COLOR_RGB    = (0, 0, 0)        # black fallback for all other classes
 
 
 
# Model loading (CPU, once at startup)
 
print("[INIT] Loading River-Segmentation model...")
_ckpt_path = hf_hub_download(repo_id=REPO_ID, filename=CKPT_FILENAME)
MODEL = load_trained_model(_ckpt_path)
PROCESSOR = build_processor()
print("[INIT] Model ready (CPU).")
 
 
 
# Inference (single GPU allocation)
 
@spaces.GPU(duration=60)
def run_inference_gpu(image: Image.Image):
    device = "cuda" if torch.cuda.is_available() else "cpu"
    MODEL.to(device)
    MODEL.eval()
 
    orig_w, orig_h = image.size
    inputs = PROCESSOR(images=image, return_tensors="pt")
    pixel_values = inputs["pixel_values"].to(device)
 
    with torch.no_grad():
        if device == "cuda":
            with torch.autocast(device_type="cuda", dtype=torch.float16):
                outputs = MODEL(pixel_values=pixel_values)
        else:
            outputs = MODEL(pixel_values=pixel_values)
        logits = outputs.logits
 
    logits_up = torch.nn.functional.interpolate(
        logits, size=(orig_h, orig_w), mode="bilinear", align_corners=False,
    )
    pred_mask = logits_up.argmax(dim=1).squeeze(0).cpu().numpy().astype(np.uint8)
 
    MODEL.to("cpu")
    return pred_mask
 
 
 
# Post-processing: collapse to binary water mask
 
def binary_water_mask(pred_mask: np.ndarray) -> np.ndarray:
    return (pred_mask == WATER_CLASS_ID).astype(np.uint8)
 
 
def mask_to_rgb(binary_mask: np.ndarray) -> np.ndarray:
    h, w = binary_mask.shape
    rgb = np.zeros((h, w, 3), dtype=np.uint8)
    rgb[binary_mask == 1] = WATER_COLOR_RGB
    rgb[binary_mask == 0] = BG_COLOR_RGB
    return rgb
 
 
def build_overlay(orig_image: Image.Image, rgb_mask: np.ndarray) -> np.ndarray:
    orig_np = np.array(orig_image.convert("RGB"))
    overlay = (orig_np * 0.55 + rgb_mask * 0.45).astype(np.uint8)
    return overlay
 
 
def build_hud_figure(orig_image, rgb_mask, overlay, water_pct):
    plt.rcParams.update({
        "font.family": "monospace",
        "text.color": SILVER,
        "axes.edgecolor": TURQUOISE,
    })
 
    fig, axes = plt.subplots(1, 3, figsize=(15, 5.2))
    fig.patch.set_facecolor(BG)
 
    for ax in axes:
        ax.set_facecolor(PANEL)
        ax.axis("off")
        for s in ax.spines.values():
            s.set_color(TURQUOISE)
 
    axes[0].imshow(np.array(orig_image.convert("RGB")))
    axes[0].set_title("ORIGINAL", color=LIGHTBLUE, fontsize=11)
 
    axes[1].imshow(rgb_mask)
    axes[1].set_title("WATER MASK (binary)", color=LIGHTBLUE, fontsize=11)
 
    axes[2].imshow(overlay)
    axes[2].set_title("OVERLAY", color=LIGHTBLUE, fontsize=11)
 
    fig.suptitle(
        f"AEGIS-RIVER-SEGMENTATION // WATER BODY DETECTION   "
        f"water_ratio={water_pct:.2f}%",
        color=TURQUOISE, fontsize=13, y=1.02
    )
 
    fig.tight_layout()
    buf = io.BytesIO()
    fig.savefig(buf, format="png", dpi=140, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    buf.seek(0)
    return Image.open(buf)
 
 
 
# Main handler
 
def analyze(image: Image.Image):
    empty_df = pd.DataFrame()
    if image is None:
        return "Please upload an image.", None, empty_df
 
    pred_mask   = run_inference_gpu(image)
    water_mask  = binary_water_mask(pred_mask)
    rgb_mask    = mask_to_rgb(water_mask)
    overlay     = build_overlay(image, rgb_mask)
 
    total_px    = water_mask.size
    water_px    = int(water_mask.sum())
    water_pct   = round((water_px / total_px) * 100, 2)
 
    hud_img = build_hud_figure(image, rgb_mask, overlay, water_pct)
 
    status = f"Analysis complete  //  Water coverage: {water_pct:.2f}%"
 
    summary_df = pd.DataFrame({
        "Metric": ["Image size", "Water pixels", "Total pixels", "Water ratio (%)"],
        "Value": [
            f"{image.size[0]} x {image.size[1]}",
            f"{water_px:,}", f"{total_px:,}", f"{water_pct:.2f}",
        ],
    })
 
    return status, hud_img, summary_df
 
 
 
# Gradio UI - Navy + Turquoise Cyberpunk HUD
 
CSS = """
.gradio-container { background: #000000 !important; }
h1, h2, h3, p, span, label { color: #00E5D4 !important; font-family: monospace !important; }
.block, .form { border: 1px solid #0A1C40 !important; border-radius: 6px !important;
                background: #050912 !important; }
.gr-button { border: 1px solid #00E5D4 !important; color: #00E5D4 !important;
             background: #050912 !important; font-family: monospace !important; }
"""
 
with gr.Blocks(css=CSS, title="Aegis-River-Segmentation") as demo:
    gr.Markdown("# AEGIS-RIVER-SEGMENTATION // WATER BODY MASK")
    gr.Markdown(
        "SegFormer-B2 semantic segmentation, collapsed to a binary water mask. "
        "Upload a river or waterway image (drag and drop) to extract the "
        "water region and its coverage ratio."
    )
 
    with gr.Row():
        with gr.Column(scale=1):
            image_in = gr.Image(label="Input image", type="pil")
            run_btn  = gr.Button("RUN ANALYSIS", variant="primary")
            status   = gr.Markdown()
 
    hud_out     = gr.Image(label="Water segmentation HUD", type="pil")
    summary_out = gr.Dataframe(label="Summary metrics", interactive=False)
 
    run_btn.click(
        fn=analyze,
        inputs=[image_in],
        outputs=[status, hud_out, summary_out],
    )
 
 
if __name__ == "__main__":
    
    demo.queue().launch(show_api=False, ssr_mode=False)