Spaces:
Sleeping
Sleeping
Commit
·
3f3aedd
1
Parent(s):
ac76f6e
Update
Browse files- backend/app.py +36 -18
backend/app.py
CHANGED
|
@@ -509,6 +509,8 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
|
|
| 509 |
content={"error": "CIN classifier not available on server."},
|
| 510 |
status_code=503,
|
| 511 |
)
|
|
|
|
|
|
|
| 512 |
nparr = np.frombuffer(contents, np.uint8)
|
| 513 |
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
| 514 |
results = yolo_colposcopy.predict(source=img, conf=0.7, save=False, verbose=False)
|
|
@@ -519,36 +521,52 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
|
|
| 519 |
crop = img[y1:y2, x1:x2]
|
| 520 |
crop = cv2.resize(crop, (224, 224))
|
| 521 |
img_t = transform(crop).unsqueeze(0).to(device)
|
|
|
|
|
|
|
| 522 |
f50 = extract_cbf_features(resnet50_blocks, img_t)
|
| 523 |
f101 = extract_cbf_features(resnet101_blocks, img_t)
|
| 524 |
f152 = extract_cbf_features(resnet152_blocks, img_t)
|
| 525 |
features = np.concatenate([f50, f101, f152]).reshape(1, -1)
|
|
|
|
|
|
|
| 526 |
X_scaled = MinMaxScaler().fit_transform(features)
|
| 527 |
-
pred = clf.predict(X_scaled)[0]
|
| 528 |
-
proba = clf.predict_proba(X_scaled)[0]
|
| 529 |
-
# Get actual number of classes from model output
|
| 530 |
-
classes = ["Low-grade", "High-grade"] # Binary CIN classification
|
| 531 |
-
predicted_label = classes[pred]
|
| 532 |
-
confidences = {classes[i]: float(proba[i]) for i in range(len(classes))}
|
| 533 |
-
|
| 534 |
-
# Map to more detailed classification based on confidence
|
| 535 |
-
if predicted_label == "High-grade" and confidences["High-grade"] > 0.8:
|
| 536 |
-
detailed_class = "CIN3"
|
| 537 |
-
elif predicted_label == "High-grade":
|
| 538 |
-
detailed_class = "CIN2"
|
| 539 |
-
else:
|
| 540 |
-
detailed_class = "CIN1"
|
| 541 |
|
| 542 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 543 |
avg_confidence = float(np.max(proba)) * 100
|
| 544 |
|
| 545 |
-
# Generate
|
| 546 |
ai_interp = generate_cin_summary(predicted_label, confidences, avg_confidence)
|
| 547 |
|
| 548 |
return {
|
| 549 |
"model_used": "CIN Classifier",
|
| 550 |
-
"prediction":
|
| 551 |
-
"grade": predicted_label,
|
| 552 |
"confidence": confidences,
|
| 553 |
"summary": {
|
| 554 |
"avg_confidence": round(avg_confidence, 2),
|
|
|
|
| 509 |
content={"error": "CIN classifier not available on server."},
|
| 510 |
status_code=503,
|
| 511 |
)
|
| 512 |
+
|
| 513 |
+
# Decode uploaded image and run colposcopy detector
|
| 514 |
nparr = np.frombuffer(contents, np.uint8)
|
| 515 |
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
| 516 |
results = yolo_colposcopy.predict(source=img, conf=0.7, save=False, verbose=False)
|
|
|
|
| 521 |
crop = img[y1:y2, x1:x2]
|
| 522 |
crop = cv2.resize(crop, (224, 224))
|
| 523 |
img_t = transform(crop).unsqueeze(0).to(device)
|
| 524 |
+
|
| 525 |
+
# Extract features from multiple ResNet backbones
|
| 526 |
f50 = extract_cbf_features(resnet50_blocks, img_t)
|
| 527 |
f101 = extract_cbf_features(resnet101_blocks, img_t)
|
| 528 |
f152 = extract_cbf_features(resnet152_blocks, img_t)
|
| 529 |
features = np.concatenate([f50, f101, f152]).reshape(1, -1)
|
| 530 |
+
|
| 531 |
+
# Scale and predict
|
| 532 |
X_scaled = MinMaxScaler().fit_transform(features)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
|
| 534 |
+
# Ensure classifier supports probability outputs
|
| 535 |
+
try:
|
| 536 |
+
proba = clf.predict_proba(X_scaled)[0]
|
| 537 |
+
except Exception as e:
|
| 538 |
+
return JSONResponse(
|
| 539 |
+
content={"error": "CIN classifier does not support probability estimates (predict_proba)."},
|
| 540 |
+
status_code=503,
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
num_classes = int(len(proba))
|
| 544 |
+
|
| 545 |
+
# Expecting a 3-class CIN classifier. If not 3, return a clear error so the user can supply a 3-class model.
|
| 546 |
+
if num_classes != 3:
|
| 547 |
+
return JSONResponse(
|
| 548 |
+
content={
|
| 549 |
+
"error": "CIN classifier must output 3-class probabilities (CIN1, CIN2, CIN3).",
|
| 550 |
+
"detected_num_classes": num_classes,
|
| 551 |
+
},
|
| 552 |
+
status_code=503,
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
# Map probabilities to explicit CIN labels (CIN1, CIN2, CIN3)
|
| 556 |
+
classes = ["CIN1", "CIN2", "CIN3"]
|
| 557 |
+
confidences = {classes[i]: float(proba[i]) for i in range(3)}
|
| 558 |
+
|
| 559 |
+
# Primary prediction is the class with the highest probability
|
| 560 |
+
predicted_idx = int(np.argmax(proba))
|
| 561 |
+
predicted_label = classes[predicted_idx]
|
| 562 |
avg_confidence = float(np.max(proba)) * 100
|
| 563 |
|
| 564 |
+
# Generate AI interpretation using Mistral client (if available)
|
| 565 |
ai_interp = generate_cin_summary(predicted_label, confidences, avg_confidence)
|
| 566 |
|
| 567 |
return {
|
| 568 |
"model_used": "CIN Classifier",
|
| 569 |
+
"prediction": predicted_label,
|
|
|
|
| 570 |
"confidence": confidences,
|
| 571 |
"summary": {
|
| 572 |
"avg_confidence": round(avg_confidence, 2),
|