nusaibah0110 commited on
Commit
9e1ba1f
·
1 Parent(s): 8b3e779
backend/__pycache__/augmentations.cpython-312.pyc CHANGED
Binary files a/backend/__pycache__/augmentations.cpython-312.pyc and b/backend/__pycache__/augmentations.cpython-312.pyc differ
 
backend/__pycache__/model.cpython-312.pyc CHANGED
Binary files a/backend/__pycache__/model.cpython-312.pyc and b/backend/__pycache__/model.cpython-312.pyc differ
 
backend/__pycache__/model_histo.cpython-312.pyc CHANGED
Binary files a/backend/__pycache__/model_histo.cpython-312.pyc and b/backend/__pycache__/model_histo.cpython-312.pyc differ
 
frontend/src/components/ResultsPanel.tsx CHANGED
@@ -1,5 +1,30 @@
1
  import { useState } from "react";
2
- import { DownloadIcon, FileTextIcon, Loader2Icon } from "lucide-react";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import { ImageWithFallback } from "./ImageWithFallback";
4
  import { ReportModal } from "./ReportModal";
5
  import axios from "axios";
@@ -67,6 +92,22 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
67
  }
68
  };
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
 
72
  return (
@@ -208,30 +249,63 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
208
 
209
  {/* Per-class bars */}
210
  <div className="space-y-2">
211
- {Object.entries(confidence).map(([cls, val]) => {
212
- const num = Number(val as any) || 0;
213
- const pct = (num * 100);
214
-
215
- // Color coding: Positive/Malignant/High-grade = red, Negative/Benign/Low-grade = green
216
- const isNegative = cls.toLowerCase().includes("negative") ||
217
- cls.toLowerCase().includes("benign") ||
218
- cls.toLowerCase().includes("low-grade");
219
-
220
- return (
221
- <div key={cls}>
222
- <div className="flex items-center justify-between text-sm mb-1">
223
- <span className="text-gray-700">{cls}</span>
224
- <span className="text-gray-600">{pct.toFixed(2)}%</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  </div>
226
- <div className="w-full bg-gray-100 rounded-full h-3">
227
- <div
228
- className={`h-3 rounded-full ${isNegative ? "bg-green-500" : "bg-red-500"}`}
229
- style={{ width: `${pct.toFixed(2)}%` }}
230
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  </div>
232
- </div>
233
- );
234
- })}
235
  </div>
236
 
237
  {/* Mistral comment */}
@@ -249,7 +323,6 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
249
  )}
250
 
251
  {/* Report Generation Modal */}
252
- {/* Move Generate Report button here so it's shown after analysis details. */}
253
  {/* Show only when not loading and we have at least a result (so Generate Report is available even if summary/confidence are missing) */}
254
  {!isLoading && result && (
255
  <div className="flex items-center justify-end mb-6">
 
1
  import { useState } from "react";
2
+
3
+ /* Minimal inline SVG icon components to avoid requiring 'lucide-react' */
4
+ const DownloadIcon = ({ className }: { className?: string }) => (
5
+ <svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
6
+ <path d="M12 3v12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
7
+ <path d="M8 11l4 4 4-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
8
+ <path d="M21 21H3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
9
+ </svg>
10
+ );
11
+
12
+ const FileTextIcon = ({ className }: { className?: string }) => (
13
+ <svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
14
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
15
+ <path d="M14 2v6h6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
16
+ <path d="M16 13H8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
17
+ <path d="M16 17H8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
18
+ </svg>
19
+ );
20
+
21
+ const Loader2Icon = ({ className }: { className?: string }) => (
22
+ <svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
23
+ <circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" strokeOpacity="0.25"/>
24
+ <path d="M22 12a10 10 0 0 0-10-10" stroke="currentColor" strokeWidth="4" strokeLinecap="round"/>
25
+ </svg>
26
+ );
27
+
28
  import { ImageWithFallback } from "./ImageWithFallback";
29
  import { ReportModal } from "./ReportModal";
30
  import axios from "axios";
 
92
  }
93
  };
