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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -191
app.py CHANGED
@@ -5,200 +5,105 @@ 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
187
-
188
- # Create Gradio interface
189
- analyzer = BloodCellAnalyzer()
190
  demo = gr.Interface(
191
- fn=analyzer.analyze_image,
192
  inputs=gr.Image(type="numpy"),
193
  outputs=[
194
- gr.Image(label="Detected Cells"),
195
- gr.Image(label="Segmentation Mask"),
196
  gr.Plot(label="Analysis Plots"),
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__":
204
- demo.launch()
 
5
  import matplotlib.pyplot as plt
6
  from datetime import datetime
7
 
8
+ def preprocess_image(image):
9
+ """Enhance image contrast, apply thresholding, and clean noise."""
10
+ if len(image.shape) == 2:
11
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
12
+
13
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
14
+
15
+ # Apply Gaussian blur to remove noise
16
+ blurred = cv2.GaussianBlur(gray, (5, 5), 0)
17
+
18
+ # Otsu's Thresholding (more robust than adaptive for blood cells)
19
+ _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
20
+
21
+ # Morphological operations to improve segmentation
22
+ kernel = np.ones((3, 3), np.uint8)
23
+ clean_mask = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2)
24
+ clean_mask = cv2.morphologyEx(clean_mask, cv2.MORPH_OPEN, kernel, iterations=1)
25
+
26
+ return clean_mask
27
+
28
+ def detect_blood_cells(image):
29
+ """Detect blood cells and extract features."""
30
+ mask = preprocess_image(image)
31
+ contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
32
+
33
+ features = []
34
+ total_area = 0
35
+
36
+ for i, contour in enumerate(contours, 1):
37
+ area = cv2.contourArea(contour)
38
+ perimeter = cv2.arcLength(contour, True)
39
+ circularity = (4 * np.pi * area / (perimeter * perimeter)) if perimeter > 0 else 0
40
+
41
+ # Filtering: Only count reasonable-sized circular objects
42
+ if 100 < area < 5000 and circularity > 0.7:
43
+ M = cv2.moments(contour)
44
+ if M["m00"] != 0:
45
+ cx = int(M["m10"] / M["m00"])
46
+ cy = int(M["m01"] / M["m00"])
47
+ features.append({
48
+ 'ID': i, 'Area': area, 'Perimeter': perimeter,
49
+ 'Circularity': circularity, 'Centroid_X': cx, 'Centroid_Y': cy
50
+ })
51
+ total_area += area
52
+
53
+ # Summary Statistics
54
+ avg_cell_size = total_area / len(features) if features else 0
55
+ cell_density = len(features) / (image.shape[0] * image.shape[1]) # Density per pixel
56
+
57
+ summary = {
58
+ 'Total Cells': len(features),
59
+ 'Avg Cell Size': avg_cell_size,
60
+ 'Cell Density': cell_density
61
+ }
62
+
63
+ return contours, features, mask, summary
64
+
65
+ def process_image(image):
66
+ if image is None:
67
+ return None, None, None, None, None
68
+
69
+ contours, features, mask, summary = detect_blood_cells(image)
70
+ vis_img = image.copy()
71
+
72
+ for feature in features:
73
+ contour = contours[feature['ID'] - 1]
74
+ cv2.drawContours(vis_img, [contour], -1, (0, 255, 0), 2)
75
+ cv2.putText(vis_img, str(feature['ID']), (feature['Centroid_X'], feature['Centroid_Y']),
76
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
77
+
78
+ df = pd.DataFrame(features)
79
+ return vis_img, mask, df, summary
80
+
81
+ def analyze(image):
82
+ vis_img, mask, df, summary = process_image(image)
83
+
84
+ plt.style.use('dark_background')
85
+ fig, axes = plt.subplots(1, 2, figsize=(12, 5))
86
+
87
+ if not df.empty:
88
+ axes[0].hist(df['Area'], bins=20, color='cyan', edgecolor='black')
89
+ axes[0].set_title('Cell Size Distribution')
90
+
91
+ axes[1].scatter(df['Area'], df['Circularity'], alpha=0.6, c='magenta')
92
+ axes[1].set_title('Area vs Circularity')
93
+
94
+ return vis_img, mask, fig, df, summary
95
+
96
+ # Gradio Interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  demo = gr.Interface(
98
+ fn=analyze,
99
  inputs=gr.Image(type="numpy"),
100
  outputs=[
101
+ gr.Image(label="Processed Image"),
102
+ gr.Image(label="Binary Mask"),
103
  gr.Plot(label="Analysis Plots"),
104
+ gr.Dataframe(label="Detected Cells Data"),
105
+ gr.JSON(label="Summary Statistics")
106
+ ]
 
107
  )
108
 
109
+ demo.launch()