Spaces:
Running on Zero
Download UIPLAN.md from akashch1512/SingleViewHeigthEstimation: direct link, hf CLI and curl.
- Browser
- Download file 10.5 kB
-
https://huggingface.co/spaces/akashch1512/SingleViewHeigthEstimation/resolve/main/UIPLAN.md
- Command line
-
hf download hf://spaces/akashch1512/SingleViewHeigthEstimation/UIPLAN.md
-
curl -L -o UIPLAN.md https://huggingface.co/spaces/akashch1512/SingleViewHeigthEstimation/resolve/main/UIPLAN.md
A newer version of the Gradio SDK is available: 6.31.0
UI review — issues and betterments
Scope: the Gradio shell (app.py) and the embedded dual-pane viewer
(viewer/index.html + viewer/app.js) as it renders inside the Space.
Ordered by impact. Each item names the file and the concrete change.
P0 — broken or misleading behaviour
1. Running… is never cleared when the run fails
app.py (run.click(...).then(...)) — the chain writes "Running…" into
status, and predict raises gr.Error on every failure path. Gradio shows
the error toast, but status is an output of the raising function, so it is
never overwritten: the page keeps claiming the job is in flight, permanently.
Fix: return errors as values instead of raising for the recoverable cases
(quota exhausted, no image, non-finite output), or add a trailing cleanup step
that resets status regardless of outcome.
2. Stale results stay on screen during and after a new run
Uploading a second image leaves the previous run's viewer iframe, gallery, 16-bit image and download bundle visible. A user who changes the GSD and re-runs sees the old 3D scene for the whole run and has no way to tell which result the downloads belong to.
Fix: in the first .then step, clear every output (gr.update(value=None)
for gallery/height/downloads, "" for the viewer) alongside "Running…".
3. The run button is not disabled while a run is in flight
Two clicks = two ZeroGPU reservations = double the quota burn, which is the
single most common failure mode for this Space (see the quota error text in
app.py). Nothing in the UI prevents it.
Fix: run.click(lambda: (gr.update(interactive=False), "Running…"), ...) and
re-enable in a trailing .then. Setting concurrency_limit=1 on the event is
worth doing too.
4. No empty state — the viewer is a 560 px void on first load
_CSS reserves the height so the layout does not collapse, but nothing is
drawn there. A first-time visitor sees a large blank panel and no signal that
it will become a 3D scene.
Fix: render a placeholder into gr.HTML at startup (a dashed frame with
"Your 3D terrain will appear here" plus the one-line workflow), replaced on
the first result.
5. No examples
There is no gr.Examples. A visitor with no aerial imagery to hand — the
majority — cannot try the demo at all. viewer/sample/ already exists.
Fix: gr.Examples([[sample_png, 0.5, False]], inputs=[image, gsd, tta]) with
two or three scenes at different GSDs.
6. Silent downsampling is never surfaced
read_scene(..., max_side=1600) resamples any large scene, and the viewer
payload is capped again at _VIEWER_MAX_SIDE = 1024. meta.json carries the
note; the UI says nothing. A user who uploads an 8000 px tile gets a
quarter-resolution product and no warning.
Fix: when the input was resampled, add a line to status — "Scene resampled
8000 → 1600 px (GSD adjusted 0.15 → 0.75 m/px)."
P1 — comprehension: the result cannot be read
7. No colour legend anywhere
The turbo height colormap is the primary output — in the gallery, the 2D map
pane and the 3D drape — and there is no colorbar in any of the three. Colour
cannot be converted back to metres by eye. viewer/app.js has turbo();
nothing draws a scale from it.
Fix: draw a vertical colorbar with min/mid/max metre ticks into the 2D pane
and the 3D pane corner, and bake one into height_colormap.png in
_write_artifacts.
8. No scale bar or north arrow on the 2D map
The pane reports nothing about ground extent. S.gsd is known, so a
100 m / 500 m bar is a few lines of canvas.
9. The elevation profile is unlabelled
drawProfile in viewer/app.js prints only the max height. No y-axis
minimum, no x-axis ground distance, no units on the axis. The numbers exist in
reportProbe but are not tied to the plot.
Fix: label both ends of the y range and the x extent in metres.
10. The low-ground warning is buried
The frac_below_1m < 0.15 warning is appended to the bottom of a four-line
markdown blob in the sidebar. It is the most important correctness signal the
app produces — it usually means the declared GSD is wrong.
Fix: give it its own gr.Markdown with a visible warning style, above the
stats, hidden when not triggered.
11. Two product names in one page
The Gradio header says "Single View Height Estimator"; the iframe sidebar says "DepthWizard · single-view DSM · SIH 2026 PS 26175". Inside one viewport that reads as an embedded third-party tool.
Fix: pick one name; hide or rewrite the viewer's <h1>/.tag under
body.embedded.
12. Dead UI in embedded mode
The viewer hides #loadSection when embedded, but leaves:
- the Validation section telling the user to "load
gt_ndsm_m.npyalongside" — impossible, the file picker is gone; - permanently-disabled
Reference height/Error vs referenceoptions in both layer dropdowns.
Fix: body.embedded hides the Validation section and both reference options;
keep them for the standalone viewer/index.html.
13. "Mesh detail" options are misleading in the Space
The embedded payload is capped at 1024 px (~1.05 M pixels), so "Full (1.5M)"
and "Balanced (500k)" differ little and "Full" can never be reached.
_VIEWER_MAX_SIDE and the #quality options are set independently and
disagree.
Fix: derive the option list from the actual grid size, or relabel to Fast / Balanced / Full without vertex counts.
14. Downloads are an unlabelled pile of temp paths
gr.File(file_count="multiple") shows dw_a1b2c3_result.zip, ndsm_m.npy,
terrain.glb… with no explanation of what to do with each. The zip name is a
temp directory id.
Fix: rename the archive after the uploaded file, and put a one-line legend
under the component — ".npy = raw metres · .glb = textured mesh ·
meta.json = encoding + scene metadata".
P2 — input ergonomics
15. The GSD field is the app's main footgun and is under-supported
minimum=0.01, no maximum, and 0 silently means "guess from the
checkpoint's canonical GSD" (float(gsd or 0) → read_scene falls back to
spec.canonical_gsd_m). Nothing tells the user that, before or after.
Fix: add a preset radio (0.3 / 0.5 / 1.0 m/px + Custom + "I don't know"), make
the "don't know" path explicit, and echo the effective GSD and its source in
the status line (scene_meta.gsd_source is already computed).
16. TTA does not disclose its cost
"Higher quality (slower)" hides the fact that it asks for a 120 s ZeroGPU window instead of 45 s — and that this is the usual cause of the quota error. The user only learns it from the failure message.
Fix: info="~4× the compute; reserves 120 s of GPU quota instead of 45 s".
17. No expected runtime anywhere
Nothing tells the user whether to wait 10 s or 3 minutes before the first run.
18. No reset / clear
Once a result is on screen there is no way back to the empty state.
P3 — layout, theme, accessibility
19. The Gradio shell is light, the viewer is hard-coded dark
viewer/index.html sets color-scheme: dark and --bg:#0d1013. In default
(light) Gradio that is a 760 px black slab in a white page.
Fix: set a dark gr.themes on the Blocks, or make the viewer's palette follow
prefers-color-scheme and pass the host theme in through the payload.
20. Fixed 760 px iframe height
Hard-coded in _viewer(). On a 768 px-tall laptop the viewer needs page
scrolling, while the viewer itself is overflow:hidden — so its sidebar
scrolls independently inside a frame that is already clipped. Two nested
scroll contexts.
Fix: size the iframe in viewport units (height:min(760px, 78vh)) with a
floor, and add a fullscreen toggle (the frame already has allowfullscreen).
21. The viewer's mobile breakpoint fires on desktop
@media (max-width:900px) is evaluated against the iframe width. In the 3:1
column split the frame is often under 900 px on a normal laptop, so the 2D
pane disappears and the sidebar stacks — the mobile layout on a desktop, which
reads as a bug.
Fix: switch to a container-driven breakpoint (or lower it and set a
min_width on the Gradio column so the frame stays wide).
22. The 322 px fixed sidebar squeezes the 2D pane
Inside an already-narrow frame, 322px | 38% | 1fr leaves the map pane a
sliver. Consider a collapsible sidebar.
23. No <label for=...> anywhere in the viewer
Every control in viewer/index.html uses a bare <label> followed by a
sibling input — exaggeration, sun azimuth/elevation, drape, mesh detail.
Labels are not clickable and screen readers announce nothing. The checkbox
rows wrap their text in a <span>, not a label.
Fix: add for=/id= pairs, or wrap the inputs.
24. Mode buttons carry no pressed state for assistive tech
button.on is visual only — add aria-pressed. #hud, #probe and #info
update silently — add aria-live="polite" to #probe and #warn.
25. gr.HTML(label="3D view") renders no label
gr.HTML ignores label. The main output panel is unheaded.
26. Keyboard shortcuts need a click first
F and R are bound on the iframe's window; until the user clicks inside
the frame the host page owns the keyboard. The hint advertises them
unconditionally.
Fix: mention "click the scene first", or focus the canvas on load.
27. Desktop-only interaction hints
drag = orbit · scroll = zoom with no touch equivalents, and the 2D pane
probe is pointer-only with no zoom/pan.
28. warn() appends without bound or dedupe
It concatenates into #warn.innerHTML forever. A repeating error grows the
sidebar indefinitely and there is no dismiss.
29. No progress feedback while the mesh builds
buildMesh() on a ~1 M-vertex grid blocks the frame for seconds after the
Gradio progress bar has already finished. The user sees a frozen black pane
and no spinner.
30. Small type throughout the viewer
11–12 px for controls, 11 px for .tag, #hud and #hint. Fine for a dense
desktop tool, tight inside an embedded frame.
Suggested order of work
- P0 1–3 — state bugs; small diffs in the
run.clickchain. - P0 4–5 — empty state + examples; the biggest first-impression win.
- P1 7–9 — colorbar, scale bar, profile axes; makes the output readable.
- P1 10–14 + P2 15–16 — messaging and input clarity.
- P3 — theme unification (19) and the layout/breakpoint pair (20–22) together, then accessibility (23–25).