Spaces:
Sleeping
Sleeping
Commit
Β·
789e997
1
Parent(s):
d06de52
Update
Browse files- backend/app.py +14 -29
- frontend/src/components/ResultsPanel.tsx +3 -24
backend/app.py
CHANGED
|
@@ -90,7 +90,6 @@ def generate_ai_summary(abnormal_cells, normal_cells, avg_confidence):
|
|
| 90 |
Observed Cell Counts:
|
| 91 |
- {abnormal_cells} Abnormal Cells
|
| 92 |
- {normal_cells} Normal Cells
|
| 93 |
-
- Detection Confidence: {avg_confidence:.1f}%
|
| 94 |
|
| 95 |
Write a 2-3 sentence professional medical assessment focusing on:
|
| 96 |
1. Cell count analysis
|
|
@@ -108,7 +107,7 @@ Use objective, scientific language suitable for a pathology report."""
|
|
| 108 |
details=True,
|
| 109 |
stop_sequences=["\n\n", "###"]
|
| 110 |
)
|
| 111 |
-
|
| 112 |
# Handle different response formats
|
| 113 |
if hasattr(response, 'generated_text'):
|
| 114 |
return response.generated_text.strip()
|
|
@@ -116,21 +115,21 @@ Use objective, scientific language suitable for a pathology report."""
|
|
| 116 |
return response.get('generated_text', '').strip()
|
| 117 |
elif isinstance(response, str):
|
| 118 |
return response.strip()
|
| 119 |
-
|
| 120 |
# Fallback summary if response format is unexpected
|
| 121 |
ratio = abnormal_cells / (abnormal_cells + normal_cells) * 100 if (abnormal_cells + normal_cells) > 0 else 0
|
| 122 |
-
return f"Analysis shows {abnormal_cells} abnormal cells ({ratio:.1f}%) and {normal_cells} normal cells
|
| 123 |
|
| 124 |
except Exception as e:
|
| 125 |
# Provide a structured fallback summary instead of error message
|
| 126 |
total = abnormal_cells + normal_cells
|
| 127 |
if total == 0:
|
| 128 |
return "No cells were detected in the sample. Consider re-scanning or adjusting detection parameters."
|
| 129 |
-
|
| 130 |
ratio = (abnormal_cells / total) * 100
|
| 131 |
severity = "high" if ratio > 70 else "moderate" if ratio > 30 else "low"
|
| 132 |
-
|
| 133 |
-
return f"Quantitative analysis detected {abnormal_cells} abnormal cells ({ratio:.1f}%) among {total} total cells, indicating {severity} abnormality ratio.
|
| 134 |
|
| 135 |
|
| 136 |
def generate_mwt_summary(predicted_label, confidences, avg_confidence):
|
|
@@ -144,7 +143,6 @@ You are a concise cytopathology expert. Given an MWT classifier result, write a
|
|
| 144 |
|
| 145 |
Result:
|
| 146 |
- Predicted label: {predicted_label}
|
| 147 |
-
- Confidence (average): {avg_confidence:.1f}%
|
| 148 |
- Class probabilities: {json.dumps(confidences)}
|
| 149 |
|
| 150 |
Provide guidance on the significance of the result and any suggested next steps in plain, objective language.
|
|
@@ -166,9 +164,9 @@ Provide guidance on the significance of the result and any suggested next steps
|
|
| 166 |
elif isinstance(response, str):
|
| 167 |
return response.strip()
|
| 168 |
|
| 169 |
-
return f"Result: {predicted_label}
|
| 170 |
except Exception as e:
|
| 171 |
-
return f"Quantitative result: {predicted_label}
|
| 172 |
|
| 173 |
|
| 174 |
def generate_cin_summary(predicted_grade, confidences, avg_confidence):
|
|
@@ -182,7 +180,6 @@ You are a concise gynecologic pathology expert. Given a CIN classifier result, w
|
|
| 182 |
|
| 183 |
Result:
|
| 184 |
- Predicted grade: {predicted_grade}
|
| 185 |
-
- Confidence (average): {avg_confidence:.1f}%
|
| 186 |
- Class probabilities: {json.dumps(confidences)}
|
| 187 |
|
| 188 |
Provide a brief statement about clinical significance and suggested next steps (e.g., further colposcopic evaluation) in objective, clinical language.
|
|
@@ -204,9 +201,9 @@ Provide a brief statement about clinical significance and suggested next steps (
|
|
| 204 |
elif isinstance(response, str):
|
| 205 |
return response.strip()
|
| 206 |
|
| 207 |
-
return f"Result: {predicted_grade}
|
| 208 |
except Exception:
|
| 209 |
-
return f"Quantitative result: {predicted_grade}
|
| 210 |
|
| 211 |
|
| 212 |
# =====================================================
|
|
@@ -392,14 +389,13 @@ def predict_histopathology(image):
|
|
| 392 |
# Return confidence as dictionary with both class probabilities (like MWT/CIN)
|
| 393 |
confidences = {class_names[i]: float(prediction_proba[i]) for i in range(len(class_names))}
|
| 394 |
avg_confidence = float(np.max(prediction_proba)) * 100
|
| 395 |
-
|
| 396 |
return {
|
| 397 |
"model_used": "Histopathology Classifier",
|
| 398 |
"prediction": class_names[predicted_class],
|
| 399 |
"confidence": confidences,
|
| 400 |
"summary": {
|
| 401 |
-
"
|
| 402 |
-
"ai_interpretation": f"Histopathological analysis indicates {class_names[predicted_class].lower()} tissue with {avg_confidence:.1f}% confidence.",
|
| 403 |
},
|
| 404 |
}
|
| 405 |
except Exception as e:
|
|
@@ -475,7 +471,6 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
|
|
| 475 |
"summary": {
|
| 476 |
"abnormal_cells": abnormal_cells,
|
| 477 |
"normal_cells": normal_cells,
|
| 478 |
-
"avg_confidence": round(float(avg_confidence), 2),
|
| 479 |
"ai_interpretation": ai_summary,
|
| 480 |
},
|
| 481 |
}
|
|
@@ -498,7 +493,6 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
|
|
| 498 |
"prediction": predicted_label,
|
| 499 |
"confidence": confidences,
|
| 500 |
"summary": {
|
| 501 |
-
"avg_confidence": round(avg_confidence, 2),
|
| 502 |
"ai_interpretation": ai_interp,
|
| 503 |
},
|
| 504 |
}
|
|
@@ -596,7 +590,6 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
|
|
| 596 |
"prediction": predicted_label,
|
| 597 |
"confidence": confidences,
|
| 598 |
"summary": {
|
| 599 |
-
"avg_confidence": round(avg_confidence, 2),
|
| 600 |
"ai_interpretation": ai_interp,
|
| 601 |
},
|
| 602 |
}
|
|
@@ -690,7 +683,6 @@ def create_designed_pdf(pdf_path, report_data, analysis_summary_json, annotated_
|
|
| 690 |
# AI Analysis Section
|
| 691 |
story.append(Paragraph("AI-ASSISTED ANALYSIS", styles['Section']))
|
| 692 |
story.append(Paragraph("<b>System:</b> Manalife AI System β Automated Analysis", styles['NormalSmall']))
|
| 693 |
-
story.append(Paragraph(f"<b>Confidence Score:</b> {ai_summary.get('avg_confidence', 'N/A')}%", styles['NormalSmall']))
|
| 694 |
|
| 695 |
# Add metrics based on report type
|
| 696 |
if report_type == "HISTOPATHOLOGY":
|
|
@@ -885,7 +877,6 @@ async def generate_report(
|
|
| 885 |
|
| 886 |
analysis_metrics_html = f"""
|
| 887 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
| 888 |
-
<tr><th>Confidence Score</th><td>{ai_summary.get('avg_confidence', 'N/A')}%</td></tr>
|
| 889 |
<tr><th>Benign Confidence</th><td>{benign_conf:.2f}%</td></tr>
|
| 890 |
<tr><th>Malignant Confidence</th><td>{malignant_conf:.2f}%</td></tr>
|
| 891 |
"""
|
|
@@ -893,7 +884,6 @@ async def generate_report(
|
|
| 893 |
# For cytology (YOLO), show abnormal/normal cells
|
| 894 |
analysis_metrics_html = f"""
|
| 895 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
| 896 |
-
<tr><th>Confidence Score</th><td>{ai_summary.get('avg_confidence', 'N/A')}%</td></tr>
|
| 897 |
<tr><th>Abnormal Cells</th><td>{ai_summary.get('abnormal_cells', 'N/A')}</td></tr>
|
| 898 |
<tr><th>Normal Cells</th><td>{ai_summary.get('normal_cells', 'N/A')}</td></tr>
|
| 899 |
"""
|
|
@@ -908,7 +898,6 @@ async def generate_report(
|
|
| 908 |
|
| 909 |
analysis_metrics_html = f"""
|
| 910 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
| 911 |
-
<tr><th>Confidence Score</th><td>{ai_summary.get('avg_confidence', 'N/A')}%</td></tr>
|
| 912 |
{confidence_rows}
|
| 913 |
"""
|
| 914 |
|
|
@@ -1036,12 +1025,8 @@ async def generate_report(
|
|
| 1036 |
<div class="section-title">Signatures</div>
|
| 1037 |
<div class="signatures">
|
| 1038 |
<div class="sig">
|
| 1039 |
-
<div style="font-weight:700">
|
| 1040 |
-
<div class="muted">
|
| 1041 |
-
</div>
|
| 1042 |
-
<div class="sig">
|
| 1043 |
-
<div style="font-weight:700">Dr. James Wilson</div>
|
| 1044 |
-
<div class="muted">MD, pathologist</div>
|
| 1045 |
</div>
|
| 1046 |
</div>
|
| 1047 |
</div>
|
|
|
|
| 90 |
Observed Cell Counts:
|
| 91 |
- {abnormal_cells} Abnormal Cells
|
| 92 |
- {normal_cells} Normal Cells
|
|
|
|
| 93 |
|
| 94 |
Write a 2-3 sentence professional medical assessment focusing on:
|
| 95 |
1. Cell count analysis
|
|
|
|
| 107 |
details=True,
|
| 108 |
stop_sequences=["\n\n", "###"]
|
| 109 |
)
|
| 110 |
+
|
| 111 |
# Handle different response formats
|
| 112 |
if hasattr(response, 'generated_text'):
|
| 113 |
return response.generated_text.strip()
|
|
|
|
| 115 |
return response.get('generated_text', '').strip()
|
| 116 |
elif isinstance(response, str):
|
| 117 |
return response.strip()
|
| 118 |
+
|
| 119 |
# Fallback summary if response format is unexpected
|
| 120 |
ratio = abnormal_cells / (abnormal_cells + normal_cells) * 100 if (abnormal_cells + normal_cells) > 0 else 0
|
| 121 |
+
return f"Analysis shows {abnormal_cells} abnormal cells ({ratio:.1f}%) and {normal_cells} normal cells."
|
| 122 |
|
| 123 |
except Exception as e:
|
| 124 |
# Provide a structured fallback summary instead of error message
|
| 125 |
total = abnormal_cells + normal_cells
|
| 126 |
if total == 0:
|
| 127 |
return "No cells were detected in the sample. Consider re-scanning or adjusting detection parameters."
|
| 128 |
+
|
| 129 |
ratio = (abnormal_cells / total) * 100
|
| 130 |
severity = "high" if ratio > 70 else "moderate" if ratio > 30 else "low"
|
| 131 |
+
|
| 132 |
+
return f"Quantitative analysis detected {abnormal_cells} abnormal cells ({ratio:.1f}%) among {total} total cells, indicating {severity} abnormality ratio."
|
| 133 |
|
| 134 |
|
| 135 |
def generate_mwt_summary(predicted_label, confidences, avg_confidence):
|
|
|
|
| 143 |
|
| 144 |
Result:
|
| 145 |
- Predicted label: {predicted_label}
|
|
|
|
| 146 |
- Class probabilities: {json.dumps(confidences)}
|
| 147 |
|
| 148 |
Provide guidance on the significance of the result and any suggested next steps in plain, objective language.
|
|
|
|
| 164 |
elif isinstance(response, str):
|
| 165 |
return response.strip()
|
| 166 |
|
| 167 |
+
return f"Result: {predicted_label}."
|
| 168 |
except Exception as e:
|
| 169 |
+
return f"Quantitative result: {predicted_label}."
|
| 170 |
|
| 171 |
|
| 172 |
def generate_cin_summary(predicted_grade, confidences, avg_confidence):
|
|
|
|
| 180 |
|
| 181 |
Result:
|
| 182 |
- Predicted grade: {predicted_grade}
|
|
|
|
| 183 |
- Class probabilities: {json.dumps(confidences)}
|
| 184 |
|
| 185 |
Provide a brief statement about clinical significance and suggested next steps (e.g., further colposcopic evaluation) in objective, clinical language.
|
|
|
|
| 201 |
elif isinstance(response, str):
|
| 202 |
return response.strip()
|
| 203 |
|
| 204 |
+
return f"Result: {predicted_grade}."
|
| 205 |
except Exception:
|
| 206 |
+
return f"Quantitative result: {predicted_grade}."
|
| 207 |
|
| 208 |
|
| 209 |
# =====================================================
|
|
|
|
| 389 |
# Return confidence as dictionary with both class probabilities (like MWT/CIN)
|
| 390 |
confidences = {class_names[i]: float(prediction_proba[i]) for i in range(len(class_names))}
|
| 391 |
avg_confidence = float(np.max(prediction_proba)) * 100
|
| 392 |
+
|
| 393 |
return {
|
| 394 |
"model_used": "Histopathology Classifier",
|
| 395 |
"prediction": class_names[predicted_class],
|
| 396 |
"confidence": confidences,
|
| 397 |
"summary": {
|
| 398 |
+
"ai_interpretation": f"Histopathological analysis indicates {class_names[predicted_class].lower()} tissue.",
|
|
|
|
| 399 |
},
|
| 400 |
}
|
| 401 |
except Exception as e:
|
|
|
|
| 471 |
"summary": {
|
| 472 |
"abnormal_cells": abnormal_cells,
|
| 473 |
"normal_cells": normal_cells,
|
|
|
|
| 474 |
"ai_interpretation": ai_summary,
|
| 475 |
},
|
| 476 |
}
|
|
|
|
| 493 |
"prediction": predicted_label,
|
| 494 |
"confidence": confidences,
|
| 495 |
"summary": {
|
|
|
|
| 496 |
"ai_interpretation": ai_interp,
|
| 497 |
},
|
| 498 |
}
|
|
|
|
| 590 |
"prediction": predicted_label,
|
| 591 |
"confidence": confidences,
|
| 592 |
"summary": {
|
|
|
|
| 593 |
"ai_interpretation": ai_interp,
|
| 594 |
},
|
| 595 |
}
|
|
|
|
| 683 |
# AI Analysis Section
|
| 684 |
story.append(Paragraph("AI-ASSISTED ANALYSIS", styles['Section']))
|
| 685 |
story.append(Paragraph("<b>System:</b> Manalife AI System β Automated Analysis", styles['NormalSmall']))
|
|
|
|
| 686 |
|
| 687 |
# Add metrics based on report type
|
| 688 |
if report_type == "HISTOPATHOLOGY":
|
|
|
|
| 877 |
|
| 878 |
analysis_metrics_html = f"""
|
| 879 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
|
|
|
| 880 |
<tr><th>Benign Confidence</th><td>{benign_conf:.2f}%</td></tr>
|
| 881 |
<tr><th>Malignant Confidence</th><td>{malignant_conf:.2f}%</td></tr>
|
| 882 |
"""
|
|
|
|
| 884 |
# For cytology (YOLO), show abnormal/normal cells
|
| 885 |
analysis_metrics_html = f"""
|
| 886 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
|
|
|
| 887 |
<tr><th>Abnormal Cells</th><td>{ai_summary.get('abnormal_cells', 'N/A')}</td></tr>
|
| 888 |
<tr><th>Normal Cells</th><td>{ai_summary.get('normal_cells', 'N/A')}</td></tr>
|
| 889 |
"""
|
|
|
|
| 898 |
|
| 899 |
analysis_metrics_html = f"""
|
| 900 |
<tr><th>System</th><td>Manalife AI System β Automated Analysis</td></tr>
|
|
|
|
| 901 |
{confidence_rows}
|
| 902 |
"""
|
| 903 |
|
|
|
|
| 1025 |
<div class="section-title">Signatures</div>
|
| 1026 |
<div class="signatures">
|
| 1027 |
<div class="sig">
|
| 1028 |
+
<div style="font-weight:700">Rajesh Venugopal</div>
|
| 1029 |
+
<div class="muted">Physician</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1030 |
</div>
|
| 1031 |
</div>
|
| 1032 |
</div>
|
frontend/src/components/ResultsPanel.tsx
CHANGED
|
@@ -139,9 +139,7 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
|
|
| 139 |
{typeof summary.normal_cells !== 'undefined' && (
|
| 140 |
<><strong>Normal Cells:</strong> {summary.normal_cells} <br /></>
|
| 141 |
)}
|
| 142 |
-
{
|
| 143 |
-
<><strong>Average Confidence:</strong> {summary.avg_confidence?.toFixed(2)}% <br /></>
|
| 144 |
-
)}
|
| 145 |
</p>
|
| 146 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 147 |
{summary.ai_interpretation || "No AI interpretation available."}
|
|
@@ -155,9 +153,6 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
|
|
| 155 |
return (
|
| 156 |
<div className="bg-gray-50 p-4 rounded-lg mb-6">
|
| 157 |
<h3 className="text-lg font-semibold text-gray-800 mb-2">AI Summary</h3>
|
| 158 |
-
<p className="text-gray-700 text-sm leading-relaxed">
|
| 159 |
-
<strong>Average Confidence:</strong> {typeof summary.avg_confidence !== 'undefined' ? `${summary.avg_confidence?.toFixed(2)}%` : "-"}
|
| 160 |
-
</p>
|
| 161 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 162 |
{summary.ai_interpretation || "No AI interpretation available."}
|
| 163 |
</div>
|
|
@@ -169,16 +164,13 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
|
|
| 169 |
return (
|
| 170 |
<div className="bg-gray-50 p-4 rounded-lg mb-6">
|
| 171 |
<h3 className="text-lg font-semibold text-gray-800 mb-2">AI Summary</h3>
|
| 172 |
-
|
| 173 |
{typeof summary.abnormal_cells !== 'undefined' && (
|
| 174 |
<><strong>Abnormal Cells:</strong> {summary.abnormal_cells} <br /></>
|
| 175 |
)}
|
| 176 |
{typeof summary.normal_cells !== 'undefined' && (
|
| 177 |
<><strong>Normal Cells:</strong> {summary.normal_cells} <br /></>
|
| 178 |
)}
|
| 179 |
-
{typeof summary.avg_confidence !== 'undefined' && (
|
| 180 |
-
<><strong>Average Confidence:</strong> {summary.avg_confidence?.toFixed(2)}% <br /></>
|
| 181 |
-
)}
|
| 182 |
</p>
|
| 183 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 184 |
{summary.ai_interpretation || "No AI interpretation available."}
|
|
@@ -212,20 +204,7 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
|
|
| 212 |
{model_used && /mwt|cin|histopathology/i.test(model_used) ? (
|
| 213 |
<div>
|
| 214 |
{/* Average confidence bar */}
|
| 215 |
-
|
| 216 |
-
<div className="flex items-center justify-between mb-1">
|
| 217 |
-
<span className="text-sm font-medium text-gray-700">Average confidence</span>
|
| 218 |
-
<span className="text-sm font-mono text-gray-600">
|
| 219 |
-
{summary?.avg_confidence ? `${summary.avg_confidence.toFixed(2)}%` : "-"}
|
| 220 |
-
</span>
|
| 221 |
-
</div>
|
| 222 |
-
<div className="w-full bg-gray-200 rounded-full h-4 overflow-hidden">
|
| 223 |
-
<div
|
| 224 |
-
className="h-4 bg-gradient-to-r from-amber-600 to-amber-400"
|
| 225 |
-
style={{ width: `${summary?.avg_confidence ?? 0}%` }}
|
| 226 |
-
/>
|
| 227 |
-
</div>
|
| 228 |
-
</div>
|
| 229 |
|
| 230 |
{/* Per-class bars */}
|
| 231 |
<div className="space-y-2">
|
|
|
|
| 139 |
{typeof summary.normal_cells !== 'undefined' && (
|
| 140 |
<><strong>Normal Cells:</strong> {summary.normal_cells} <br /></>
|
| 141 |
)}
|
| 142 |
+
{/* average confidence removed */}
|
|
|
|
|
|
|
| 143 |
</p>
|
| 144 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 145 |
{summary.ai_interpretation || "No AI interpretation available."}
|
|
|
|
| 153 |
return (
|
| 154 |
<div className="bg-gray-50 p-4 rounded-lg mb-6">
|
| 155 |
<h3 className="text-lg font-semibold text-gray-800 mb-2">AI Summary</h3>
|
|
|
|
|
|
|
|
|
|
| 156 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 157 |
{summary.ai_interpretation || "No AI interpretation available."}
|
| 158 |
</div>
|
|
|
|
| 164 |
return (
|
| 165 |
<div className="bg-gray-50 p-4 rounded-lg mb-6">
|
| 166 |
<h3 className="text-lg font-semibold text-gray-800 mb-2">AI Summary</h3>
|
| 167 |
+
<p className="text-gray-700 text-sm leading-relaxed">
|
| 168 |
{typeof summary.abnormal_cells !== 'undefined' && (
|
| 169 |
<><strong>Abnormal Cells:</strong> {summary.abnormal_cells} <br /></>
|
| 170 |
)}
|
| 171 |
{typeof summary.normal_cells !== 'undefined' && (
|
| 172 |
<><strong>Normal Cells:</strong> {summary.normal_cells} <br /></>
|
| 173 |
)}
|
|
|
|
|
|
|
|
|
|
| 174 |
</p>
|
| 175 |
<div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
|
| 176 |
{summary.ai_interpretation || "No AI interpretation available."}
|
|
|
|
| 204 |
{model_used && /mwt|cin|histopathology/i.test(model_used) ? (
|
| 205 |
<div>
|
| 206 |
{/* Average confidence bar */}
|
| 207 |
+
{/* Average confidence removed from visualization */}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
{/* Per-class bars */}
|
| 210 |
<div className="space-y-2">
|