Medisync / utils /skin_analysis.py
anique-1's picture
Deploy MediSync AI Backend with APK to Hugging Face
903c699
Raw
History Blame Contribute Delete
5.86 kB
import io
import base64
import numpy as np
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from .disease_knowledge import get_disease_info
MODEL_LABEL = "best.onnx (YOLOv12)"
CLASS_NAMES = [
"Darier_s-Disease", "Epidermolysis-Bullosa-Pruriginosa", "Hailey-Hailey-Disease",
"Hemangiome", "Impetigo", "Leishmanios", "Lupus-Erythematosus-Chronicus-Discoides",
"Melanoma", "Molluscum-Contagiosum", "Porokeratosis", "Psoriasis", "Tinea-Corporis",
"Tungiasis", "acne", "basal-cell-carcinoma", "eczema", "lichen", "nevus", "normal skin",
]
DISPLAY_NAMES = {
"Darier_s-Disease": "Darier's Disease",
"Epidermolysis-Bullosa-Pruriginosa": "Epidermolysis Bullosa Pruriginosa",
"Hailey-Hailey-Disease": "Hailey-Hailey Disease",
"Hemangiome": "Hemangioma",
"Impetigo": "Impetigo",
"Leishmanios": "Leishmaniasis",
"Lupus-Erythematosus-Chronicus-Discoides": "Discoid Lupus Erythematosus",
"Melanoma": "Melanoma",
"Molluscum-Contagiosum": "Molluscum Contagiosum",
"Porokeratosis": "Porokeratosis",
"Psoriasis": "Psoriasis",
"Tinea-Corporis": "Tinea Corporis (Ringworm)",
"Tungiasis": "Tungiasis",
"acne": "Acne",
"basal-cell-carcinoma": "Basal Cell Carcinoma",
"eczema": "Eczema",
"lichen": "Lichen Planus",
"nevus": "Nevus (Mole)",
"normal skin": "Normal Skin",
}
_ort_session = None
MODEL_PATH = Path(__file__).parent.parent / "best.onnx"
def get_ort_session():
global _ort_session
if _ort_session is None:
try:
import onnxruntime as ort
providers = ["CPUExecutionProvider"]
_ort_session = ort.InferenceSession(str(MODEL_PATH), providers=providers)
except Exception as e:
raise Exception(f"Failed to load CV model: {str(e)}. Ensure best.onnx exists in the backend folder.")
return _ort_session
def preprocess_image(image_bytes: bytes, input_size: int = 640) -> np.ndarray:
img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
w, h = img.size
scale = input_size / max(w, h)
new_w, new_h = int(w * scale), int(h * scale)
img = img.resize((new_w, new_h), Image.BILINEAR)
canvas = Image.new("RGB", (input_size, input_size), (114, 114, 114))
pad_x = (input_size - new_w) // 2
pad_y = (input_size - new_h) // 2
canvas.paste(img, (pad_x, pad_y))
arr = np.array(canvas, dtype=np.float32) / 255.0
arr = arr.transpose(2, 0, 1)
arr = np.expand_dims(arr, 0)
return arr
def parse_yolo_output(output: np.ndarray) -> tuple[str, float]:
num_classes = len(CLASS_NAMES)
out = output[0]
if out.ndim == 2 and out.shape[0] == (4 + num_classes):
class_scores = out[4:, :]
per_anchor_max = class_scores.max(axis=0)
best_anchor = int(np.argmax(per_anchor_max))
anchor_scores = class_scores[:, best_anchor]
elif out.ndim == 2 and out.shape[1] == (4 + num_classes):
class_scores = out[:, 4:]
per_anchor_max = class_scores.max(axis=1)
best_anchor = int(np.argmax(per_anchor_max))
anchor_scores = class_scores[best_anchor, :]
elif out.ndim == 1:
anchor_scores = out[:num_classes]
elif out.ndim == 2 and out.shape[1] == num_classes:
anchor_scores = out[0]
else:
flat = out.flatten()
anchor_scores = flat[:num_classes] if len(flat) >= num_classes else flat
exp_s = np.exp(anchor_scores - anchor_scores.max())
probs = exp_s / exp_s.sum()
class_idx = int(np.argmax(probs))
confidence = float(probs[class_idx])
class_idx = min(class_idx, num_classes - 1)
raw_name = CLASS_NAMES[class_idx]
display_name = DISPLAY_NAMES.get(raw_name, raw_name)
return display_name, round(confidence, 4)
CRITICAL_DISEASES = {"Melanoma", "Basal Cell Carcinoma"}
SEVERE_DISEASES = {"Discoid Lupus Erythematosus", "Darier's Disease", "Epidermolysis Bullosa Pruriginosa", "Hailey-Hailey Disease"}
def determine_severity_str(confidence: float, disease_name: str) -> str:
if disease_name in CRITICAL_DISEASES: return "critical"
if disease_name in SEVERE_DISEASES: return "severe" if confidence >= 0.5 else "moderate"
if disease_name == "Normal Skin": return "mild"
if confidence >= 0.85: return "severe"
elif confidence >= 0.65: return "moderate"
else: return "mild"
_PALETTE = [
(255, 80, 80), (80, 200, 80), (80, 120, 255), (255, 180, 0),
(200, 0, 200), (0, 210, 210), (255, 100, 0), (0, 150, 255),
(180, 255, 0), (255, 0, 120), (120, 0, 255), (0, 255, 150),
(255, 220, 50), (50, 255, 220), (200, 100, 50), (100, 200, 50),
(50, 100, 200), (200, 50, 100), (150, 150, 150),
]
def analyze_skin_image_core(image_bytes: bytes):
session = get_ort_session()
input_name = session.get_inputs()[0].name
input_tensor = preprocess_image(image_bytes)
outputs = session.run(None, {input_name: input_tensor})
disease_name, confidence = parse_yolo_output(outputs[0])
severity = determine_severity_str(confidence, disease_name)
disease_info = get_disease_info(disease_name)
recommendations = []
if "lifestyle" in disease_info.get("recommendations", {}):
recommendations.extend(disease_info["recommendations"]["lifestyle"])
suggested_medicines = [m["name"] for m in disease_info.get("medicines", [])]
precautions = disease_info.get("recommendations", {}).get("precautions", [])
return {
"condition": disease_name,
"confidence": confidence,
"severity": severity,
"description": disease_info["description"],
"recommendations": recommendations,
"suggestedMedicines": suggested_medicines,
"precautions": precautions,
"doctorConsultationUrgent": severity in ["severe", "critical"],
"boundingBox": None # Optional
}