ibrahim313 commited on
Commit
3a99d61
·
verified ·
1 Parent(s): 1c1375e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -131
app.py CHANGED
@@ -4,179 +4,183 @@ import pandas as pd
4
  import gradio as gr
5
  import matplotlib.pyplot as plt
6
  from datetime import datetime
7
- from sklearn.cluster import DBSCAN
8
- from scipy import ndimage
9
 
10
  class BloodCellAnalyzer:
11
  def __init__(self):
12
- self.min_cell_area = 100
13
- self.max_cell_area = 5000
14
- self.min_circularity = 0.7
15
-
16
- def preprocess_image(self, image):
17
- """Enhanced image preprocessing with multiple color spaces and filtering."""
 
 
 
 
 
 
 
18
  if len(image.shape) == 2:
19
  image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
20
-
21
- # Convert to multiple color spaces for robust detection
22
  hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
23
  lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB)
24
- gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
25
 
26
- # Create masks for different color ranges
27
- hsv_mask = cv2.inRange(hsv, np.array([0, 20, 20]), np.array([180, 255, 255]))
28
- lab_mask = cv2.inRange(lab, np.array([20, 120, 120]), np.array([200, 140, 140]))
 
 
29
 
30
- # Combine masks
31
- combined_mask = cv2.bitwise_or(hsv_mask, lab_mask)
 
32
 
33
- # Apply advanced morphological operations
34
- kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
35
- clean_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel, iterations=2)
36
- clean_mask = cv2.morphologyEx(clean_mask, cv2.MORPH_OPEN, kernel, iterations=1)
37
 
38
- # Apply distance transform to separate touching cells
39
- dist_transform = cv2.distanceTransform(clean_mask, cv2.DIST_L2, 5)
40
- _, sure_fg = cv2.threshold(dist_transform, 0.5 * dist_transform.max(), 255, 0)
 
 
 
41
 
42
- return clean_mask.astype(np.uint8), sure_fg.astype(np.uint8)
43
-
44
- def extract_cell_features(self, contour):
45
- """Extract comprehensive features for each detected cell."""
46
- area = cv2.contourArea(contour)
47
- perimeter = cv2.arcLength(contour, True)
48
- circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0
49
-
50
- # Calculate additional shape features
51
- hull = cv2.convexHull(contour)
52
- hull_area = cv2.contourArea(hull)
53
- solidity = float(area) / hull_area if hull_area > 0 else 0
54
-
55
- # Calculate moments and orientation
56
- moments = cv2.moments(contour)
57
- cx = int(moments['m10'] / moments['m00']) if moments['m00'] != 0 else 0
58
- cy = int(moments['m01'] / moments['m00']) if moments['m00'] != 0 else 0
59
-
60
- # Calculate eccentricity using ellipse fitting
61
- if len(contour) >= 5:
62
- (x, y), (MA, ma), angle = cv2.fitEllipse(contour)
63
- eccentricity = np.sqrt(1 - (ma / MA) ** 2) if MA > 0 else 0
64
- else:
65
- eccentricity = 0
66
- angle = 0
67
-
68
- return {
69
- 'area': area,
70
- 'perimeter': perimeter,
71
- 'circularity': circularity,
72
- 'solidity': solidity,
73
- 'eccentricity': eccentricity,
74
- 'orientation': angle,
75
- 'centroid_x': cx,
76
- 'centroid_y': cy
77
- }
78
-
79
- def detect_cells(self, image):
80
- """Detect and analyze blood cells with advanced filtering."""
81
- mask, sure_fg = self.preprocess_image(image)
82
 
83
- # Find contours
84
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
85
-
86
- # Extract features and filter cells
87
  cells = []
88
  valid_contours = []
89
 
