-
Notifications
You must be signed in to change notification settings - Fork 0
/
calc_nuclei_threshold.py
111 lines (87 loc) · 2.66 KB
/
calc_nuclei_threshold.py
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
import sys
from skimage import io, color
import numpy as np
def read_hae_file(image_file):
rgba = io.imread(image_file)
rgb = color.rgba2rgb(rgba)
he = unmix_color(rgb, CHOICE_HEMATOXYLIN)
es = unmix_color(rgb, CHOICE_EOSIN)
result = np.zeros([he.shape[0], he.shape[1], 2])
result[:, :, 0] = he
result[:, :, 1] = es
return result
def calc_nuclei_threshold(image_file, threshold=150):
rgba = io.imread(image_file)
rgb = color.rgba2rgb(rgba)
# lab = color.rgb2lab(rgb)
# threshold = 0.5 + np.mean(lab[:, :, 1]) / 100 * 0.3
unmixed = unmix_color(rgb)
# ignore black background
mean = np.mean(unmixed[unmixed > 0.3])
threshold = mean - 0.1
if threshold < 0.35:
threshold = 0.35
return threshold
def html_color(rgb):
'''Return an HTML color for a given stain'''
rgb = np.exp(-np.array(rgb)) * 255
rgb = rgb.astype(int)
color = hex((rgb[0] * 256 + rgb[1]) * 256 + rgb[2])
if color.endswith("L"):
color = color[:-1]
return "#" + color[2:]
CHOICE_HEMATOXYLIN = "Hematoxylin"
ST_HEMATOXYLIN = (0.644, 0.717, 0.267)
COLOR_HEMATOXYLIN = html_color(ST_HEMATOXYLIN)
CHOICE_EOSIN = "Eosin"
ST_EOSIN = (0.093, 0.954, 0.283)
COLOR_EOSIN = html_color(ST_EOSIN)
STAIN_DICTIONARY = {
CHOICE_EOSIN: ST_EOSIN,
CHOICE_HEMATOXYLIN: ST_HEMATOXYLIN,
}
STAINS_BY_POPULARITY = (
CHOICE_HEMATOXYLIN, CHOICE_EOSIN,
)
FIXED_SETTING_COUNT = 2
VARIABLE_SETTING_COUNT = 5
def unmix_color(input_pixels, stein):
'''Produce one image - storing it in the image set'''
inverse_absorbances = get_inverse_absorbances(stein)
#########################################
#
# Renormalize to control for the other stains
#
# Log transform the image data
#
# First, rescale it a little to offset it from zero
#
eps = 1.0 / 256.0 / 2.0
image = input_pixels + eps
log_image = np.log(image)
#
# Now multiply the log-transformed image
#
scaled_image = (log_image
* inverse_absorbances[np.newaxis, np.newaxis, :])
#
# Exponentiate to get the image without the dye effect
#
image = np.exp(np.sum(scaled_image, 2))
#
# and subtract out the epsilon we originally introduced
#
image -= eps
image[image < 0] = 0
image[image > 1] = 1
image = 1 - image
return image
def get_inverse_absorbances(choice):
'''Given one of the outputs, return the red, green and blue
absorbance'''
result = STAIN_DICTIONARY[choice]
result = np.array(result)
result = result / np.sqrt(np.sum(result ** 2))
return result
if __name__ == "__main__":
print calc_nuclei_threshold(sys.argv[1])