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