90
- for i, contour in enumerate(contours):
91
- features = self.extract_cell_features(contour)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- # Apply multiple criteria for cell validation
94
- if (self.min_cell_area < features['area'] < self.max_cell_area and
95
- features['circularity'] > self.min_circularity and
96
- features['solidity'] > 0.8):
97
-
98
- features['label'] = i + 1
99
- cells.append(features)
100
- valid_contours.append(contour)
101
-
102
- return valid_contours, cells, mask
 
 
 
 
 
 
103
 
104
  def analyze_image(self, image):
105
- """Perform comprehensive image analysis and generate visualizations."""
106
  if image is None:
107
  return None, None, None, None
108
 
109
- # Detect cells and extract features
110
  contours, cells, mask = self.detect_cells(image)
111
  vis_img = image.copy()
112
 
113
- # Draw detections and labels
114
  for cell in cells:
115
  contour = contours[cell['label'] - 1]
116
- cv2.drawContours(vis_img, [contour], -1, (0, 255, 0), 2)
117
- cv2.putText(vis_img, str(cell['label']),
 
118
  (cell['centroid_x'], cell['centroid_y']),
119
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
120
 
121
- # Create DataFrame and calculate summary statistics
122
  df = pd.DataFrame(cells)
 
 
123
  if not df.empty:
 
 
 
124
  summary_stats = {
125
- 'total_cells': len(cells),
126
- 'avg_cell_size': df['area'].mean(),
127
- 'std_cell_size': df['area'].std(),
128
- 'avg_circularity': df['circularity'].mean(),
129
- 'cell_density': len(cells) / (image.shape[0] * image.shape[1])
130
  }
131
- df = df.assign(**{k: [v] * len(df) for k, v in summary_stats.items()})
 
 
 
132
 
133
- # Generate visualizations
134
  fig = self.generate_analysis_plots(df)
135
 
136
  return vis_img, mask, fig, df
137
 
138
  def generate_analysis_plots(self, df):
139
- """Generate comprehensive analysis plots."""
140
  if df.empty:
141
  return None
142
 
143
  plt.style.use('dark_background')
144
- fig = plt.figure(figsize=(15, 10))
145
-
146
- # Create subplot grid
147
- gs = plt.GridSpec(2, 3, figure=fig)
148
- ax1 = fig.add_subplot(gs[0, 0])
149
- ax2 = fig.add_subplot(gs[0, 1])
150
- ax3 = fig.add_subplot(gs[0, 2])
151
- ax4 = fig.add_subplot(gs[1, :])
152
-
153
- # Cell size distribution
154
- ax1.hist(df['area'], bins=20, color='cyan', edgecolor='black')
155
- ax1.set_title('Cell Size Distribution')
156
- ax1.set_xlabel('Area')
157
- ax1.set_ylabel('Count')
158
-
159
- # Area vs Circularity
160
- scatter = ax2.scatter(df['area'], df['circularity'],
161
- c=df['solidity'], cmap='viridis', alpha=0.6)
162
- ax2.set_title('Area vs Circularity')
163
- ax2.set_xlabel('Area')
164
- ax2.set_ylabel('Circularity')
165
- plt.colorbar(scatter, ax=ax2, label='Solidity')
166
-
167
- # Eccentricity distribution
168
- ax3.hist(df['eccentricity'], bins=15, color='magenta', edgecolor='black')
169
- ax3.set_title('Eccentricity Distribution')
170
- ax3.set_xlabel('Eccentricity')
171
- ax3.set_ylabel('Count')
172
-
173
- # Cell position scatter plot
174
- scatter = ax4.scatter(df['centroid_x'], df['centroid_y'],
175
- c=df['area'], cmap='plasma', alpha=0.6, s=100)
176
- ax4.set_title('Cell Positions and Sizes')
177
- ax4.set_xlabel('X Position')
178
- ax4.set_ylabel('Y Position')
179
- plt.colorbar(scatter, ax=ax4, label='Cell Area')
180
 
181
  plt.tight_layout()
182
  return fig
@@ -193,7 +197,7 @@ demo = gr.Interface(
193
  gr.DataFrame(label="Cell Data")
194
  ],
195
  title="Blood Cell Analysis Tool",
196
- description="Upload an image to analyze blood cells and extract features."
197
  )
