Skip to content

Commit 05d6121

Browse files
Harshith ReddyHarshith Reddy
authored andcommitted
2nd model added
1 parent 95c6115 commit 05d6121

1 file changed

Lines changed: 82 additions & 24 deletions

File tree

src/App.tsx

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
import { useRef, useState, useCallback, useEffect, DragEvent, ChangeEvent } from "react";
22

3+
const API_BASE = "https://harshithreddy01-polyp-detection.hf.space";
4+
35
const ALLOWED_TYPES = ["image/jpeg", "image/jpg", "image/png"];
46
const MAX_MB = 10;
57

8+
type ModelName = "Kvasir-Seg" | "BKAI-IGH";
9+
610
type State =
711
| { stage: "idle" }
812
| { stage: "loading" }
913
| { stage: "error"; message: string }
10-
| { stage: "result"; originalURL: string; maskURL: string };
14+
| { stage: "result"; originalURL: string; maskURL: string; model: ModelName };
15+
16+
const MODEL_INFO: Record<ModelName, { title: string; description: string }> = {
17+
"Kvasir-Seg": {
18+
title: "Kvasir-SEG",
19+
description:
20+
"Trained on 1,000 annotated colonoscopy images covering a wide variety of polyp shapes, sizes, and textures. Best choice for general-purpose polyp detection in standard colonoscopy footage.",
21+
},
22+
"BKAI-IGH": {
23+
title: "BKAI-IGH",
24+
description:
25+
"Trained on a clinically diverse dataset that distinguishes between neoplastic and non-neoplastic polyp categories. Recommended when finer discrimination between polyp types is needed.",
26+
},
27+
};
1128

