ibrahim313 commited on
Commit
67b1d32
·
verified ·
1 Parent(s): b5ba092

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -215
app.py CHANGED
@@ -2,248 +2,77 @@ import cv2
2
  import numpy as np
3
  import pandas as pd
4
  import gradio as gr
5
- from skimage import morphology, segmentation
6
  import matplotlib.pyplot as plt
7
  from datetime import datetime
8
 
9
- def enhanced_preprocessing(image):
10
- """Advanced image preprocessing pipeline"""
11
- # Convert to LAB color space for better color separation
12
- lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB)
13
 
14
- # CLAHE on L-channel
15
- l_channel = lab[:,:,0]
16
- clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
17
- lab[:,:,0] = clahe.apply(l_channel)
18
 
19
- # Convert back to RGB
20
- enhanced = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)
21
 
22
- # Edge-preserving smoothing
23
- filtered = cv2.bilateralFilter(enhanced, 9, 75, 75)
 
 
24
 
25
- return filtered
26
 
27
- def detect_cells(image):
28
- """Advanced cell detection using multiple techniques"""
29
- # Enhanced preprocessing
30
- processed = enhanced_preprocessing(image)
31
 
32
- # Convert to grayscale
33
- gray = cv2.cvtColor(processed, cv2.COLOR_RGB2GRAY)
34
-
35
- # Adaptive thresholding
36
- binary = cv2.adaptiveThreshold(gray, 255,
37
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
38
- cv2.THRESH_BINARY_INV, 21, 4)
39
-
40
- # Morphological operations
41
- kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
42
- cleaned = morphology.area_opening(binary, area_threshold=128)
43
- cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel, iterations=2)
44
-
45
- # Watershed segmentation for overlapping cells
46
- distance = cv2.distanceTransform(cleaned, cv2.DIST_L2, 3)
47
- _, sure_fg = cv2.threshold(distance, 0.5*distance.max(), 255, 0)
48
- sure_fg = np.uint8(sure_fg)
49
-
50
- # Marker labeling
51
- _, markers = cv2.connectedComponents(sure_fg)
52
- markers += 1 # Add one to all labels
53
- markers[cleaned == 0] = 0 # Set background to 0
54
-
55
- # Apply watershed
56
- segmented = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
57
- markers = segmentation.watershed(segmented, markers)
58
-
59
- # Find contours from markers
60
- contours = []
61
- for label in np.unique(markers):
62
- if label < 1: # Skip background
63
- continue
64
- mask = np.zeros(gray.shape, dtype="uint8")
65
- mask[markers == label] = 255
66
- cnts, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
67
- contours.extend(cnts)
68
-
69
- return contours, cleaned
70
-
71
- def feature_analysis(contours, image):
72
- """Comprehensive feature extraction and validation"""
73
  features = []
74
  for i, contour in enumerate(contours, 1):
75
  area = cv2.contourArea(contour)
76
  perimeter = cv2.arcLength(contour, True)
 
77
 
78
- # Improved circularity calculation
79
- circularity = (4 * np.pi * area) / (perimeter**2 + 1e-6)
80
-
81
- # Advanced shape validation
82
- if 50 < area < 10000 and 0.4 < circularity < 1.2:
83
  M = cv2.moments(contour)
84
  if M["m00"] != 0:
85
  cx = int(M["m10"] / M["m00"])
86
  cy = int(M["m01"] / M["m00"])
87
-
88
- # Convexity check
89
- hull = cv2.convexHull(contour)
90
- hull_area = cv2.contourArea(hull)
91
- convexity = area / hull_area if hull_area > 0 else 0
92
-
93
- features.append({
94
- 'Cell ID': i,
95
- 'Area (px²)': area,
96
- 'Perimeter (px)': perimeter,
97
- 'Circularity': round(circularity, 3),
98
- 'Convexity': round(convexity, 3),
99
- 'Centroid X': cx,
100
- 'Centroid Y': cy
101
- })
102
 
103
- return features
104
 
