1
+ # ------------------------------------------------------------------------
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # ------------------------------------------------------------------------
16
+
17
+ import math
18
+ import numpy as np
19
+ from PIL import Image
20
+ import skimage .morphology as sk_morphology
21
+
22
+ def open_image (filename ):
23
+ image = Image .open (filename )
24
+ return image
25
+
26
+ def pil_to_np_rgb (pil_img ):
27
+ rgb = np .asarray (pil_img )
28
+ return rgb
29
+
30
+ def mask_percent (np_img ):
31
+ if (len (np_img .shape ) == 3 ) and (np_img .shape [2 ] == 3 ):
32
+ np_sum = np_img [:, :, 0 ] + np_img [:, :, 1 ] + np_img [:, :, 2 ]
33
+ mask_percentage = 100 - np .count_nonzero (np_sum ) / np_sum .size * 100 #(np_img.shape[0]*np_img.shape[1])
34
+ else :
35
+ mask_percentage = 100 - np .count_nonzero (np_img ) / np_img .size * 100
36
+ return mask_percentage
37
+
38
+ def tissue_percent (np_img ):
39
+ return 100 - mask_percent (np_img )
40
+
41
+ def filter_green_channel (np_img , green_thresh = 200 , avoid_overmask = True , overmask_thresh = 90 , output_type = "bool" ):
42
+ g = np_img [:, :, 1 ]
43
+ gr_ch_mask = (g < green_thresh ) & (g > 0 )
44
+ mask_percentage = mask_percent (gr_ch_mask )
45
+ if (mask_percentage >= overmask_thresh ) and (green_thresh < 255 ) and (avoid_overmask is True ):
46
+ new_green_thresh = math .ceil ((255 - green_thresh ) / 2 + green_thresh )
47
+ print ("Mask percentage %3.2f%% >= overmask threshold %3.2f%% for Remove Green Channel green_thresh=%d, so try %d" % (
48
+ mask_percentage , overmask_thresh , green_thresh , new_green_thresh ))
49
+ gr_ch_mask = filter_green_channel (np_img , new_green_thresh , avoid_overmask , overmask_thresh , output_type )
50
+ np_img = gr_ch_mask
51
+ if output_type == "bool" :
52
+ pass
53
+ elif output_type == "float" :
54
+ np_img = np_img .astype (float )
55
+ else :
56
+ np_img = np_img .astype ("uint8" ) * 255
57
+ return np_img
58
+
59
+ def filter_grays (rgb , tolerance = 15 , output_type = "bool" ):
60
+ (h , w , c ) = rgb .shape
61
+ rgb = rgb .astype (np .int )
62
+ rg_diff = abs (rgb [:, :, 0 ] - rgb [:, :, 1 ]) <= tolerance
63
+ rb_diff = abs (rgb [:, :, 0 ] - rgb [:, :, 2 ]) <= tolerance
64
+ gb_diff = abs (rgb [:, :, 1 ] - rgb [:, :, 2 ]) <= tolerance
65
+ result = ~ (rg_diff & rb_diff & gb_diff )
66
+ if output_type == "bool" :
67
+ pass
68
+ elif output_type == "float" :
69
+ result = result .astype (float )
70
+ else :
71
+ result = result .astype ("uint8" ) * 255
72
+ return result
73
+
74
+ def filter_red (rgb , red_lower_thresh , green_upper_thresh , blue_upper_thresh , output_type = "bool" ):
75
+ r = rgb [:, :, 0 ] > red_lower_thresh
76
+ g = rgb [:, :, 1 ] < green_upper_thresh
77
+ b = rgb [:, :, 2 ] < blue_upper_thresh
78
+ result = ~ (r & g & b )
79
+ if output_type == "bool" :
80
+ pass
81
+ elif output_type == "float" :
82
+ result = result .astype (float )
83
+ else :
84
+ result = result .astype ("uint8" ) * 255
85
+ return result
86
+
87
+ def filter_red_pen (rgb , output_type = "bool" ):
88
+ result = filter_red (rgb , red_lower_thresh = 150 , green_upper_thresh = 80 , blue_upper_thresh = 90 ) & \
89
+ filter_red (rgb , red_lower_thresh = 110 , green_upper_thresh = 20 , blue_upper_thresh = 30 ) & \
90
+ filter_red (rgb , red_lower_thresh = 185 , green_upper_thresh = 65 , blue_upper_thresh = 105 ) & \
91
+ filter_red (rgb , red_lower_thresh = 195 , green_upper_thresh = 85 , blue_upper_thresh = 125 ) & \
92
+ filter_red (rgb , red_lower_thresh = 220 , green_upper_thresh = 115 , blue_upper_thresh = 145 ) & \
93
+ filter_red (rgb , red_lower_thresh = 125 , green_upper_thresh = 40 , blue_upper_thresh = 70 ) & \
94
+ filter_red (rgb , red_lower_thresh = 200 , green_upper_thresh = 120 , blue_upper_thresh = 150 ) & \
95
+ filter_red (rgb , red_lower_thresh = 100 , green_upper_thresh = 50 , blue_upper_thresh = 65 ) & \
96
+ filter_red (rgb , red_lower_thresh = 85 , green_upper_thresh = 25 , blue_upper_thresh = 45 )
97
+ if output_type == "bool" :
98
+ pass
99
+ elif output_type == "float" :
100
+ result = result .astype (float )
101
+ else :
102
+ result = result .astype ("uint8" ) * 255
103
+ return result
104
+
105
+ def filter_green (rgb , red_upper_thresh , green_lower_thresh , blue_lower_thresh , output_type = "bool" ):
106
+ r = rgb [:, :, 0 ] < red_upper_thresh
107
+ g = rgb [:, :, 1 ] > green_lower_thresh
108
+ b = rgb [:, :, 2 ] > blue_lower_thresh
109
+ result = ~ (r & g & b )
110
+ if output_type == "bool" :
111
+ pass
112
+ elif output_type == "float" :
113
+ result = result .astype (float )
114
+ else :
115
+ result = result .astype ("uint8" ) * 255
116
+ return result
117
+
118
+ def filter_green_pen (rgb , output_type = "bool" ):
119
+ result = filter_green (rgb , red_upper_thresh = 150 , green_lower_thresh = 160 , blue_lower_thresh = 140 ) & \
120
+ filter_green (rgb , red_upper_thresh = 70 , green_lower_thresh = 110 , blue_lower_thresh = 110 ) & \
121
+ filter_green (rgb , red_upper_thresh = 45 , green_lower_thresh = 115 , blue_lower_thresh = 100 ) & \
122
+ filter_green (rgb , red_upper_thresh = 30 , green_lower_thresh = 75 , blue_lower_thresh = 60 ) & \
123
+ filter_green (rgb , red_upper_thresh = 195 , green_lower_thresh = 220 , blue_lower_thresh = 210 ) & \
124
+ filter_green (rgb , red_upper_thresh = 225 , green_lower_thresh = 230 , blue_lower_thresh = 225 ) & \
125
+ filter_green (rgb , red_upper_thresh = 170 , green_lower_thresh = 210 , blue_lower_thresh = 200 ) & \
126
+ filter_green (rgb , red_upper_thresh = 20 , green_lower_thresh = 30 , blue_lower_thresh = 20 ) & \
127
+ filter_green (rgb , red_upper_thresh = 50 , green_lower_thresh = 60 , blue_lower_thresh = 40 ) & \
128
+ filter_green (rgb , red_upper_thresh = 30 , green_lower_thresh = 50 , blue_lower_thresh = 35 ) & \
129
+ filter_green (rgb , red_upper_thresh = 65 , green_lower_thresh = 70 , blue_lower_thresh = 60 ) & \
130
+ filter_green (rgb , red_upper_thresh = 100 , green_lower_thresh = 110 , blue_lower_thresh = 105 ) & \
131
+ filter_green (rgb , red_upper_thresh = 165 , green_lower_thresh = 180 , blue_lower_thresh = 180 ) & \
132
+ filter_green (rgb , red_upper_thresh = 140 , green_lower_thresh = 140 , blue_lower_thresh = 150 ) & \
133
+ filter_green (rgb , red_upper_thresh = 185 , green_lower_thresh = 195 , blue_lower_thresh = 195 )
134
+ if output_type == "bool" :
135
+ pass
136
+ elif output_type == "float" :
137
+ result = result .astype (float )
138
+ else :
139
+ result = result .astype ("uint8" ) * 255
140
+ return result
141
+
142
+ def filter_blue (rgb , red_upper_thresh , green_upper_thresh , blue_lower_thresh , output_type = "bool" ):
143
+ r = rgb [:, :, 0 ] < red_upper_thresh
144
+ g = rgb [:, :, 1 ] < green_upper_thresh
145
+ b = rgb [:, :, 2 ] > blue_lower_thresh
146
+ result = ~ (r & g & b )
147
+ if output_type == "bool" :
148
+ pass
149
+ elif output_type == "float" :
150
+ result = result .astype (float )
151
+ else :
152
+ result = result .astype ("uint8" ) * 255
153
+ return result
154
+
155
+ def filter_blue_pen (rgb , output_type = "bool" ):
156
+ result = filter_blue (rgb , red_upper_thresh = 60 , green_upper_thresh = 120 , blue_lower_thresh = 190 ) & \
157
+ filter_blue (rgb , red_upper_thresh = 120 , green_upper_thresh = 170 , blue_lower_thresh = 200 ) & \
158
+ filter_blue (rgb , red_upper_thresh = 175 , green_upper_thresh = 210 , blue_lower_thresh = 230 ) & \
159
+ filter_blue (rgb , red_upper_thresh = 145 , green_upper_thresh = 180 , blue_lower_thresh = 210 ) & \
160
+ filter_blue (rgb , red_upper_thresh = 37 , green_upper_thresh = 95 , blue_lower_thresh = 160 ) & \
161
+ filter_blue (rgb , red_upper_thresh = 30 , green_upper_thresh = 65 , blue_lower_thresh = 130 ) & \
162
+ filter_blue (rgb , red_upper_thresh = 130 , green_upper_thresh = 155 , blue_lower_thresh = 180 ) & \
163
+ filter_blue (rgb , red_upper_thresh = 40 , green_upper_thresh = 35 , blue_lower_thresh = 85 ) & \
164
+ filter_blue (rgb , red_upper_thresh = 30 , green_upper_thresh = 20 , blue_lower_thresh = 65 ) & \
165
+ filter_blue (rgb , red_upper_thresh = 90 , green_upper_thresh = 90 , blue_lower_thresh = 140 ) & \
166
+ filter_blue (rgb , red_upper_thresh = 60 , green_upper_thresh = 60 , blue_lower_thresh = 120 ) & \
167
+ filter_blue (rgb , red_upper_thresh = 110 , green_upper_thresh = 110 , blue_lower_thresh = 175 )
168
+ if output_type == "bool" :
169
+ pass
170
+ elif output_type == "float" :
171
+ result = result .astype (float )
172
+ else :
173
+ result = result .astype ("uint8" ) * 255
174
+ return result
175
+
176
+ def filter_remove_small_objects (np_img , min_size = 500 , avoid_overmask = True , overmask_thresh = 95 , output_type = "uint8" ):
177
+ rem_sm = np_img .astype (bool ) # make sure mask is boolean
178
+ rem_sm = sk_morphology .remove_small_objects (rem_sm , min_size = min_size )
179
+ mask_percentage = mask_percent (rem_sm )
180
+ if (mask_percentage >= overmask_thresh ) and (min_size >= 1 ) and (avoid_overmask is True ):
181
+ new_min_size = min_size / 2
182
+ print ("Mask percentage %3.2f%% >= overmask threshold %3.2f%% for Remove Small Objs size %d, so try %d" % (
183
+ mask_percentage , overmask_thresh , min_size , new_min_size ))
184
+ rem_sm = filter_remove_small_objects (np_img , new_min_size , avoid_overmask , overmask_thresh , output_type )
185
+ np_img = rem_sm
186
+ if output_type == "bool" :
187
+ pass
188
+ elif output_type == "float" :
189
+ np_img = np_img .astype (float )
190
+ else :
191
+ np_img = np_img .astype ("uint8" ) * 255
192
+ return np_img
193
+
194
+ def mask_rgb (rgb , mask ):
195
+ result = rgb * np .dstack ([mask , mask , mask ])
196
+ return result
197
+
198
+ def apply_image_filters (np_img ):
199
+ rgb = np_img
200
+ mask_not_green = filter_green_channel (rgb )
201
+ mask_not_gray = filter_grays (rgb )
202
+ mask_no_red_pen = filter_red_pen (rgb )
203
+ mask_no_green_pen = filter_green_pen (rgb )
204
+ rgb_no_green_pen = mask_rgb (rgb , mask_no_green_pen )
205
+ mask_no_blue_pen = filter_blue_pen (rgb )
206
+ mask_gray_green_pens = mask_not_gray & mask_not_green & mask_no_red_pen & mask_no_green_pen & mask_no_blue_pen
207
+ mask_remove_small = filter_remove_small_objects (mask_gray_green_pens , min_size = 500 , output_type = "bool" )
208
+ rgb_remove_small = mask_rgb (rgb , mask_remove_small )
209
+ img = rgb_remove_small
210
+ return img
0 commit comments