-
Notifications
You must be signed in to change notification settings - Fork 0
/
image_parser.py
152 lines (114 loc) · 5.17 KB
/
image_parser.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
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
from bisect import bisect_left
from scipy.signal import argrelextrema
from skimage.color import rgb2gray
from skimage.filters import sobel
from skimage.io import imread
import matplotlib.pyplot as plt
import numpy as np
from utils import smooth, find_nearest_value_index, get_local_maximas, print_return_value, pairwise
class ImageParser:
def __init__(self):
self._matrix = None # type: list[list[int]]
self.img = None # type: np.ndarray
self._threshold = None
self._threshold_range = 0.3
def _smooth_param(self, img_size):
return img_size // 65
def threshold(self, cell_size=None):
if cell_size is None:
if self._threshold is None:
raise AttributeError('Pass to threshold() a cell_size before using it without one')
else:
self._threshold = cell_size * self._threshold_range
return self._threshold
def _get_rows(self, grayscale_img, draw_plots=False):
gradient_magnitude = sobel(grayscale_img)
# sum up rows of gradients, so that local maximas
# of resulting arrays are equal cell rows coordinates
gradient_y_projection = np.sum(gradient_magnitude, axis=1)
local_max_indices = get_local_maximas(gradient_y_projection,
self._smooth_param(grayscale_img.shape[1]))
height_middle = gradient_magnitude.shape[0] // 2
index = find_nearest_value_index(local_max_indices, height_middle)
diff = np.roll(local_max_indices, -1) - local_max_indices
def rows_range():
start_i, end_i = 0, 0
threshold = self.threshold(diff[index])
gen = reversed(list(enumerate(diff[:index])))
for i, diff_item in gen:
if np.abs(diff_item - diff[index]) > threshold:
start_i = i + 1
break
gen = enumerate(diff[index+1:], start=index+1)
for i, diff_item in gen:
if np.abs(diff_item - diff[index]) > threshold:
end_i = i
break
return start_i, end_i
start_i, end_i = rows_range()
rows = local_max_indices[start_i:end_i+1]
if draw_plots:
plt.plot(gradient_y_projection)
plt.scatter(rows, np.ones(len(rows)), c='r')
plt.scatter(local_max_indices, 4*np.ones(len(local_max_indices)), c='g')
return rows
def _get_columns(self, grayscale, draw_plots=False):
return self._get_rows(np.transpose(grayscale), draw_plots)
def parse(self, filename):
self.img = imread(filename)
grayscale_img = rgb2gray(self.img)
plt.figure(figsize=(12, 8))
plt.subplot(221)
rows = self._get_rows(grayscale_img, True)
cropped_grayscale = grayscale_img[rows[0] - self.threshold() : rows[-1] + self.threshold(), :]
plt.subplot(222)
plt.imshow(cropped_grayscale, cmap='gray')
plt.subplot(223)
columns = self._get_columns(cropped_grayscale, True)
self._matrix = self.get_matrix(cropped_grayscale, rows - rows[0] + self.threshold(), columns)
plt.subplot(224)
plt.imshow(self.img)
plt.show()
@print_return_value
def get_matrix(self, img, rows, columns):
def cells(img, rows, columns):
for row, (left, right) in enumerate(pairwise(rows)):
for column, (top, bottom) in enumerate(pairwise(columns)):
if top >= 0 and bottom < img.shape[0] and left >= 0 and right < img.shape[1]:
yield row, column, img[top:bottom, left:right]
def cell_type(cell):
#plt.imshow(cell, cmap='gray')
#plt.show()
avg_intensity = np.mean(cell)
return 2 - bisect_left([0.7, 0.9], avg_intensity)
cell_size = np.mean(rows[1:]-rows[:-1])
print('cell_size =', cell_size)
padding = 3
def pad_array_left(arr, padding, size):
left = np.array([arr[0] - i * size for i in range(1, padding + 1)], dtype=np.int32)
return np.append(left, arr)
def pad_array_right(arr, padding, size):
right = np.array([arr[-1] + i * size for i in range(1, padding + 1)], dtype=np.int32)
return np.append(arr, right)
first_row_zero = False
last_row_zero = False
while True:
cell_types = np.zeros((len(columns)-1, len(rows)-1))
for row, column, cell in cells(img, rows, columns):
cell_types[column, row] = cell_type(cell)
if np.all(cell_types[0] == 0):
first_row_zero = True
if np.all(cell_types[-1] == 0):
last_row_zero = True
if last_row_zero and first_row_zero:
break
if not first_row_zero:
columns = pad_array_left(columns, 1, cell_size)
if not last_row_zero:
columns = pad_array_right(columns, 1, cell_size)
return cell_types
@property
def matrix(self):
if self._matrix is None:
raise AttributeError('Call parse function before using the matrix')
return self._matrix