File size: 4,577 Bytes
1a80759
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import gradio as gr
import cv2
import numpy as np
import math
from ultralytics import YOLO

# Load Model
MODEL_PATH = "best.pt"
model = YOLO(MODEL_PATH, task="pose")

def get_hip_points(image_crop, model, conf=0.25):
    results = model(image_crop, conf=conf, verbose=False)
    for r in results:
        if r.keypoints and r.keypoints.shape[1] >= 2:
            kpts = r.keypoints.xy[0].cpu().numpy().astype(int)
            valid_kpts = [p for p in kpts if p[0] != 0 and p[1] != 0]
            if len(valid_kpts) >= 2:
                valid_kpts.sort(key=lambda p: p[1])
                return valid_kpts[-1], valid_kpts[0]
    return None, None

def calculate_slope_angle(center, rim, other_center):
    try:
        if other_center[0] - center[0] == 0: m_h = 0
        else: m_h = (other_center[1] - center[1]) / (other_center[0] - center[0])
        if rim[0] - center[0] == 0: m_r = 1e9
        else: m_r = (rim[1] - center[1]) / (rim[0] - center[0])
        tan_theta = abs((m_r - m_h) / (1 + m_h * m_r))
        return math.degrees(math.atan(tan_theta))
    except: return 0.0

def get_diagnosis_style(angle):
    if angle >= 30: return "Dysplasia (High Severity)", "#FF4B4B"
    if angle >= 25: return "Borderline / Mild", "#FFA500"
    return "Normal", "#09AB3B"

def analyze_xray(input_img, conf):
    image = np.array(input_img)
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    h, w, _ = image.shape
    mid_x = w // 2
    
    right_hip_img = image[:, :mid_x]
    left_hip_img = image[:, mid_x:]
    
    r_tri, r_rim = get_hip_points(right_hip_img, model, conf)
    l_tri_local, l_rim_local = get_hip_points(left_hip_img, model, conf)
    
    if r_tri is None or l_tri_local is None:
        return image, "⚠️ **Error:** Could not detect anatomical landmarks. Try adjusting the confidence threshold."

    l_tri_global = (l_tri_local[0] + mid_x, l_tri_local[1])
    l_rim_global = (l_rim_local[0] + mid_x, l_rim_local[1])
    
    # Draw Hilgenreiner's line (Base Line)
    cv2.line(image, tuple(r_tri), tuple(l_tri_global), (0, 255, 255), 3)
    
    ang_r = calculate_slope_angle(r_tri, r_rim, l_tri_global)
    diag_r, color_r = get_diagnosis_style(ang_r)
    
    ang_l = calculate_slope_angle(l_tri_global, l_rim_global, r_tri)
    diag_l, color_l = get_diagnosis_style(ang_l)
    
    # Formatting the output text with Markdown
    result_text = f"### 🩺 Clinical Assessment Results\n\n"
    result_text += f"**Right Hip AI:** `{ang_r:.1f}°` ➔ **{diag_r}**\n\n"
    result_text += f"**Left Hip AI:** `{ang_l:.1f}°` ➔ **{diag_l}**"

    # Draw Roof Lines
    cv2.line(image, tuple(r_tri), tuple(r_rim), (0, 0, 255), 3)
    cv2.line(image, tuple(l_tri_global), tuple(l_rim_global), (0, 0, 255), 3)
    
    return cv2.cvtColor(image, cv2.COLOR_BGR2RGB), result_text

# ==========================================
# Gradio UI Design (Enhanced & English)
# ==========================================
custom_theme = gr.themes.Soft(
    primary_hue="blue",
    secondary_hue="slate",
)

with gr.Blocks(title="DislocPred: DDH System", theme=custom_theme) as demo:
    
    # Header Section
    with gr.Row():
        gr.Markdown(
            """
            # 🏥 DislocPred: AI Diagnostic Assistant
            **Automated Detection & Grading for Developmental Dysplasia of the Hip (DDH)** Upload a pediatric pelvic X-ray (AP view). The system will analyze the anatomical landmarks, calculate the Acetabular Index (AI), and provide a clinical evaluation.
            """
        )
    
    # Main Application Area
    with gr.Row():
        # Left Column: Inputs
        with gr.Column(scale=1):
            gr.Markdown("### 📥 Input Panel")
            input_img = gr.Image(type="pil", label="Upload X-ray Image")
            
            with gr.Accordion("⚙️ Advanced Model Settings", open=False):
                conf_slider = gr.Slider(0.1, 1.0, value=0.25, step=0.05, label="Detection Confidence Threshold")
                
            btn = gr.Button("🔍 Run AI Analysis", variant="primary")
            
        # Right Column: Outputs
        with gr.Column(scale=1):
            gr.Markdown("### 📤 Diagnostic Output")
            output_img = gr.Image(label="Annotated Radiograph")
            output_text = gr.Markdown(label="Results")

    # Connect logic to UI
    btn.click(
        fn=analyze_xray, 
        inputs=[input_img, conf_slider], 
        outputs=[output_img, output_text]
    )

# Launch with proper Hugging Face settings
if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=7860)