Chayanat commited on
Commit
2e1f4b0
·
verified ·
1 Parent(s): 8ee2e62

เพิ่มตรวจจับเอียง test 001

Browse files
Files changed (1) hide show
  1. app.py +100 -26
app.py CHANGED
@@ -49,6 +49,44 @@ def getMasks(landmarks, h, w):
49
  return RL_mask, LL_mask, H_mask
50
 
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  def drawOnTop(img, landmarks, original_shape):
53
  h, w = original_shape
54
  output = getDenseMask(landmarks, h, w)
@@ -62,6 +100,26 @@ def drawOnTop(img, landmarks, original_shape):
62
 
63
  RL, LL, H = landmarks[0:44], landmarks[44:94], landmarks[94:]
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  # Draw the landmarks as dots
66
  for l in RL:
67
  image = cv2.circle(image, (int(l[0]), int(l[1])), 5, (1, 0, 1), -1)
@@ -70,11 +128,11 @@ def drawOnTop(img, landmarks, original_shape):
70
  for l in H:
71
  image = cv2.circle(image, (int(l[0]), int(l[1])), 5, (1, 1, 0), -1)
72
 
73
- # Draw horizontal lines for CTR calculation
74
- # Heart (red line)
75
- heart_xmin = int(np.min(H[:, 0]))
76
- heart_xmax = int(np.max(H[:, 0]))
77
- heart_y = int(np.mean([H[np.argmin(H[:, 0]), 1], H[np.argmax(H[:, 0]), 1]]))
78
  image = cv2.line(image, (heart_xmin, heart_y), (heart_xmax, heart_y), (1, 0, 0), 2)
79
 
80
  # Add vertical lines at heart endpoints to verify widest points
@@ -82,24 +140,27 @@ def drawOnTop(img, landmarks, original_shape):
82
  image = cv2.line(image, (heart_xmin, heart_y - line_length), (heart_xmin, heart_y + line_length), (1, 0, 0), 2)
83
  image = cv2.line(image, (heart_xmax, heart_y - line_length), (heart_xmax, heart_y + line_length), (1, 0, 0), 2)
84
 
85
- # Thorax (blue line)
86
- thorax_xmin = int(min(np.min(RL[:, 0]), np.min(LL[:, 0])))
87
- thorax_xmax = int(max(np.max(RL[:, 0]), np.max(LL[:, 0])))
88
  # Find y at leftmost and rightmost points
89
- if np.min(RL[:, 0]) < np.min(LL[:, 0]):
90
- thorax_ymin = RL[np.argmin(RL[:, 0]), 1]
91
  else:
92
- thorax_ymin = LL[np.argmin(LL[:, 0]), 1]
93
- if np.max(RL[:, 0]) > np.max(LL[:, 0]):
94
- thorax_ymax = RL[np.argmax(RL[:, 0]), 1]
95
  else:
96
- thorax_ymax = LL[np.argmax(LL[:, 0]), 1]
97
  thorax_y = int(np.mean([thorax_ymin, thorax_ymax]))
98
  image = cv2.line(image, (thorax_xmin, thorax_y), (thorax_xmax, thorax_y), (0, 0, 1), 2)
99
 
100
  # Add vertical lines at thorax endpoints to verify widest points
101
  image = cv2.line(image, (thorax_xmin, thorax_y - line_length), (thorax_xmin, thorax_y + line_length), (0, 0, 1), 2)
102
  image = cv2.line(image, (thorax_xmax, thorax_y - line_length), (thorax_xmax, thorax_y + line_length), (0, 0, 1), 2)
 
 
 
103
 
104
  return image
105
 
@@ -186,14 +247,19 @@ def removePreprocess(output, info):
186
  return output
187
 
188
 
189
- def calculate_ctr(landmarks):
190
- H = landmarks[94:]
191
- RL = landmarks[0:44]
192
- LL = landmarks[44:94]
 
 
 
 
 
193
  cardiac_width = np.max(H[:, 0]) - np.min(H[:, 0])
194
  thoracic_width = max(np.max(RL[:, 0]), np.max(LL[:, 0])) - min(np.min(RL[:, 0]), np.min(LL[:, 0]))
195
  ctr = cardiac_width / thoracic_width if thoracic_width > 0 else 0
196
- return round(ctr, 3)
197
 
198
 
199
  def segment(input_img):
@@ -216,22 +282,30 @@ def segment(input_img):
216
 
217
  output = output.astype('int')
218
 
219
- outseg = drawOnTop(input_img, output, original_shape)
220
 
221
  seg_to_save = (outseg.copy() * 255).astype('uint8')
222
  cv2.imwrite("tmp/overlap_segmentation.png", cv2.cvtColor(seg_to_save, cv2.COLOR_RGB2BGR))
223
 