198
 
199
  if __name__ == "__main__":
 
4
  import gradio as gr
5
  import matplotlib.pyplot as plt
6
  from datetime import datetime
 
 
7
 
8
  class BloodCellAnalyzer:
9
  def __init__(self):
10
+ # Adjusted parameters for the specific image characteristics
11
+ self.min_rbc_area = 400
12
+ self.max_rbc_area = 2000
13
+ self.min_wbc_area = 500
14
+ self.max_wbc_area = 3000
15
+ self.min_circularity = 0.75
16
+
17
+ def detect_cells(self, image):
18
+ """Detect both red and white blood cells using color-based segmentation."""
19
+ if image is None:
20
+ return None, [], None
21
+
22
+ # Convert to RGB if grayscale
23
  if len(image.shape) == 2:
24
  image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
25
+
26
+ # Convert to different color spaces
27
  hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
28
  lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB)
 
29
 
30
+ # Red blood cell detection (red color range)
31
+ lower_red1 = np.array([0, 50, 50])
32
+ upper_red1 = np.array([10, 255, 255])
33
+ lower_red2 = np.array([160, 50, 50])
34
+ upper_red2 = np.array([180, 255, 255])
35
 
36
+ red_mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
37
+ red_mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
38
+ red_mask = cv2.bitwise_or(red_mask1, red_mask2)
39
 
40
+ # White blood cell detection (blue color range)
41
+ lower_blue = np.array([90, 50, 50])
42
+ upper_blue = np.array([130, 255, 255])
43
+ blue_mask = cv2.inRange(hsv, lower_blue, upper_blue)
44
 
45
+ # Enhance masks
46
+ kernel = np.ones((3,3), np.uint8)
47
+ red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_OPEN, kernel, iterations=1)
48
+ red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_CLOSE, kernel, iterations=1)
49
+ blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_OPEN, kernel, iterations=1)
50
+ blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_CLOSE, kernel, iterations=1)
51
 
