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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -141
app.py CHANGED
@@ -2,177 +2,184 @@ import cv2
2
  import numpy as np
3
  import pandas as pd
4
  import gradio as gr
5
- from skimage import measure, morphology
6
  import matplotlib.pyplot as plt
7
  from datetime import datetime
8
 
9
- def detect_blood_cells(image):
10
- """Specialized function for blood cell detection"""
11
- # Convert to RGB if grayscale
12
- if len(image.shape) == 2:
13
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
14
-
15
- # Convert to HSV color space
16
- hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
17
-
18
- # Create mask for red blood cells
19
- # Red color has two ranges in HSV
20
- lower_red1 = np.array([0, 70, 50])
21
- upper_red1 = np.array([10, 255, 255])
22
- lower_red2 = np.array([170, 70, 50])
23
- upper_red2 = np.array([180, 255, 255])
24
-
25
- mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
26
- mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
27
- mask = mask1 + mask2
28
-
29
- # Noise removal and smoothing
30
- kernel = np.ones((3,3), np.uint8)
31
- mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
32
- mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)
33
-
34
- # Find contours
35
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
36
-
37
- return contours, mask
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- def apply_color_transformation(image, transform_type):
40
- """Apply different color transformations to the image"""
41
- if len(image.shape) == 2:
42
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
43
-
44
- if transform_type == "Original":
45
- return image
46
- elif transform_type == "Grayscale":
47
- return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
48
- elif transform_type == "Binary":
49
- gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
50
- _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
51
- return binary
52
- elif transform_type == "CLAHE":
53
- gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
54
- clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
55
- return clahe.apply(gray)
56
- return image
 
 
 
 
 
 
 
 
 
 
57
 
58
  def process_image(image, transform_type):
59
- """Process uploaded image and extract blood cell features"""
60
  if image is None:
61
  return None, None, None, None
62
 
63
  try:
64
- # Store original image for transformations
65
- original_image = image.copy()
66
-
67
- # Detect blood cells
68
- contours, mask = detect_blood_cells(image)
69
-
70
- # Extract features
71
- features = []
72
- for i, contour in enumerate(contours, 1):
73
- area = cv2.contourArea(contour)
74
- # Filter out very small or very large regions
75
- if 100 < area < 5000: # Adjust these thresholds based on your images
76
- perimeter = cv2.arcLength(contour, True)
77
- circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
78
-
79
- # Only include if it's reasonably circular
80
- if circularity > 0.7: # Adjust threshold as needed
81
- M = cv2.moments(contour)
82
- if M["m00"] != 0:
83
- cx = int(M["m10"] / M["m00"])
84
- cy = int(M["m01"] / M["m00"])
85
-
86
- features.append({
87
- 'label': i,
88
- 'area': area,
89
- 'perimeter': perimeter,
90
- 'circularity': circularity,
91
- 'centroid_x': cx,
92
- 'centroid_y': cy
93
- })
94
 
95
- # Create visualization
96
- vis_img = image.copy()
97
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
98
-
99
- # Draw contours and labels
100
- for feature in features:
101
- contour = contours[feature['label']-1]
102
- cv2.drawContours(vis_img, [contour], -1, (0, 255, 0), 2)
103
-
104
- # Add cell labels
105
- x = feature['centroid_x']
106
- y = feature['centroid_y']
107
- # White outline
108
- cv2.putText(vis_img, str(feature['label']),
109
- (x, y), cv2.FONT_HERSHEY_SIMPLEX,
110
- 0.5, (255, 255, 255), 2)
111
- # Red text
112
- cv2.putText(vis_img, str(feature['label']),
113
- (x, y), cv2.FONT_HERSHEY_SIMPLEX,
114
- 0.5, (0, 0, 255), 1)
115
-
116
- # Add timestamp and cell count
117
- cv2.putText(vis_img, f"Analyzed: {timestamp} | Cells: {len(features)}",
118
- (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
119
- 0.7, (255, 255, 255), 2)
120
-
121
- # Create analysis plots with default style
122
- plt.style.use('default')
123
- fig, axes = plt.subplots(2, 2, figsize=(15, 12))
124
- fig.suptitle('Blood Cell Analysis Results', fontsize=16, y=0.95)
125
 
126
  df = pd.DataFrame(features)
127
  if not df.empty:
128
- # Distribution plots
129
- axes[0,0].hist(df['area'], bins=20, color='skyblue', edgecolor='black')
130
- axes[0,0].set_title('Cell Size Distribution')
131
- axes[0,0].set_xlabel('Area (pixels)')
132
- axes[0,0].set_ylabel('Count')
133
- axes[0,0].grid(True, alpha=0.3)
134
 
135
- axes[0,1].hist(df['circularity'], bins=20, color='lightgreen', edgecolor='black')
136
- axes[0,1].set_title('Circularity Distribution')
137
- axes[0,1].set_xlabel('Circularity')
138
- axes[0,1].set_ylabel('Count')
139
- axes[0,1].grid(True, alpha=0.3)
140
 
141
- # Scatter plot
142
- axes[1,0].scatter(df['area'], df['circularity'], alpha=0.6, c='purple')
143
- axes[1,0].set_title('Area vs Circularity')
144
- axes[1,0].set_xlabel('Area')
145
- axes[1,0].set_ylabel('Circularity')
146
- axes[1,0].grid(True, alpha=0.3)
 
147
 
148
- # Box plot
149
- df.boxplot(column=['area', 'circularity'], ax=axes[1,1])
150
- axes[1,1].set_title('Feature Distributions')
151
- axes[1,1].grid(True, alpha=0.3)
152
- else:
153
- for ax in axes.flat:
154
- ax.text(0.5, 0.5, 'No cells detected', ha='center', va='center')
155
-
156
  plt.tight_layout()
157
 
158
- # Apply color transformation
159
- transformed_image = apply_color_transformation(original_image, transform_type)
160
-
161
  return (
162
  vis_img,
163
- transformed_image,
164
  fig,
165
  df
166
  )
167
 
168
  except Exception as e:
169
- print(f"Error processing image: {str(e)}")
170
- import traceback
171
- traceback.print_exc() # This will print the full error trace
172
  return None, None, None, None
173
 
174
 
175
 
 
176
  # Create Gradio interface
177
  with gr.Blocks(title="Advanced Cell Analysis Tool", theme=gr.themes.Soft()) as demo:
178
  gr.Markdown("""
 
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("""