224
- ctr_value = calculate_ctr(output)
 
 
 
 
 
 
 
 
225
  if ctr_value < 0.5:
226
- interpretation = "Normal"
227
  elif 0.51 <= ctr_value <= 0.55:
228
- interpretation = "Mild Cardiomegaly (CTR 51-55%)"
229
  elif 0.56 <= ctr_value <= 0.60:
230
- interpretation = "Moderate Cardiomegaly (CTR 56-60%)"
231
  elif ctr_value > 0.60:
232
- interpretation = "Severe Cardiomegaly (CTR > 60%)"
233
  else:
234
- interpretation = "Cardiomegaly"
235
 
236
  return outseg, "tmp/overlap_segmentation.png", ctr_value, interpretation
237
 
 
49
  return RL_mask, LL_mask, H_mask
50
 
51
 
52
+ def calculate_image_tilt(landmarks):
53
+ """Calculate image tilt angle based on lung symmetry"""
54
+ RL = landmarks[0:44] # Right lung
55
+ LL = landmarks[44:94] # Left lung
56
+
57
+ # Find the topmost points of both lungs
58
+ rl_top_idx = np.argmin(RL[:, 1])
59
+ ll_top_idx = np.argmin(LL[:, 1])
60
+
61
+ rl_top = RL[rl_top_idx]
62
+ ll_top = LL[ll_top_idx]
63
+
64
+ # Calculate angle between the line connecting lung tops and horizontal
65
+ dx = ll_top[0] - rl_top[0]
66
+ dy = ll_top[1] - rl_top[1]
67
+
68
+ angle_rad = np.arctan2(dy, dx)
69
+ angle_deg = np.degrees(angle_rad)
70
+
71
+ return angle_deg, rl_top, ll_top
72
+
73
+ def rotate_points(points, angle_deg, center):
74
+ """Rotate points around a center by given angle"""
75
+ angle_rad = np.radians(-angle_deg) # Negative to correct the tilt
76
+ cos_a = np.cos(angle_rad)
77
+ sin_a = np.sin(angle_rad)
78
+
79
+ # Translate to origin
80
+ translated = points - center
81
+
82
+ # Rotate
83
+ rotated = np.zeros_like(translated)
84
+ rotated[:, 0] = translated[:, 0] * cos_a - translated[:, 1] * sin_a
85
+ rotated[:, 1] = translated[:, 0] * sin_a + translated[:, 1] * cos_a
86
+
87
+ # Translate back
88
+ return rotated + center
89
+
90
  def drawOnTop(img, landmarks, original_shape):
91
  h, w = original_shape
92
  output = getDenseMask(landmarks, h, w)
 
100
 
101
  RL, LL, H = landmarks[0:44], landmarks[44:94], landmarks[94:]
102
 
103
+ # Calculate image tilt and correct it for measurements
104
+ tilt_angle, rl_top, ll_top = calculate_image_tilt(landmarks)
105
+ image_center = np.array([w/2, h/2])
106
+
107
+ # Draw tilt reference line (green)
108
+ image = cv2.line(image, (int(rl_top[0]), int(rl_top[1])), (int(ll_top[0]), int(ll_top[1])), (0, 1, 0), 1)
109
+
110
+ # Add tilt angle text
111
+ tilt_text = f"Tilt: {tilt_angle:.1f}°"
112
+ cv2.putText(image, tilt_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 1, 0), 2)
113
+
114
+ # Correct landmarks for tilt
115
+ if abs(tilt_angle) > 2: # Only correct if tilt is significant
116
+ RL_corrected = rotate_points(RL, tilt_angle, image_center)
117
+ LL_corrected = rotate_points(LL, tilt_angle, image_center)
118
+ H_corrected = rotate_points(H, tilt_angle, image_center)
119
+ cv2.putText(image, "Tilt Corrected", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (1, 1, 0), 2)
120
+ else:
121
+ RL_corrected, LL_corrected, H_corrected = RL, LL, H
122
+
123
  # Draw the landmarks as dots
124
  for l in RL:
125
  image = cv2.circle(image, (int(l[0]), int(l[1])), 5, (1, 0, 1), -1)
 
128
  for l in H:
129
  image = cv2.circle(image, (int(l[0]), int(l[1])), 5, (1, 1, 0), -1)
130
 
131
+ # Draw horizontal lines for CTR calculation using corrected landmarks
132
+ # Heart (red line) - using corrected coordinates
133
+ heart_xmin = int(np.min(H_corrected[:, 0]))
134
+ heart_xmax = int(np.max(H_corrected[:, 0]))
135
+ heart_y = int(np.mean([H_corrected[np.argmin(H_corrected[:, 0]), 1], H_corrected[np.argmax(H_corrected[:, 0]), 1]]))
136
  image = cv2.line(image, (heart_xmin, heart_y), (heart_xmax, heart_y), (1, 0, 0), 2)