105
- def visualize_results(image, contours, features):
106
- """Enhanced visualization with better annotations"""
107
- vis_img = image.copy()
108
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
109
-
110
- # Draw refined contours
111
- for idx, feature in enumerate(features):
112
- contour = contours[idx]
113
- cv2.drawContours(vis_img, [contour], -1, (0, 255, 0), 2)
114
-
115
- # Improved annotation placement
116
- x, y = feature['Centroid X'], feature['Centroid Y']
117
- cv2.putText(vis_img, str(feature['Cell ID']),
118
- (x+5, y-5), cv2.FONT_HERSHEY_SIMPLEX,
119
- 0.6, (255, 255, 255), 3)
120
- cv2.putText(vis_img, str(feature['Cell ID']),
121
- (x+5, y-5), cv2.FONT_HERSHEY_SIMPLEX,
122
- 0.6, (0, 0, 255), 2)
123
-
124
- # Add enhanced overlay
125
- cv2.putText(vis_img, f"Cells Detected: {len(features)} | {timestamp}",
126
- (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
127
- 0.7, (0, 0, 0), 3)
128
- cv2.putText(vis_img, f"Cells Detected: {len(features)} | {timestamp}",
129
- (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
130
- 0.7, (255, 255, 255), 2)
131
-
132
- return vis_img
133
-
134
- def process_image(image, transform_type):
135
- """Upgraded image processing pipeline"""
136
  if image is None:
137
  return None, None, None, None
138
 
139
- try:
140
- original = image.copy()
141
- contours, mask = detect_cells(image)
142
- features = feature_analysis(contours, image)
143
- vis_img = visualize_results(image, contours, features)
144
-
145
- # Create analysis plots
146
- plt.style.use('seaborn-v0_8')
147
- fig, ax = plt.subplots(2, 2, figsize=(15, 12))
148
- fig.suptitle('Advanced Cell Analysis', fontsize=16)
149
-
150
- df = pd.DataFrame(features)
151
- if not df.empty:
152
- ax[0,0].hist(df['Area (px²)'], bins=30, color='#1f77b4', ec='black')
153
- ax[0,0].set_title('Area Distribution')
154
-
155
- ax[0,1].scatter(df['Circularity'], df['Convexity'],
156
- c=df['Area (px²)'], cmap='viridis', alpha=0.7)
157
- ax[0,1].set_title('Shape Correlation')
158
-
159
- ax[1,0].boxplot([df['Area (px²)'], df['Circularity']],
160
- labels=['Area', 'Circularity'])
161
- ax[1,0].set_title('Feature Distribution')
162
-
163
- ax[1,1].hexbin(df['Centroid X'], df['Centroid Y'],
164
- gridsize=20, cmap='plasma', bins='log')
165
- ax[1,1].set_title('Spatial Distribution')
166
-
167
- plt.tight_layout()
168
-
169
- return (
170
- vis_img,
171
- apply_color_transformation(original, transform_type),
172
- fig,
173
- df
174
- )
175
 
176
- except Exception as e:
177
- print(f"Error: {str(e)}")
178
- return None, None, None, None
179
-
180
-
181
-
182
-
183
- # Create Gradio interface
184
- with gr.Blocks(title="Advanced Cell Analysis Tool", theme=gr.themes.Soft()) as demo:
185
- gr.Markdown("""
186
- # 🔬 Advanced Bioengineering Cell Analysis Tool
187
 
188
- ## Features
189
- - 🔍 Automated cell detection and measurement
190
- - 📊 Comprehensive statistical analysis
191
- - 🎨 Multiple visualization options
192
- - 📥 Downloadable results
193
 
194
- ## Author
195
- - **Muhammad Ibrahim Qasmi**
196
- - [LinkedIn](https://www.linkedin.com/in/muhammad-ibrahim-qasmi-9876a1297/)
197
- """)
198
 
199
- with gr.Row():
200
- with gr.Column(scale=1):
201
- input_image = gr.Image(
202
- label="Upload Image",
203
- type="numpy"
204
- )
205
- transform_type = gr.Dropdown(
206
- choices=["Original", "Grayscale", "Binary", "CLAHE"],
207
- value="Original",
208
- label="Image Transform"
209
- )
210
- analyze_btn = gr.Button(
211
- "Analyze Image",
212
- variant="primary",
213
- size="lg"
214
- )
215
 
216
- with gr.Column(scale=2):
217
- with gr.Tabs():
218
- with gr.Tab("Analysis Results"):
219
- output_image = gr.Image(
220
- label="Detected Cells"
221
- )
222
- gr.Markdown("*Green contours show detected cells, red numbers are cell IDs*")
223
-
224
- with gr.Tab("Image Transformations"):
225
- transformed_image = gr.Image(
226
- label="Transformed Image"
227
- )
228
- gr.Markdown("*Select different transformations from the dropdown menu*")
229
-
230
- with gr.Tab("Statistics"):
231
- output_plot = gr.Plot(
232
- label="Statistical Analysis"
233
- )
234
- gr.Markdown("*Hover over plots for detailed values*")
235
-
236
- with gr.Tab("Data"):
237
- output_table = gr.DataFrame(
238
- label="Cell Features"
239
- )
240
 
241
- analyze_btn.click(
242
- fn=process_image,
243
- inputs=[input_image, transform_type],
244
- outputs=[output_image, transformed_image, output_plot, output_table]
245
- )
246
 
247
- # Launch the demo
248
- if __name__ == "__main__":
249
- demo.launch()
 
2
  import numpy as np
3
  import pandas as pd
4
  import gradio as gr
 
5
  import matplotlib.pyplot as plt
6
  from datetime import datetime
7
 
8
+ def preprocess_image(image):
9
+ """Convert image to HSV and apply adaptive thresholding for better detection."""
10
+ if len(image.shape) == 2:
11
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
12
 
13
+ hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
14
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
 
 
15
 
16
+ # Adaptive thresholding for better contrast
17
+ adaptive_thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
18
 
19
+ # Morphological operations to remove noise
20
+ kernel = np.ones((3,3), np.uint8)
21
+ clean_mask = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
22
+ clean_mask = cv2.morphologyEx(clean_mask, cv2.MORPH_OPEN, kernel, iterations=2)
23
 
24
+ return clean_mask
25
 
26
+ def detect_blood_cells(image):
27
+ """Detect blood cells using contour analysis with refined filtering."""
28
+ mask = preprocess_image(image)
29
+ contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  features = []
32
  for i, contour in enumerate(contours, 1):
33
  area = cv2.contourArea(contour)
34
  perimeter = cv2.arcLength(contour, True)
35
+ circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
36
 
37
+ if 100 < area < 5000 and circularity > 0.7:
 
 
 
 
38
  M = cv2.moments(contour)
39
  if M["m00"] != 0:
40
  cx = int(M["m10"] / M["m00"])
41
  cy = int(M["m01"] / M["m00"])
42
+ features.append({'label': i, 'area': area, 'perimeter': perimeter, 'circularity': circularity, 'centroid_x': cx, 'centroid_y': cy})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
+ return contours, features, mask
45
 
46
+ def process_image(image):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  if image is None:
48
  return None, None, None, None
49
 
50
+ contours, features, mask = detect_blood_cells(image)
51
+ vis_img = image.copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ for feature in features:
54
+ contour = contours[feature['label'] - 1]
55
+ cv2.drawContours(vis_img, [contour], -1, (0, 255, 0), 2)
56
+ cv2.putText(vis_img, str(feature['label']), (feature['centroid_x'], feature['centroid_y']), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
 
 
 
 
 
 
 
57
 
58
+ df = pd.DataFrame(features)
59
+ return vis_img, mask, df
60
+
61
+ def analyze(image):
62
+ vis_img, mask, df = process_image(image)
63
 
64
+ plt.style.use('dark_background')
65
+ fig, axes = plt.subplots(1, 2, figsize=(12, 5))
 
 
66
 
67
+ if not df.empty:
68
+ axes[0].hist(df['area'], bins=20, color='cyan', edgecolor='black')
69
+ axes[0].set_title('Cell Size Distribution')
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ axes[1].scatter(df['area'], df['circularity'], alpha=0.6, c='magenta')
72
+ axes[1].set_title('Area vs Circularity')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
+ return vis_img, mask, fig, df
 
 
 
 
75
 
76
+ # Gradio Interface
77
+ demo = gr.Interface(fn=analyze, inputs=gr.Image(type="numpy"), outputs=[gr.Image(), gr.Image(), gr.Plot(), gr.Dataframe()])
78
+ demo.launch()