94
 
95
+ // Precompute some helpers for rendering confidences
96
+ const isCINModel = /cin/i.test(String(model_used || ""));
97
+ // Determine predicted class from summary.prediction if available, otherwise pick the highest confidence
98
+ const predictedClassFromConfidence = (conf: any) => {
99
+ try {
100
+ const entries = Object.entries(conf || {});
101
+ if (entries.length === 0) return "";
102
+ return entries.reduce((a: any, b: any) => (Number(a[1]) > Number(b[1]) ? a : b))[0];
103
+ } catch (e) {
104
+ return "";
105
+ }
106
+ };
107
+ const predictedClass = (summary && (summary.prediction || summary.result))
108
+ ? String(summary.prediction || summary.result)
109
+ : predictedClassFromConfidence(confidence || {});
110
+
111
 
112
 
113
  return (
 
249
 
250
  {/* Per-class bars */}
251
  <div className="space-y-2">
252
+ {isCINModel ? (
253
+ // For CIN/colposcopy classifiers, show ONLY the predicted grade (no bars for other classes)
254
+ (() => {
255
+ const cls = String(predictedClass || "");
256
+ const val = (confidence && (confidence as any)[cls]) || 0;
257
+ const num = Number(val) || 0;
258
+ const pct = num * 100;
259
+ const isNegative = cls.toLowerCase().includes("negative") ||
260
+ cls.toLowerCase().includes("benign") ||
261
+ cls.toLowerCase().includes("low-grade") ||
262
+ cls.toLowerCase().includes("cin1");
263
+
264
+ if (!cls) {
265
+ return <div className="text-sm text-gray-600">Prediction not available.</div>;
266
+ }
267
+
268
+ return (
269
+ <div key={cls}>
270
+ <div className="flex items-center justify-between text-sm mb-1">
271
+ <span className="text-gray-700">{cls}</span>
272
+ <span className="text-gray-600">{pct.toFixed(2)}%</span>
273
+ </div>
274
+ <div className="w-full bg-gray-100 rounded-full h-3">
275
+ <div
276
+ className={`h-3 rounded-full ${isNegative ? "bg-green-500" : "bg-red-500"}`}
277
+ style={{ width: `${pct.toFixed(2)}%` }}
278
+ />
279
+ </div>
280
  </div>
281
+ );
282
+ })()
283
+ ) : (
284
+ Object.entries(confidence).map(([cls, val]) => {
285
+ const num = Number(val as any) || 0;
286
+ const pct = (num * 100);
287
+
288
+ // Color coding: Positive/Malignant/High-grade = red, Negative/Benign/Low-grade = green
289
+ const isNegative = cls.toLowerCase().includes("negative") ||
290
+ cls.toLowerCase().includes("benign") ||
291
+ cls.toLowerCase().includes("low-grade");
292
+
293
+ return (
294
+ <div key={cls}>
295
+ <div className="flex items-center justify-between text-sm mb-1">
296
+ <span className="text-gray-700">{cls}</span>
297
+ <span className="text-gray-600">{pct.toFixed(2)}%</span>
298
+ </div>
299
+ <div className="w-full bg-gray-100 rounded-full h-3">
300
+ <div
301
+ className={`h-3 rounded-full ${isNegative ? "bg-green-500" : "bg-red-500"}`}
302
+ style={{ width: `${pct.toFixed(2)}%` }}
303
+ />
304
+ </div>
305
  </div>
306
+ );
307
+ })
308
+ )}
309
  </div>
310
 
311
  {/* Mistral comment */}
 
323
  )}
324
 
325
  {/* Report Generation Modal */}
 
326
  {/* Show only when not loading and we have at least a result (so Generate Report is available even if summary/confidence are missing) */}
327
  {!isLoading && result && (
328
  <div className="flex items-center justify-end mb-6">