ibrahim313's picture
Update app.py
7702060 verified
raw
history blame
3.66 kB
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()