-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathHitsManager.py
316 lines (253 loc) · 10.8 KB
/
HitsManager.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
import Geometry2D as geo2D
import numpy as np
CANDIDATE = 0
VERIFIED = 1
candidate_hits = []
verified_hits = []
class Hit:
def __init__(self, x, y, score, bullseyeRelation):
'''
{Number} x - x coordinate of the hit
{Number} y - y coordinate of the hit
{Number} score - The hit's score
{Tuple} bullseysRelation - (
{Number} current x coordinate of the bull'seye point,
{Number} current y coordinate of the bull'seye point
)
'''
self.point = (x, y)
self.score = score
self.reputation = 1
self.bullseye_relation = bullseyeRelation
# has this hit been checked during current iteration
self.iter_mark = False
def increase_rep(self):
'''
Increase the hit's reputation.
'''
self.reputation += 1
def decrease_rep(self):
'''
Decrease the hit's reputation.
'''
self.reputation -= 1
def isVerified(self, repScore):
'''
Parameters:
{Number} repScore - The minimum reputation needed to verify a hit
Returns:
{Boolean} True if the hit is considered verified.
'''
return self.reputation >= repScore
def create_scoreboard(hits, scale, ringsAmount, innerDiam):
'''
Calculate the score of each detected hit.
Parameters:
{list} hits - [
{tuple} (
{Number} x coordinates of the hit,
{Number} y coordinates of the hit,
{Number} The distance of the hit from the bull'seye
)
...
]
{tuple} scale - (
{Number} The percentage of the warped image's average horizontal edges' length
out of the model's average horizontal edges' length,
{Number} The percentage of the warped image's average vertical edges' length
out of the model's average vertical edges' length,
{Number} The size of the filmed target divided by the model target
)
Returns:
{list} [
{tuple} (
{tuple} (
{Number} x coordinates of the hit,
{Number} y coordinates of the hit
),
{Number} The hit's score according to the target's data
)
...
]
'''
scoreboard = []
for hit in hits:
hit_dist = hit[2]
scaled_diam = innerDiam * scale[2]
score = 10 - int(hit_dist / scaled_diam)
# clamp score between 10 and minimum available score
if score < 10 - ringsAmount + 1:
score = 0
elif score > 10:
score = 10
hit_obj = Hit(int(hit[0]), int(hit[1]), score, hit[3])
scoreboard.append(hit_obj)
return scoreboard
def is_verified_hit(point, distanceTolerance):
'''
Parameters:
{Tuple} point - (
{Number} x coordinate of the point,
{Number} y coordinate of the point
)
{Number} distanceTolerance - Amount of pixels around the point that can be ignored
in order to consider another point as the same one
Returns:
{Boolean} True if the point is of a verified hit.
'''
return type(get_hit(VERIFIED, point, distanceTolerance)) != type(None)
def is_candidate_hit(point, distanceTolerance):
'''
Parameters:
{Tuple} point - (
{Number} x coordinate of the point,
{Number} y coordinate of the point
)
{Number} distanceTolerance - Amount of pixels around the point that can be ignored
in order to consider another point as the same one
Returns:
{Boolean} True if the point is of a known hit that's yet to be verified.
'''
return type(get_hit(CANDIDATE, point, distanceTolerance)) != type(None)
def get_hit(group, point, distanceTolerance):
'''
Parameters:
{Number} group - The group to which the hit belongs
[HitsManager constant (VERIFIED, CANDIDATE)]
{Tuple} point - (
{Number} x coordinate of the point,
{Number} y coordinate of the point
)
{Number} distanceTolerance - Amount of pixels around the point that can be ignored
in order to consider another point as the same one
Returns:
{HitsManager.Hit} The hit that's closest to the given point,
considering the tolarance distance around it.
If no hit is found, this function returns None.
'''
hits_list = get_hits(group)
compatible_hits = []
for hit in hits_list:
if geo2D.euclidean_dist(point, hit.point) <= distanceTolerance:
compatible_hits.append(hit)
if len(compatible_hits) > 0:
return compatible_hits[0]
else:
return None
def eliminate_verified_redundancy(distanceTolerance):
'''
Find duplicate verified hits and eliminate them.
Parameters:
{Number} distanceTolerance - Amount of pixels around a point that can be ignored
in order to consider another point as the same one
'''
if len(verified_hits) <= 1:
return
# create a table of the distances between all hits
table = np.ndarray((len(verified_hits),len(verified_hits)), np.float32)
j_leap = 0
for i in range(len(table)):
for j in range(len(table[i])):
col = j + j_leap
if col >= len(table[i]):
continue
hit_i = verified_hits[i].point
hit_j = verified_hits[col].point
dist = geo2D.euclidean_dist(hit_i, hit_j)
table[i][col] = dist
j_leap += 1
# find distances that are smaller than the threshold and eliminate the redundant hits
j_leap = 0
for i in range(len(table)):
for j in range(len(table[i])):
col = j + j_leap
if col >= len(verified_hits):
continue
if i == col or i >= len(verified_hits):
continue
if table[i][col] < distanceTolerance:
hit_i = verified_hits[i].point
hit_j = verified_hits[col].point
# check the distance from the bull'seye point
bullseye_i = verified_hits[i].bullseye_relation
bullseye_j = verified_hits[col].bullseye_relation
bullseye_dist_i = geo2D.euclidean_dist(hit_i, bullseye_i)
bullseye_dist_j = geo2D.euclidean_dist(hit_j, bullseye_j)
if bullseye_dist_i < bullseye_dist_j:
verified_hits.remove(verified_hits[col])
else:
verified_hits.remove(verified_hits[i])
j_leap += 1
def sort_hit(hit, distanceTolerance, minVerifiedReputation):
'''
Sort a hit and place it in either of the lists.
Increase the reputation of a hit that's already a candidate,
or add a hit as a candidate if it's not already known.
Parameters:
{HitsManager.Hit} hit - The hit to sort
{Number} distanceTolerance - Amount of pixels around a point that can be ignored
in order to consider another point as the same one
{Number} minVerifiedReputation - The minimum reputation needed to verify a hit
'''
candidate = get_hit(CANDIDATE, hit.point, distanceTolerance)
# the hit is a known candidate
if type(candidate) != type(None):
candidate.increase_rep()
candidate.iter_mark = True
# candidate is now eligable for verification
if candidate.isVerified(minVerifiedReputation):
verified_hits.append(candidate)
candidate_hits.remove(candidate)
# find duplicate verified hits and eliminate them
eliminate_verified_redundancy(distanceTolerance)
# new candidate
else:
candidate_hits.append(hit)
hit.iter_mark = True
def discharge_hits():
'''
Lower the reputation of hits that were not detected during the last iteration.
Hits with reputation under 1 are disqualified and removed.
'''
for candidate in candidate_hits:
# candidate is not present during the current iteration
if not candidate.iter_mark:
candidate.decrease_rep()
# candidate disqualified
if candidate.reputation <= 0:
candidate_hits.remove(candidate)
continue
# get ready for the next iteration
candidate.iter_mark = False
def shift_hits(bullseye):
'''
Shift all hits according to the new position of the bull'seye point in the target.
Parameters:
{Tuple} bullseye - (
{Number} current x coordinate of the bull'seye point in the target,
{Number} current y coordinate of the bull'seye point in the target
)
'''
all_hits = candidate_hits + verified_hits
for h in all_hits:
# find the correct translation amount
x_dist = bullseye[0] - h.bullseye_relation[0]
y_dist = bullseye[1] - h.bullseye_relation[1]
new_x = int(round(h.point[0] + x_dist))
new_y = int(round(h.point[1] + y_dist))
# translate and update relation attribute
h.bullseye_relation = bullseye
h.point = (new_x,new_y)
def get_hits(group):
'''
Parameters:
{Number} group - The group to which the hit belongs
[HitsManager constant (VERIFIED, CANDIDATE)]
Returns:
{List} The requested group of hits.
'''
switcher = {
0: candidate_hits,
1: verified_hits
}
return switcher.get(group, [])