|
import cv2 |
|
import numpy as np |
|
import pandas as pd |
|
import gradio as gr |
|
from skimage import measure, morphology |
|
from skimage.segmentation import watershed |
|
import matplotlib.pyplot as plt |
|
|
|
def process_image(image): |
|
"""Process uploaded image and extract cell features""" |
|
|
|
if len(image.shape) == 3: |
|
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) |
|
|
|
|
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) |
|
enhanced = clahe.apply(gray) |
|
blurred = cv2.medianBlur(enhanced, 5) |
|
|
|
|
|
thresh = cv2.adaptiveThreshold( |
|
blurred, 255, |
|
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, |
|
cv2.THRESH_BINARY_INV, 21, 4 |
|
) |
|
|
|
|
|
cleaned = morphology.opening(thresh, morphology.disk(2)) |
|
|
|
|
|
sure_bg = cv2.dilate(cleaned, morphology.disk(3), iterations=3) |
|
dist = cv2.distanceTransform(cleaned, cv2.DIST_L2, 5) |
|
ret, sure_fg = cv2.threshold(dist, 0.5*dist.max(), 255, 0) |
|
sure_fg = np.uint8(sure_fg) |
|
unknown = cv2.subtract(sure_bg, sure_fg) |
|
|
|
|
|
ret, markers = cv2.connectedComponents(sure_fg) |
|
markers += 1 |
|
markers[unknown == 255] = 0 |
|
markers = watershed(-dist, markers, mask=cleaned) |
|
|
|
|
|
features = [] |
|
props = measure.regionprops(markers, intensity_image=gray) |
|
|
|
for i, prop in enumerate(props): |
|
if prop.area < 50: |
|
continue |
|
|
|
features.append({ |
|
'cell_id': i+1, |
|
'area': prop.area, |
|
'perimeter': prop.perimeter, |
|
'circularity': (4 * np.pi * prop.area) / (prop.perimeter**2 + 1e-6), |
|
'mean_intensity': prop.mean_intensity, |
|
'centroid_x': prop.centroid[1], |
|
'centroid_y': prop.centroid[0] |
|
}) |
|
|
|
|
|
vis_img = image.copy() |
|
contours = measure.find_contours(markers, 0.5) |
|
|
|
|
|
for contour in contours: |
|
coords = contour.astype(int) |
|
cv2.drawContours(vis_img, [coords], -1, (0,255,0), 1) |
|
|
|
for region in measure.regionprops(markers): |
|
if region.area >= 50: |
|
y, x = region.centroid |
|
cv2.putText(vis_img, str(region.label), |
|
(int(x), int(y)), |
|
cv2.FONT_HERSHEY_SIMPLEX, |
|
0.4, (255,0,0), 1) |
|
|
|
|
|
fig, axes = plt.subplots(1, 2, figsize=(12, 6)) |
|
|
|
|
|
df = pd.DataFrame(features) |
|
df['area'].hist(ax=axes[0], bins=20) |
|
axes[0].set_title('Cell Size Distribution') |
|
axes[0].set_xlabel('Area') |
|
axes[0].set_ylabel('Count') |
|
|
|
|
|
axes[1].scatter(df['circularity'], df['mean_intensity']) |
|
axes[1].set_title('Circularity vs Intensity') |
|
axes[1].set_xlabel('Circularity') |
|
axes[1].set_ylabel('Mean Intensity') |
|
|
|
plt.tight_layout() |
|
|
|
return ( |
|
cv2.cvtColor(vis_img, cv2.COLOR_BGR2RGB), |
|
fig, |
|
df |
|
) |
|
|
|
|
|
with gr.Blocks(title="Cell Analysis Tool") as demo: |
|
gr.Markdown(""" |
|
# 🔬 Bioengineering Cell Analysis Tool |
|
|
|
Upload microscopy images to analyze cell properties: |
|
- Automated cell detection |
|
- Feature extraction (size, shape, intensity) |
|
- Statistical analysis |
|
|
|
**Instructions:** |
|
1. Upload an image containing cells |
|
2. Wait for analysis to complete |
|
3. Review results in three tabs: |
|
- Detected cells visualization |
|
- Statistical plots |
|
- Detailed measurements table |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
input_image = gr.Image( |
|
label="Upload Image", |
|
type="numpy", |
|
tool="upload" |
|
) |
|
analyze_btn = gr.Button( |
|
"Analyze Image", |
|
variant="primary" |
|
) |
|
|
|
with gr.Column(): |
|
with gr.Tabs(): |
|
with gr.Tab("Detection Results"): |
|
output_image = gr.Image( |
|
label="Detected Cells" |
|
) |
|
with gr.Tab("Statistics"): |
|
output_plot = gr.Plot( |
|
label="Statistical Analysis" |
|
) |
|
with gr.Tab("Measurements"): |
|
output_table = gr.DataFrame( |
|
label="Cell Features" |
|
) |
|
|
|
analyze_btn.click( |
|
fn=process_image, |
|
inputs=input_image, |
|
outputs=[output_image, output_plot, output_table] |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch() |