import cv2 import numpy as np import pandas as pd import gradio as gr import matplotlib.pyplot as plt from datetime import datetime class BloodCellAnalyzer: def __init__(self): # Adjusted parameters for the specific image characteristics self.min_rbc_area = 400 self.max_rbc_area = 2000 self.min_wbc_area = 500 self.max_wbc_area = 3000 self.min_circularity = 0.75 def detect_cells(self, image): """Detect both red and white blood cells using color-based segmentation.""" if image is None: return None, [], None # Convert to RGB if grayscale if len(image.shape) == 2: image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # Convert to different color spaces hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB) # Red blood cell detection (red color range) lower_red1 = np.array([0, 50, 50]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([160, 50, 50]) upper_red2 = np.array([180, 255, 255]) red_mask1 = cv2.inRange(hsv, lower_red1, upper_red1) red_mask2 = cv2.inRange(hsv, lower_red2, upper_red2) red_mask = cv2.bitwise_or(red_mask1, red_mask2) # White blood cell detection (blue color range) lower_blue = np.array([90, 50, 50]) upper_blue = np.array([130, 255, 255]) blue_mask = cv2.inRange(hsv, lower_blue, upper_blue) # Enhance masks kernel = np.ones((3,3), np.uint8) red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_OPEN, kernel, iterations=1) red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_CLOSE, kernel, iterations=1) blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_OPEN, kernel, iterations=1) blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_CLOSE, kernel, iterations=1) # Find contours for both cell types rbc_contours, _ = cv2.findContours(red_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) wbc_contours, _ = cv2.findContours(blue_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cells = [] valid_contours = [] # Process RBCs for i, contour in enumerate(rbc_contours): area = cv2.contourArea(contour) perimeter = cv2.arcLength(contour, True) circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0 if (self.min_rbc_area < area < self.max_rbc_area and circularity > self.min_circularity): M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) cells.append({ 'label': len(valid_contours) + 1, 'type': 'RBC', 'area': area, 'circularity': circularity, 'centroid_x': cx, 'centroid_y': cy }) valid_contours.append(contour) # Process WBCs for i, contour in enumerate(wbc_contours): area = cv2.contourArea(contour) perimeter = cv2.arcLength(contour, True) circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0 if (self.min_wbc_area < area < self.max_wbc_area): M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) cells.append({ 'label': len(valid_contours) + 1, 'type': 'WBC', 'area': area, 'circularity': circularity, 'centroid_x': cx, 'centroid_y': cy }) valid_contours.append(contour) return valid_contours, cells, red_mask def analyze_image(self, image): """Analyze the blood cell image and generate visualizations.""" if image is None: return None, None, None, None # Detect cells contours, cells, mask = self.detect_cells(image) vis_img = image.copy() # Draw detections for cell in cells: contour = contours[cell['label'] - 1] color = (0, 0, 255) if cell['type'] == 'RBC' else (255, 0, 0) cv2.drawContours(vis_img, [contour], -1, color, 2) cv2.putText(vis_img, f"{cell['type']}", (cell['centroid_x'], cell['centroid_y']), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) # Create DataFrame df = pd.DataFrame(cells) # Generate summary statistics if not df.empty: rbc_count = len(df[df['type'] == 'RBC']) wbc_count = len(df[df['type'] == 'WBC']) summary_stats = { 'total_rbc': rbc_count, 'total_wbc': wbc_count, 'rbc_avg_size': df[df['type'] == 'RBC']['area'].mean() if rbc_count > 0 else 0, 'wbc_avg_size': df[df['type'] == 'WBC']['area'].mean() if wbc_count > 0 else 0, } # Add summary stats to DataFrame for k, v in summary_stats.items(): df[k] = v # Generate visualization fig = self.generate_analysis_plots(df) return vis_img, mask, fig, df def generate_analysis_plots(self, df): """Generate analysis plots for the detected cells.""" if df.empty: return None plt.style.use('dark_background') fig, axes = plt.subplots(2, 2, figsize=(12, 10)) # Cell count by type cell_counts = df['type'].value_counts() axes[0, 0].bar(cell_counts.index, cell_counts.values, color=['red', 'blue']) axes[0, 0].set_title('Cell Count by Type') # Size distribution for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']): if len(df[df['type'] == cell_type]) > 0: axes[0, 1].hist(df[df['type'] == cell_type]['area'], bins=20, alpha=0.5, color=color, label=cell_type) axes[0, 1].set_title('Cell Size Distribution') axes[0, 1].legend() # Circularity by type for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']): cell_data = df[df['type'] == cell_type] if len(cell_data) > 0: axes[1, 0].scatter(cell_data['area'], cell_data['circularity'], c=color, label=cell_type, alpha=0.6) axes[1, 0].set_title('Area vs Circularity') axes[1, 0].legend() # Spatial distribution for cell_type, color in zip(['RBC', 'WBC'], ['red', 'blue']): cell_data = df[df['type'] == cell_type] if len(cell_data) > 0: axes[1, 1].scatter(cell_data['centroid_x'], cell_data['centroid_y'], c=color, label=cell_type, alpha=0.6) axes[1, 1].set_title('Spatial Distribution') axes[1, 1].legend() plt.tight_layout() return fig # Create Gradio interface analyzer = BloodCellAnalyzer() demo = gr.Interface( fn=analyzer.analyze_image, inputs=gr.Image(type="numpy"), outputs=[ gr.Image(label="Detected Cells"), gr.Image(label="Segmentation Mask"), gr.Plot(label="Analysis Plots"), gr.DataFrame(label="Cell Data") ], title="Blood Cell Analysis Tool", description="Upload an image to analyze red and white blood cells." ) if __name__ == "__main__": demo.launch()