1229
function validate(file: File): string | null {
1330
if (!ALLOWED_TYPES.includes(file.type))
@@ -43,21 +60,16 @@ function Overlay({ originalURL, maskURL }: { originalURL: string; maskURL: strin
4360
const tryDraw = () => {
4461
loaded++;
4562
if (loaded < 2) return;
46-
4763
canvas.width = 256;
4864
canvas.height = 256;
49-
5065
ctx.drawImage(orig, 0, 0, 256, 256);
51-
5266
const off = document.createElement("canvas");
5367
off.width = 256;
5468
off.height = 256;
5569
const octx = off.getContext("2d")!;
5670
octx.drawImage(mask, 0, 0, 256, 256);
57-
5871
const maskPx = octx.getImageData(0, 0, 256, 256);
5972
const base = ctx.getImageData(0, 0, 256, 256);
60-
6173
for (let i = 0; i < maskPx.data.length; i += 4) {
6274
if (maskPx.data[i] > 128) {
6375
base.data[i] = Math.min(255, base.data[i] + 90);
@@ -85,9 +97,10 @@ function Overlay({ originalURL, maskURL }: { originalURL: string; maskURL: strin
8597
export default function App() {
8698
const [state, setState] = useState<State>({ stage: "idle" });
8799
const [dragOver, setDragOver] = useState(false);
100+
const [selectedModel, setSelectedModel] = useState<ModelName>("Kvasir-Seg");
88101
const inputRef = useRef<HTMLInputElement>(null);
89102

90-
const run = useCallback(async (file: File) => {
103+
const run = useCallback(async (file: File, model: ModelName) => {
91104
const err = validate(file);
92105
if (err) { setState({ stage: "error", message: err }); return; }
93106

@@ -98,29 +111,32 @@ export default function App() {
98111
form.append("file", file);
99112

100113
try {
101-
const resp = await fetch("/predict", { method: "POST", body: form });
114+
const resp = await fetch(`${API_BASE}/predict?model=${encodeURIComponent(model)}`, {
115+
method: "POST",
116+
body: form,
117+
});
102118
if (!resp.ok) {
103119
const body = await resp.json().catch(() => ({})) as { detail?: string };
104120
throw new Error(body.detail ?? `Server error (${resp.status})`);
105121
}
106122
const data = await resp.json() as { mask: string };
107123
const maskURL = "data:image/png;base64," + data.mask;
108-
setState({ stage: "result", originalURL, maskURL });
124+
setState({ stage: "result", originalURL, maskURL, model });
109125
} catch (e) {
110126
setState({ stage: "error", message: (e as Error).message });
111127
}
112128
}, []);
113129

114130
const onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
115131
const file = e.target.files?.[0];
116-
if (file) run(file);
132+
if (file) run(file, selectedModel);
117133
};
118134

119135
const onDrop = (e: DragEvent<HTMLDivElement>) => {
120136
e.preventDefault();
121137
setDragOver(false);
122138
const file = e.dataTransfer.files[0];
123-
if (file) run(file);
139+
if (file) run(file, selectedModel);
124140
};
125141

126142
const reset = () => {
@@ -131,7 +147,6 @@ export default function App() {
131147
return (
132148
<div style={{ display: "flex", flexDirection: "column", minHeight: "100vh" }}>
133149

134-
{/* Header */}
135150
<header style={{
136151
background: "#fff",
137152
borderBottom: "1px solid var(--border)",
@@ -166,10 +181,8 @@ export default function App() {
166181
</div>
167182
</header>
168183

169-
{/* Main */}
170184
<main style={{ flex: 1, maxWidth: 960, width: "100%", margin: "0 auto", padding: "40px 24px", display: "flex", flexDirection: "column", gap: 24 }}>
171185

172-
{/* Notice */}
173186
<div style={{
174187
background: "var(--accent-light)",
175188
border: "1px solid #bfdbfe",
@@ -179,9 +192,7 @@ export default function App() {
179192
color: "#1e40af",
180193
lineHeight: 1.65,
181194
}}>
182-
<strong style={{ display: "block", marginBottom: 6, fontSize: "0.9rem" }}>
183-
What images to upload
184-
</strong>
195+
<strong style={{ display: "block", marginBottom: 6, fontSize: "0.9rem" }}>What images to upload</strong>
185196
This model is trained on <strong>colonoscopy and endoscopy images</strong> for detecting colorectal polyps.
186197
Upload only images captured by a colonoscope showing the inner colon wall.
187198
<ul style={{ paddingLeft: 18, marginTop: 8, display: "flex", flexDirection: "column", gap: 4 }}>
@@ -192,7 +203,52 @@ export default function App() {
192203
</ul>
193204
</div>
194205

195-
{/* Upload zone — hidden when result is shown */}
206+
<div>
207+
<div style={{ fontWeight: 700, fontSize: "0.9rem", marginBottom: 12 }}>Select Model</div>
208+
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
209+
{(Object.keys(MODEL_INFO) as ModelName[]).map((key) => {
210+
const active = selectedModel === key;
211+
return (
212+
<button
213+
key={key}
214+
onClick={() => setSelectedModel(key)}
215+
style={{
216+
background: active ? "var(--accent-light)" : "#fff",
217+
border: `2px solid ${active ? "var(--accent)" : "var(--border)"}`,
218+
borderRadius: "var(--radius)",
219+
padding: "14px 16px",
220+
textAlign: "left",
221+
cursor: "pointer",
222+
transition: "border-color 0.15s, background 0.15s",
223+
}}
224+
>
225+
<div style={{
226+
fontWeight: 700,
227+
fontSize: "0.9rem",
228+
color: active ? "var(--accent)" : "var(--text)",
229+
marginBottom: 6,
230+
display: "flex",
231+
alignItems: "center",
232+
gap: 8,
233+
}}>
234+
<span style={{
235+
width: 14, height: 14, borderRadius: "50%",
236+
border: `2px solid ${active ? "var(--accent)" : "var(--border)"}`,
237+
background: active ? "var(--accent)" : "transparent",
238+
display: "inline-block",
239+
flexShrink: 0,
240+
}} />
241+
{MODEL_INFO[key].title}
242+
</div>
243+
<div style={{ fontSize: "0.8rem", color: "var(--muted)", lineHeight: 1.55 }}>
244+
{MODEL_INFO[key].description}
245+
</div>
246+
</button>
247+
);
248+
})}
249+
</div>
250+
</div>
251+
196252
{state.stage !== "result" && (
197253
<div
198254
onClick={() => inputRef.current?.click()}
@@ -244,7 +300,6 @@ export default function App() {
244300
</div>
245301
)}
246302

247-
{/* Error */}
248303
{state.stage === "error" && (
249304
<div style={{
250305
background: "var(--red-light)",
@@ -259,7 +314,6 @@ export default function App() {
259314
</div>
260315
)}
261316

262-
{/* Loader */}
263317
{state.stage === "loading" && (
264318
<div style={{
265319
display: "flex", alignItems: "center", justifyContent: "center",
@@ -275,15 +329,19 @@ export default function App() {
275329
borderRadius: "50%",
276330
animation: "spin 0.7s linear infinite",
277331
}} />
278-
Running polyp segmentation…
332+
Running segmentation with {MODEL_INFO[selectedModel].title}
279333
</div>
280334
)}
281335

282-
{/* Results */}
283336
{state.stage === "result" && (
284337
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
285-
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
286-
<div style={{ fontWeight: 700, fontSize: "1.05rem" }}>Segmentation Results</div>
338+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 10 }}>
339+
<div>
340+
<div style={{ fontWeight: 700, fontSize: "1.05rem" }}>Segmentation Results</div>
341+
<div style={{ fontSize: "0.78rem", color: "var(--muted)", marginTop: 2 }}>
342+
Model: {MODEL_INFO[state.model].title}
343+
</div>
344+
</div>
287345
<button
288346
onClick={reset}
289347
style={{

0 commit comments

Comments
 (0)