52
+ # Find contours for both cell types
53
+ rbc_contours, _ = cv2.findContours(red_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
54
+ wbc_contours, _ = cv2.findContours(blue_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
 
 
 
 
56
  cells = []
57
  valid_contours = []
58
 
59
+ # Process RBCs
60
+ for i, contour in enumerate(rbc_contours):
61
+ area = cv2.contourArea(contour)
62
+ perimeter = cv2.arcLength(contour, True)
63
+ circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
64
+
65
+ if (self.min_rbc_area < area < self.max_rbc_area and
66
+ circularity > self.min_circularity):
67
+ M = cv2.moments(contour)
68
+ if M["m00"] != 0:
69
+ cx = int(M["m10"] / M["m00"])
70
+ cy = int(M["m01"] / M["m00"])
71
+ cells.append({
72
+ 'label': len(valid_contours) + 1,
73
+ 'type': 'RBC',
74
+ 'area': area,
75
+ 'circularity': circularity,
76
+ 'centroid_x': cx,
77
+ 'centroid_y': cy
78
+ })
79
+ valid_contours.append(contour)
80
+
81
+ # Process WBCs
82
+ for i, contour in enumerate(wbc_contours):
83
+ area = cv2.contourArea(contour)
84
+ perimeter = cv2.arcLength(contour, True)
85
+ circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
86
 
87
+ if (self.min_wbc_area < area < self.max_wbc_area):
88
+ M = cv2.moments(contour)
89
+ if M["m00"] != 0:
90
+ cx = int(M["m10"] / M["m00"])
91
+ cy = int(M["m01"] / M["m00"])
92
+ cells.append({
93
+ 'label': len(valid_contours) + 1,
94
+ 'type': 'WBC',
95
+ 'area': area,
96
+ 'circularity': circularity,
97
+ 'centroid_x': cx,
98
+ 'centroid_y': cy
99
+ })
100
+ valid_contours.append(contour)
101
+
102
+ return valid_contours, cells, red_mask
103
 
104
  def analyze_image(self, image):
105
+ """Analyze the blood cell image and generate visualizations."""
106
  if image is None:
107
  return None, None, None, None
108
 
109
+ # Detect cells
110
  contours, cells, mask = self.detect_cells(image)
111
  vis_img = image.copy()
112
 
113
+ # Draw detections
114
  for cell in cells:
115
  contour = contours[cell['label'] - 1]
116
+ color = (0, 0, 255) if cell['type'] == 'RBC' else (255, 0, 0)
117
+ cv2.drawContours(vis_img, [contour], -1, color, 2)
118
+ cv2.putText(vis_img, f"{cell['type']}",
119
  (cell['centroid_x'], cell['centroid_y']),
120
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
121
 
122
+ # Create DataFrame
123
  df = pd.DataFrame(cells)
124
+
125
+ # Generate summary statistics
126
  if not df.empty:
127
+ rbc_count = len(df[df['type'] == 'RBC'])
128
+ wbc_count = len(df[df['type'] == 'WBC'])
129
+
130
  summary_stats = {
131
+ 'total_rbc': rbc_count,
132
+ 'total_wbc': wbc_count,
133
+ 'rbc_avg_size': df[df['type'] == 'RBC']['area'].mean() if rbc_count > 0 else 0,
134
+ 'wbc_avg_size': df[df['type'] == 'WBC']['area'].mean() if wbc_count > 0 else 0,
 
135
  }
136
+
137
+ # Add summary stats to DataFrame
138
+ for k, v in summary_stats.items():
139
+ df[k] = v
140
 
141
+ # Generate visualization
142
  fig = self.generate_analysis_plots(df)
143
 
144
  return vis_img, mask, fig, df
145
 
146
  def generate_analysis_plots(self, df):
147
+ """Generate analysis plots for the detected cells."""
148
  if df.empty:
149
  return None
150
 
151
  plt.style.use('dark_background')
152
+ fig, axes = plt.subplots(2, 2, figsize=(12, 10))
153
+
154
+ # Cell count by type
155
+ cell_counts = df['type'].value_counts()
156
+ axes[0, 0].bar(cell_counts.index, cell_counts.values, color=['red', 'blue'])
157
+ axes[0, 0].set_title('Cell Count by Type')
158
+
159
+ # Size distribution
160
+ for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']):
161
+ if len(df[df['type'] == cell_type]) > 0:
162
+ axes[0, 1].hist(df[df['type'] == cell_type]['area'],
163
+ bins=20, alpha=0.5, color=color, label=cell_type)
164
+ axes[0, 1].set_title('Cell Size Distribution')
165
+ axes[0, 1].legend()
166
+
167
+ # Circularity by type
168
+ for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']):
169
+ cell_data = df[df['type'] == cell_type]
170
+ if len(cell_data) > 0:
171
+ axes[1, 0].scatter(cell_data['area'], cell_data['circularity'],
172
+ c=color, label=cell_type, alpha=0.6)
173
+ axes[1, 0].set_title('Area vs Circularity')
174
+ axes[1, 0].legend()
175
+
176
+ # Spatial distribution
177
+ for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']):
178
+ cell_data = df[df['type'] == cell_type]
179
+ if len(cell_data) > 0:
180
+ axes[1, 1].scatter(cell_data['centroid_x'], cell_data['centroid_y'],
181
+ c=color, label=cell_type, alpha=0.6)
182
+ axes[1, 1].set_title('Spatial Distribution')
183
+ axes[1, 1].legend()
 
 
 
 
184
 
185
  plt.tight_layout()
186
  return fig
 
197
  gr.DataFrame(label="Cell Data")
198
  ],
199
  title="Blood Cell Analysis Tool",
200
+ description="Upload an image to analyze red and white blood cells."
201
  )
202
 
203
  if __name__ == "__main__":