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()