omgy commited on
Commit
6f2b96e
·
verified ·
1 Parent(s): abf9e9b

Update stenosis.py

Browse files
Files changed (1) hide show
  1. stenosis.py +20 -84
stenosis.py CHANGED
@@ -1,108 +1,44 @@
1
- import cv2
2
  import numpy as np
3
- import base64
4
  from skimage.morphology import skeletonize
5
  from scipy.ndimage import distance_transform_edt
6
 
 
7
 
8
- # -------------------------------
9
- # Severity classification
10
- # -------------------------------
11
- def classify_severity(percent):
12
- if percent < 30:
13
- return "Mild"
14
- elif percent < 70:
15
- return "Moderate"
16
- else:
17
- return "Severe"
18
-
19
-
20
- # -------------------------------
21
- # Core stenosis detection
22
- # -------------------------------
23
- def detect_stenosis(binary_mask):
24
-
25
- mask = (binary_mask > 0).astype(np.uint8)
26
  skeleton = skeletonize(mask).astype(np.uint8)
27
  dist = distance_transform_edt(mask)
28
 
29
  coords = np.column_stack(np.where(skeleton > 0))
30
- if len(coords) < 20:
31
  return 0.0, None
32
 
33
  radii = np.array([dist[y, x] for y, x in coords])
34
 
35
- ref_radius = np.percentile(radii, 90)
 
 
 
36
  min_radius = radii.min()
37
 
38
- narrowing_percent = (1 - min_radius / ref_radius) * 100
 
 
 
 
 
39
 
40
- idx = np.argmin(radii)
41
  y, x = coords[idx]
42
 
43
- box = int(ref_radius * 4)
44
- h, w = mask.shape
45
 
 
46
  x1 = max(0, x - box)
47
  y1 = max(0, y - box)
48
  x2 = min(w, x + box)
49
  y2 = min(h, y + box)
50
 
51
- return float(narrowing_percent), (x1, y1, x2, y2)
52
-
53
-
54
- # -------------------------------
55
- # Draw bounding box
56
- # -------------------------------
57
- def draw_stenosis_box(image, bbox, percent):
58
-
59
- if len(image.shape) == 2:
60
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
61
-
62
- severity = classify_severity(percent)
63
- x1, y1, x2, y2 = bbox
64
-
65
- cv2.rectangle(image, (x1, y1), (x2, y2), (0, 0, 255), 3)
66
-
67
- label = f"{severity} ({percent:.1f}%)"
68
- (w, h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
69
-
70
- cv2.rectangle(image, (x1, y1 - h - 10), (x1 + w + 6, y1), (0, 0, 255), -1)
71
- cv2.putText(image, label, (x1 + 3, y1 - 4),
72
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
73
-
74
- return image
75
-
76
-
77
- # -------------------------------
78
- # Convert image → base64
79
- # -------------------------------
80
- def image_to_base64(image):
81
- _, buffer = cv2.imencode(".png", image)
82
- return base64.b64encode(buffer).decode("utf-8")
83
-
84
-
85
- # -------------------------------
86
- # MAIN API FUNCTION
87
- # -------------------------------
88
- def analyze_stenosis(original_image, vessel_mask):
89
-
90
- percent, bbox = detect_stenosis(vessel_mask)
91
-
92
- if bbox is None:
93
- return {
94
- "stenosis_detected": False,
95
- "max_narrowing_percent": 0.0,
96
- "severity": "None",
97
- "annotated_image_base64": None
98
- }
99
-
100
- severity = classify_severity(percent)
101
- annotated = draw_stenosis_box(original_image, bbox, percent)
102
-
103
- return {
104
- "stenosis_detected": True,
105
- "max_narrowing_percent": round(percent, 2),
106
- "severity": severity,
107
- "annotated_image_base64": image_to_base64(annotated)
108
- }
 
1
+ # stenosis.py
2
  import numpy as np
3
+ import cv2
4
  from skimage.morphology import skeletonize
5
  from scipy.ndimage import distance_transform_edt
6
 
7
+ def detect_stenosis(mask: np.ndarray):
8
 
9
+ mask = (mask > 0).astype(np.uint8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  skeleton = skeletonize(mask).astype(np.uint8)
11
  dist = distance_transform_edt(mask)
12
 
13
  coords = np.column_stack(np.where(skeleton > 0))
14
+ if len(coords) < 30:
15
  return 0.0, None
16
 
17
  radii = np.array([dist[y, x] for y, x in coords])
18
 
19
+ # smooth radius (VERY IMPORTANT)
20
+ radii = np.convolve(radii, np.ones(7)/7, mode="same")
21
+
22
+ ref_radius = np.percentile(radii, 85)
23
  min_radius = radii.min()
24
 
25
+ severity = float((1 - min_radius / ref_radius) * 100)
26
+
27
+ # pick worst region center
28
+ worst = np.where(radii < ref_radius * 0.6)[0]
29
+ if len(worst) == 0:
30
+ return severity, None
31
 
32
+ idx = worst[len(worst)//2]
33
  y, x = coords[idx]
34
 
35
+ # force visible bounding box
36
+ box = max(int(ref_radius * 8), 30)
37
 
38
+ h, w = mask.shape
39
  x1 = max(0, x - box)
40
  y1 = max(0, y - box)
41
  x2 = min(w, x + box)
42
  y2 = min(h, y + box)
43
 
44
+ return severity, (x1, y1, x2, y2)