137
 
138
  # Add vertical lines at heart endpoints to verify widest points
 
140
  image = cv2.line(image, (heart_xmin, heart_y - line_length), (heart_xmin, heart_y + line_length), (1, 0, 0), 2)
141
  image = cv2.line(image, (heart_xmax, heart_y - line_length), (heart_xmax, heart_y + line_length), (1, 0, 0), 2)
142
 
143
+ # Thorax (blue line) - using corrected coordinates
144
+ thorax_xmin = int(min(np.min(RL_corrected[:, 0]), np.min(LL_corrected[:, 0])))
145
+ thorax_xmax = int(max(np.max(RL_corrected[:, 0]), np.max(LL_corrected[:, 0])))
146
  # Find y at leftmost and rightmost points
147
+ if np.min(RL_corrected[:, 0]) < np.min(LL_corrected[:, 0]):
148
+ thorax_ymin = RL_corrected[np.argmin(RL_corrected[:, 0]), 1]
149
  else:
150
+ thorax_ymin = LL_corrected[np.argmin(LL_corrected[:, 0]), 1]
151
+ if np.max(RL_corrected[:, 0]) > np.max(LL_corrected[:, 0]):
152
+ thorax_ymax = RL_corrected[np.argmax(RL_corrected[:, 0]), 1]
153
  else:
154
+ thorax_ymax = LL_corrected[np.argmax(LL_corrected[:, 0]), 1]
155
  thorax_y = int(np.mean([thorax_ymin, thorax_ymax]))
156
  image = cv2.line(image, (thorax_xmin, thorax_y), (thorax_xmax, thorax_y), (0, 0, 1), 2)
157
 
158
  # Add vertical lines at thorax endpoints to verify widest points
159
  image = cv2.line(image, (thorax_xmin, thorax_y - line_length), (thorax_xmin, thorax_y + line_length), (0, 0, 1), 2)
160
  image = cv2.line(image, (thorax_xmax, thorax_y - line_length), (thorax_xmax, thorax_y + line_length), (0, 0, 1), 2)
161
+
162
+ # Store corrected landmarks for CTR calculation
163
+ return image, (RL_corrected, LL_corrected, H_corrected, tilt_angle)
164
 
165
  return image
166
 
 
247
  return output
248
 
249
 
250
+ def calculate_ctr(landmarks, corrected_landmarks=None):
251
+ if corrected_landmarks is not None:
252
+ RL, LL, H, tilt_angle = corrected_landmarks
253
+ else:
254
+ H = landmarks[94:]
255
+ RL = landmarks[0:44]
256
+ LL = landmarks[44:94]
257
+ tilt_angle = 0
258
+
259
  cardiac_width = np.max(H[:, 0]) - np.min(H[:, 0])
260
  thoracic_width = max(np.max(RL[:, 0]), np.max(LL[:, 0])) - min(np.min(RL[:, 0]), np.min(LL[:, 0]))
261
  ctr = cardiac_width / thoracic_width if thoracic_width > 0 else 0
262
+ return round(ctr, 3), abs(tilt_angle)
263
 
264
 
265
  def segment(input_img):
 
282
 
283
  output = output.astype('int')
284
 
285
+ outseg, corrected_data = drawOnTop(input_img, output, original_shape)
286
 
287
  seg_to_save = (outseg.copy() * 255).astype('uint8')
288
  cv2.imwrite("tmp/overlap_segmentation.png", cv2.cvtColor(seg_to_save, cv2.COLOR_RGB2BGR))
289
 
290
+ ctr_value, tilt_angle = calculate_ctr(output, corrected_data)
291
+
292
+ # Add tilt warning to interpretation
293
+ tilt_warning = ""
294
+ if tilt_angle > 5:
295
+ tilt_warning = f" (⚠️ Image tilted {tilt_angle:.1f}° - measurement corrected)"
296
+ elif tilt_angle > 2:
297
+ tilt_warning = f" (Image tilted {tilt_angle:.1f}° - corrected)"
298
+
299
  if ctr_value < 0.5:
300
+ interpretation = f"Normal{tilt_warning}"
301
  elif 0.51 <= ctr_value <= 0.55:
302
+ interpretation = f"Mild Cardiomegaly (CTR 51-55%){tilt_warning}"
303
  elif 0.56 <= ctr_value <= 0.60:
304
+ interpretation = f"Moderate Cardiomegaly (CTR 56-60%){tilt_warning}"
305
  elif ctr_value > 0.60:
306
+ interpretation = f"Severe Cardiomegaly (CTR > 60%){tilt_warning}"
307
  else:
308
+ interpretation = f"Cardiomegaly{tilt_warning}"
309
 
310
  return outseg, "tmp/overlap_segmentation.png", ctr_value, interpretation
311