-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
358 lines (246 loc) · 14.8 KB
/
main.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
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
from os import listdir
from os.path import isfile, basename
import cv2
import numpy as np
import skimage
from skimage.feature import greycomatrix
from skimage.feature import greycoprops
from calibration import load_calibration, undistort_image
from find_corners import find_good_features_to_track, match_corners
from find_max_contour import find_max_contour, clip_img, find_moments, align_and_clip, ensure_max_width, \
find_edges_threshold, supress_background_noise, find_edges
from sift import match_with_sift
from texture import find_lbp
def load_and_prepare_img(src_img_filename):
src_img = cv2.imread(src_img_filename)
camera_matrix, dist_coefs = load_calibration()
undistorted = undistort_image(camera_matrix, dist_coefs, src_img)
height, width, color_depth = undistorted.shape
clipped = undistorted[10:height-10, 100:width-100]
grey = cv2.cvtColor(clipped, cv2.COLOR_BGR2GRAY)
return {
'original_img': src_img,
'grey_img': grey,
'filename': src_img_filename,
'basename': basename(src_img_filename)
}
def iterate_sift(training_set, query_set):
training_set = [load_and_prepare_img(img_filename) for img_filename in training_set]
query_set = [load_and_prepare_img(img_filename) for img_filename in query_set]
training_set = [find_max_contour(res) for res in training_set]
query_set = [find_max_contour(res) for res in query_set]
training_set = [clip_img(res, 'original_img') for res in training_set]
query_set = [clip_img(res, 'original_img') for res in query_set]
match_with_sift(training_set, query_set)
def detection(training_set_filenames, query_set_filenames):
training_set = [load_and_prepare_img(filename) for filename in training_set_filenames]
query_set = [load_and_prepare_img(filename) for filename in query_set_filenames]
training_set = [supress_background_noise(res, 'grey_img') for res in training_set]
query_set = [supress_background_noise(res, 'grey_img') for res in query_set]
training_set = [find_max_contour(res) for res in training_set]
query_set = [find_max_contour(res) for res in query_set]
training_set = [align_and_clip(res, 'grey_img') for res in training_set]
query_set = [align_and_clip(res, 'grey_img') for res in query_set]
training_set = [ensure_max_width(res, 'clipped_img') for res in training_set]
query_set = [ensure_max_width(res, 'clipped_img') for res in query_set]
training_set = [find_edges(res, 'clipped_img') for res in training_set]
query_set = [find_edges(res, 'clipped_img') for res in query_set]
training_set = [find_edges_threshold(res, 'clipped_img') for res in training_set]
query_set = [find_edges_threshold(res, 'clipped_img') for res in query_set]
training_set = [find_moments(res, 'edges_threshold_img', binary_image=True) for res in training_set]
query_set = [find_moments(res, 'edges_threshold_img', binary_image=True) for res in query_set]
query_set = [find_lbp(res, 'clipped_img', num_points=16, radius=2) for res in query_set]
training_set = [find_lbp(res, 'clipped_img', num_points=16, radius=2) for res in training_set]
training_set = [find_good_features_to_track(res, 'clipped_img') for res in training_set]
query_set = [find_good_features_to_track(res, 'clipped_img') for res in query_set]
for query_idx, query in enumerate(query_set):
print(f"Query {query['filename']}")
cv2.imwrite(f'out/{query["basename"]}-query.png', query['clipped_img'])
cv2.imwrite(f'out/{query["basename"]}-lbp.png', query['lbp_img'])
cv2.imwrite(f'out/{query["basename"]}-edges.png', query['edges_img'])
cv2.imwrite(f'out/{query["basename"]}-edges-threshold.png', query['edges_threshold_img'])
cv2.imwrite(f'out/{query["basename"]}-corners.png', query['corners_img'])
scores = {}
filtered_training_set = filter_by_dimensions(query, training_set)
# [cv2.imwrite(f'out/{res["basename"]}-lbp.png', res['lbp_img']) for res in filtered_training_set]
if len(filtered_training_set) > 1:
calc_corners_score(query, filtered_training_set, scores)
filtered_training_set = filter_by_proximity_to_score(filtered_training_set, scores, 'corners_scores',
find_top_score(scores, 'corners_scores'), 0.3)
calc_shapes_score(query, filtered_training_set, scores)
calc_histogram_score(query, filtered_training_set, scores)
calc_template_matching_correlation_score(query, filtered_training_set, scores)
calc_grey_corr_score(query, filtered_training_set, scores)
calc_aggregated_score(scores)
filtered_training_set = sort_by_score(filtered_training_set, scores, 'final_score02')
if len(filtered_training_set) >= 1:
best_match = filtered_training_set[0]
print(f"Best match for {query['filename']}: {best_match['filename']}")
cv2.imwrite(f'out/{query["basename"]}-best-match.png', best_match['clipped_img'])
cv2.imwrite(f'out/{query["basename"]}-best-match-lbp.png', best_match['lbp_img'])
cv2.imwrite(f'out/{query["basename"]}-best-match-edges.png', best_match['edges_img'])
cv2.imwrite(f'out/{query["basename"]}-best-match-edges-threshold.png', best_match['edges_threshold_img'])
cv2.imwrite(f'out/{query["basename"]}-best-match-corners.png', best_match['corners_img'])
print("")
def sort_by_score(training_set, scores, what_to_sort, reverse=True):
def sorter_func(a):
return scores[what_to_sort][a['filename']]
return sorted(training_set, key=sorter_func, reverse=reverse)
def find_top_score(scores, what_to_filter_key):
max_score = -1
for key, value in scores[what_to_filter_key].items():
if value > max_score:
max_score = value
return max_score
def find_bottom_score(scores, what_to_filter_key):
min_score = 100000
for key, value in scores[what_to_filter_key].items():
if value < min_score:
min_score = value
return min_score
def filter_by_proximity_to_score(training_set, scores, what_to_filter_key, top_score, percent_proximity):
def percent_distance(a):
if top_score == 0:
percent = 0
else:
percent = a / top_score
return abs(1 - percent) < percent_proximity
return [close_enough for close_enough in training_set if percent_distance(scores[what_to_filter_key][close_enough['filename']])]
def filter_by_score(training_set, scores, what_to_filter_key, min_value, max_value):
return [blah for blah in training_set
if (min_value is None or scores[what_to_filter_key][blah['filename']] > min_value)
and (max_value is None or scores[what_to_filter_key][blah['filename']] < max_value)]
def calc_aggregated_score(scores):
scores['final_score'] = {}
scores['final_score02'] = {}
for hist_key in scores['hist_scores']:
scores['final_score'][hist_key] = (scores['hist_scores'][hist_key]
+ scores['shapes_scores'][hist_key]
+ scores['correlation_scores'][hist_key])
print(f"FinalScore for {hist_key}: {scores['final_score'][hist_key]}")
for hist_key in scores['hist_scores']:
# scores['final_score02'][hist_key] = (scores['hist_scores'][hist_key]
# + scores['shapes_scores'][hist_key]
# + scores['shapes_scores02'][hist_key]
# + scores['correlation_scores'][hist_key])
scores['final_score02'][hist_key] = (scores['correlation_scores'][hist_key]
+ scores['shapes_scores02'][hist_key])
print(f"FinalScore02 for {hist_key}: {scores['final_score02'][hist_key]}")
def calc_grey_corr_score(query, training_set, scores):
scores['grey_corr_scores'] = {}
query_comatrix = greycomatrix(query['clipped_img'], [1], [0])
query_asm = greycoprops(query_comatrix, prop='dissimilarity')
for train in training_set:
train_comatrix = greycomatrix(train['clipped_img'], [1], [0])
train_asm = greycoprops(train_comatrix, prop='dissimilarity')
scores['grey_corr_scores'][train['filename']] = abs(query_asm - train_asm)
print(f"GreyCorr for {train['filename']}: {scores['grey_corr_scores'][train['filename']]}")
def clip_to_same_dimensions(query_img, train_img):
min_width = min(query_img.shape[1], train_img.shape[1])
min_height = min(query_img.shape[0], train_img.shape[0])
same_dimensions_query = query_img[0:min_height, 0:min_width]
same_dimensions_train = train_img[0:min_height, 0:min_width]
return same_dimensions_query, same_dimensions_train
def calc_template_matching_correlation_score(query, training_set, scores):
scores['correlation_scores'] = {}
query_img = query['clipped_img']
for train in training_set:
train_img = train['clipped_img']
same_dimensions_query, same_dimensions_train = clip_to_same_dimensions(query_img, train_img)
corr = skimage.feature.match_template(same_dimensions_query, same_dimensions_train, pad_input=True)
first_corr = corr.max()
corr = skimage.feature.match_template(cv2.flip(same_dimensions_query, -1), same_dimensions_train, pad_input=True)
second_corr = corr.max()
corr = max(first_corr, second_corr)
scores['correlation_scores'][train['filename']] = corr
print(f"CorrScore for {train['filename']}: {scores['correlation_scores'][train['filename']]}")
def calc_corners_score(query, training_set, scores):
scores['corners_scores'] = {}
for train in training_set:
percentage_match, matches = match_corners(query, train)
scores['corners_scores'][train['filename']] = percentage_match
print(f"Corners match for {train['filename']}: {scores['corners_scores'][train['filename']]}")
def calc_histogram_score(query, training_set, scores):
scores['hist_scores'] = {}
for train in training_set:
# For some similarity functions a LARGER value indicates higher similarity (Correlation and Intersection).
# And for others, a SMALLER value indicates higher similarity (Chi-Squared and Hellinger).
hist_dist = cv2.compareHist(query['hist'], train['hist'], cv2.HISTCMP_BHATTACHARYYA)
scores['hist_scores'][train['filename']] = 1 - hist_dist
print(f"HistDist for {train['filename']}: {scores['hist_scores'][train['filename']]}")
def calc_shapes_score(query, training_set, scores):
scores['shapes_scores'] = {}
scores['shapes_scores02'] = {}
for train in training_set:
match_coeff = cv2.matchShapes(train['edges_threshold_img'], query['edges_threshold_img'], method=1, parameter=0.0)
scores['shapes_scores'][train['filename']] = 1 - (5*match_coeff)
print(f"MatchCoeff for {train['filename']}: {scores['shapes_scores'][train['filename']]}")
for train in training_set:
match_coeff = cv2.matchShapes(train['edges_img'], query['edges_img'], method=1, parameter=0.0)
# The 0.5 is totally empirical. edges_img is much more noisy and therefore we don't want its contribution
# to the final score to be as important as edges_threshold_img
scores['shapes_scores02'][train['filename']] = (1 - (5*match_coeff)) * 0.5
print(f"MatchCoeff02 for {train['filename']}: {scores['shapes_scores02'][train['filename']]}")
def filter_by_dimensions(query, training_set):
filtered = []
for training in training_set:
w_ratio, h_ratio = get_dimensions_ratio(training['contour_rect'], query['contour_rect'])
if w_ratio < 1.1 and h_ratio < 1.1:
filtered.append(training)
return filtered
def get_dimensions_ratio(training_rect, query_rect):
def abs_ratio(a, b):
return a / b if a > b else b / a
query_width_rect = max(query_rect[1][0], query_rect[1][1])
query_height_rect = min(query_rect[1][0], query_rect[1][1])
training_width_rect = max(training_rect[1][0], training_rect[1][1])
training_height_rect = min(training_rect[1][0], training_rect[1][1])
return abs_ratio(query_width_rect, training_width_rect), abs_ratio(query_height_rect, training_height_rect)
def save_to_file(res, img_idx):
img_01 = res['grey_img']
img_02 = res['threshold_img']
img_03 = res['edges_threshold_img']
img_04 = res['contours_img']
combined = np.zeros((img_01.shape[0] + img_03.shape[0], img_01.shape[1] + img_02.shape[1]), np.uint8)
combined[:img_01.shape[0], :img_01.shape[1]] = img_01
combined[:img_02.shape[0], img_01.shape[1]:img_01.shape[1] + img_02.shape[1]] = img_02
combined[img_01.shape[0]:img_01.shape[0] + img_03.shape[0], :img_03.shape[1]] = img_03
combined[img_01.shape[0]:img_01.shape[0] + img_04.shape[0], img_01.shape[1]:img_01.shape[1] + img_04.shape[1]] = img_04
rect = res['contour_rect']
if rect:
rect_text = "Bounding rect ({0:.0f},{1:.0f}), angle {2:.0f}".format(rect[1][0], rect[1][1], rect[2])
cv2.putText(combined, rect_text, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA)
print("Image {0} {1}".format(img_idx, rect_text))
cv2.imwrite('out/{0}-combined.png'.format(img_idx), combined)
def iterate_over_images_quick_test(img_names):
for img_idx, img_name in enumerate(img_names):
res = load_and_prepare_img(img_name)
find_max_contour(res)
align_and_clip(res, 'grey_img')
ensure_max_width(res, 'clipped_img')
find_good_features_to_track(res, 'clipped_img')
cv2.imwrite('out/{0}-corners.png'.format(img_idx), res['corners_img'])
# find_sift(res, 'clipped_img')
# cv2.imwrite('out/{0}-sift.png'.format(img_idx), res['sift_img'])
if __name__ == '__main__':
training_filenames = ['in/control/' + file for file in listdir('in/control')]
training_filenames = [file for file in training_filenames if isfile(file)]
# training_filenames = [
# 'in/control/10.bmp',
# 'in/control/13.bmp',
# 'in/control/21.bmp',
# 'in/control/22.bmp',
# ]
# training_filenames = [ 'in/control/00.bmp', 'in/control/14.bmp']
query_filenames = ['in/trial02/' + file for file in listdir('in/trial02')]
query_filenames = [file for file in query_filenames if isfile(file)]
# query_filenames = [
# 'in/trial02/01.bmp',
# ]
# iterate_sift(training_filenames, query_filenames)
detection(training_filenames, query_filenames)
# calibrate()
# camera_matrix, dist_coefs = load_calibration()
# undistort_images(camera_matrix, dist_coefs, glob.glob('in/trial/*.bmp'))
# iterate_over_images_quick_test(training_filenames)
cv2.destroyAllWindows()