File size: 5,870 Bytes
dfa545b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce20c03
dfa545b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce20c03
dfa545b
 
 
 
 
 
 
 
 
c473c85
 
dfa545b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c473c85
 
 
 
 
dfa545b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce20c03
dfa545b
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
from typing import Tuple, Dict, Optional

import numpy as np
import cv2

def component_filter_thresh(
    img: np.ndarray,
    color_thresh: int = 75,
    avg_area: float = 800,
    kernal_size: tuple[int, int] = (3, 3),
    max_eggs: Optional[int] = None
) -> Dict:
    # Clone image, get grayscale, and masc for candidate egg pixels
    visualization_img = img.copy()
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    bin_mask = cv2.inRange(img_gray, 0, color_thresh)

    # Filter pixels not part of a elliptical region
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, kernal_size)
    opening = cv2.morphologyEx(bin_mask, cv2.MORPH_OPEN, kernel, iterations = 1)
    close =  cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations = 1)

    # Get connected components of filtered image
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(
        close,
    )

    num_eggs = 0

    # Iterate over stats, calculating the number of eggs in each connected component
    for curr_label, curr_stat in enumerate(stats):
        left_x = curr_stat[cv2.CC_STAT_LEFT]
        top_y = curr_stat[cv2.CC_STAT_TOP]
        area = curr_stat[cv2.CC_STAT_AREA]

        if (area < avg_area / 2) or curr_label == 0:
            continue

        # Calculate number of eggs
        curr_num_eggs = round(area / avg_area)

        if max_eggs and (curr_num_eggs > max_eggs):
            continue

        # Draw border around current component
        component_mask = (labels == curr_label).astype(np.uint8)
        contours, _ = cv2.findContours(component_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cv2.drawContours(visualization_img, contours, -1, (255, 0, 0), 2)

        # Label current component with number of eggs
        cv2.putText(
            visualization_img,
            str(curr_num_eggs),
            (left_x, top_y),
            cv2.FONT_HERSHEY_SIMPLEX,
            1.5,
            (0, 0, 255),
            3
        )

        num_eggs += curr_num_eggs

    return {
        "stats": {
            "Num-Eggs": num_eggs
        },
        "vis": {
            "Gray-Scale": img_gray.astype(np.uint8),
            "Egg-Mask": bin_mask.astype(np.uint8),
            "Ellipse-Filter": close.astype(np.uint8),
            "Visualization": visualization_img.astype(np.uint8)
        }
    }

def component_thesh(
    img: np.ndarray,
    color_thresh: int = 75,
    avg_area: float = 800,
    max_eggs: Optional[int] = None
) -> Dict:
    # Clone image
    visualization_img = img.copy()

    # Convert to grayscale
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    # Mask out egg pixels
    bin_mask = cv2.inRange(img_gray, 0, color_thresh)

    # Get connected components
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(
        bin_mask,
    )

    num_eggs = 0

    # Iterate over stats, calculating the number of eggs in each connected component
    for curr_label, curr_stat in enumerate(stats):
        left_x = curr_stat[cv2.CC_STAT_LEFT]
        top_y = curr_stat[cv2.CC_STAT_TOP]
        area = curr_stat[cv2.CC_STAT_AREA]

        if (area < avg_area / 2) or curr_label == 0:
            continue

        # Calculate number of eggs
        curr_num_eggs = round(area / avg_area)

        if max_eggs and (curr_num_eggs > max_eggs):
            continue

        # Draw border around current component
        component_mask = (labels == curr_label).astype(np.uint8)
        contours, _ = cv2.findContours(component_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cv2.drawContours(visualization_img, contours, -1, (255, 0, 0), 2)

        # Label current component with number of eggs
        cv2.putText(
            visualization_img,
            str(curr_num_eggs),
            (left_x, top_y),
            cv2.FONT_HERSHEY_SIMPLEX,
            1.5,
            (0, 0, 255),
            3
        )

        num_eggs += curr_num_eggs

    return {
        "stats": {
            "Num-Eggs": num_eggs
        },
        "vis": {
            "Gray-Scale": img_gray.astype(np.uint8),
            "Egg-Mask": bin_mask.astype(np.uint8),
            "Visualization": visualization_img.astype(np.uint8)
        }
    }

def contour_thresh(
    img: np.ndarray,
    color_thresh: int = 75,
    avg_area: float = 800,
    kernal_size: tuple[int, int] = (3, 3),
    max_eggs: Optional[int] = None
) -> Dict:
    visualization_img = img.copy()
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    bin_mask = cv2.inRange(img_gray, 0, color_thresh)

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, kernal_size)
    opening = cv2.morphologyEx(bin_mask, cv2.MORPH_OPEN, kernel, iterations = 1)
    close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations = 2)

    cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    num = 0

    for cnt in cnts:
        area = cv2.contourArea(cnt)

        if area > avg_area / 2:
            curr_num = round(area / avg_area)

            if max_eggs and (curr_num > max_eggs):
                continue

            cv2.drawContours(visualization_img, [cnt], -1, (255, 0, 0), 2)
            curr_num = round(area / avg_area)
            num += curr_num

            cv2.putText(
                visualization_img,
                str(curr_num),
                cnt[0, 0],
                cv2.FONT_HERSHEY_SIMPLEX,
                1.5,
                (0, 0, 255),
                3
            )

    return {
        "stats": {
            "Num-Eggs": num
        },
        "vis": {
            "Gray-Scale": img_gray.astype(np.uint8),
            "Egg-Mask": bin_mask.astype(np.uint8),
            "Ellipse-Filter": close.astype(np.uint8),
            "Visualization": visualization_img.astype(np.uint8)
        }
    }