28
28
import time
29
29
import math
30
30
import copy
31
+ from typing import Generator
31
32
32
33
from .finder import *
33
- from .target import Target
34
+ from .target import Target , Image
34
35
from .imagelogger import ImageLogger
35
36
from .errors import *
37
+ from .location import Location
36
38
37
39
import logging
38
40
log = logging .getLogger ('guibot.calibrator' )
@@ -56,14 +58,14 @@ class Calibrator(object):
56
58
multiple random starts from a uniform or normal probability distribution.
57
59
"""
58
60
59
- def __init__ (self , needle = None , haystack = None , config = None ):
61
+ def __init__ (self , needle : Target = None , haystack : Image = None ,
62
+ config : str = None ) -> None :
60
63
"""
61
64
Build a calibrator object for a given match case.
62
65
63
- :param haystack: image to look in
64
- :type haystack: :py:class:`target.Image` or None
65
66
:param needle: target to look for
66
- :type needle: :py:class:`target.Target` or None
67
+ :param haystack: image to look in
68
+ :param config: config file for calibration
67
69
"""
68
70
self .cases = []
69
71
if needle is not None and haystack is not None :
@@ -86,21 +88,20 @@ def __init__(self, needle=None, haystack=None, config=None):
86
88
# this attribute can be changed to use different run function
87
89
self .run = self .run_default
88
90
89
- def benchmark (self , finder , random_starts = 0 , uniform = False ,
90
- calibration = False , max_attempts = 3 , ** kwargs ):
91
+ def benchmark (self , finder : Finder , random_starts : int = 0 , uniform : bool = False ,
92
+ calibration : bool = False , max_attempts : int = 3 ,
93
+ ** kwargs : dict [str , type ]) -> list [tuple [str , float , float ]]:
91
94
"""
92
95
Perform benchmarking on all available algorithms of a finder
93
96
for a given needle and haystack.
94
97
95
98
:param finder: CV backend whose backend algorithms will be benchmarked
96
- :type finder: :py:class:`finder.Finder`
97
- :param int random_starts: number of random starts to try with (0 for nonrandom)
98
- :param bool uniform: whether to use uniform or normal distribution
99
- :param bool calibration: whether to use calibration
100
- :param int max_attempts: maximal number of refinements to reach
101
- the parameter delta below the tolerance
102
- :returns: list of (method, similarity, location, time) tuples sorted according to similarity
103
- :rtype: [(str, float, :py:class:`location.Location`, float)]
99
+ :param random_starts: number of random starts to try with (0 for nonrandom)
100
+ :param uniform: whether to use uniform or normal distribution
101
+ :param calibration: whether to use calibration
102
+ :param max_attempts: maximal number of refinements to reach
103
+ the parameter delta below the tolerance
104
+ :returns: list of (method, similarity, time) tuples sorted according to similarity
104
105
105
106
.. note:: Methods that are supported by OpenCV and others but currently don't work
106
107
are excluded from the dictionary. The dictionary can thus also be used to
@@ -120,7 +121,7 @@ def benchmark(self, finder, random_starts=0, uniform=False,
120
121
ordered_categories .remove ("find" )
121
122
122
123
# test all matching methods of the current finder
123
- def backend_tuples (category_list , finder ) :
124
+ def backend_tuples (category_list : list [ str ] , finder : Finder ) -> Generator [ tuple [ str , ...], None , None ] :
124
125
if len (category_list ) == 0 :
125
126
yield ()
126
127
else :
@@ -159,22 +160,20 @@ def backend_tuples(category_list, finder):
159
160
ImageLogger .accumulate_logging = False
160
161
return sorted (results , key = lambda x : x [1 ], reverse = True )
161
162
162
- def search (self , finder , random_starts = 1 , uniform = False ,
163
- calibration = True , max_attempts = 3 , ** kwargs ) :
163
+ def search (self , finder : Finder , random_starts : int = 1 , uniform : bool = False ,
164
+ calibration : bool = True , max_attempts : int = 3 , ** kwargs : dict [ str , type ]) -> float :
164
165
"""
165
166
Search for the best match configuration for a given needle and haystack
166
167
using calibration from random initial conditions.
167
168
168
169
:param finder: CV backend to use in order to determine deltas, fixed, and free
169
170
parameters and ultimately tweak to minimize error
170
- :type finder: :py:class:`finder.Finder`
171
- :param int random_starts: number of random starts to try with
172
- :param bool uniform: whether to use uniform or normal distribution
173
- :param bool calibration: whether to use calibration
174
- :param int max_attempts: maximal number of refinements to reach
175
- the parameter delta below the tolerance
171
+ :param random_starts: number of random starts to try with
172
+ :param uniform: whether to use uniform or normal distribution
173
+ :param calibration: whether to use calibration
174
+ :param max_attempts: maximal number of refinements to reach
175
+ the parameter delta below the tolerance
176
176
:returns: maximized similarity
177
- :rtype: float
178
177
179
178
If normal distribution is used, the mean will be the current value of the
180
179
respective CV parameter and the standard variation will be determined from
@@ -225,17 +224,15 @@ def search(self, finder, random_starts=1, uniform=False,
225
224
category , key , param .value , param .delta )
226
225
return 1.0 - best_error
227
226
228
- def calibrate (self , finder , max_attempts = 3 , ** kwargs ) :
227
+ def calibrate (self , finder : Finder , max_attempts : int = 3 , ** kwargs : dict [ str , type ]) -> float :
229
228
"""
230
229
Calibrate the available match configuration for a given needle
231
230
and haystack minimizing the matchign error.
232
231
233
232
:param finder: configuration for the CV backend to calibrate
234
- :type finder: :py:class:`finder.Finder`
235
- :param int max_attempts: maximal number of refinements to reach
236
- the parameter delta below the tolerance
233
+ :param max_attempts: maximal number of refinements to reach
234
+ the parameter delta below the tolerance
237
235
:returns: maximized similarity
238
- :rtype: float
239
236
240
237
This method calibrates only parameters that are not protected
241
238
from calibration, i.e. that have `fixed` attribute set to false.
@@ -291,17 +288,17 @@ def calibrate(self, finder, max_attempts=3, **kwargs):
291
288
# add the delta to the current parameter
292
289
if isinstance (param .value , float ):
293
290
if param .range [1 ] is not None :
294
- param .value = min (start_value + param .delta ,
291
+ param .value = min (float ( start_value ) + param .delta ,
295
292
param .range [1 ])
296
293
else :
297
- param .value = start_value + param .delta
294
+ param .value = float ( start_value ) + param .delta
298
295
elif isinstance (param .value , int ) and not param .enumerated :
299
296
intdelta = int (math .ceil (param .delta ))
300
297
if param .range [1 ] is not None :
301
- param .value = min (start_value + intdelta ,
298
+ param .value = min (int ( start_value ) + intdelta ,
302
299
param .range [1 ])
303
300
else :
304
- param .value = start_value + intdelta
301
+ param .value = int ( start_value ) + intdelta
305
302
# remaining types require special handling
306
303
elif isinstance (param .value , int ) and param .enumerated :
307
304
delta_coeff = 0.9
@@ -339,17 +336,17 @@ def calibrate(self, finder, max_attempts=3, **kwargs):
339
336
340
337
if isinstance (param .value , float ):
341
338
if param .range [0 ] is not None :
342
- param .value = max (start_value - param .delta ,
339
+ param .value = max (float ( start_value ) - param .delta ,
343
340
param .range [0 ])
344
341
else :
345
- param .value = start_value - param .delta
342
+ param .value = float ( start_value ) - param .delta
346
343
elif isinstance (param .value , int ):
347
344
intdelta = int (math .floor (param .delta ))
348
345
if param .range [0 ] is not None :
349
- param .value = max (start_value - intdelta ,
346
+ param .value = max (int ( start_value ) - intdelta ,
350
347
param .range [0 ])
351
348
else :
352
- param .value = start_value - intdelta
349
+ param .value = int ( start_value ) - intdelta
353
350
elif isinstance (param .value , bool ):
354
351
# the default boolean value was already checked
355
352
param .value = start_value
@@ -388,14 +385,12 @@ def calibrate(self, finder, max_attempts=3, **kwargs):
388
385
category , key , param .value , param .delta )
389
386
return 1.0 - best_error
390
387
391
- def run_default (self , finder , ** _kwargs ) :
388
+ def run_default (self , finder : Finder , ** _kwargs : dict [ str , type ]) -> float :
392
389
"""
393
390
Run a match case and return error from the match as dissimilarity.
394
391
395
392
:param finder: finder with match configuration to use for the run
396
- :type finder: :py:class:`finder.Finder`
397
393
:returns: error obtained as unity minus similarity
398
- :rtype: float
399
394
"""
400
395
self ._handle_restricted_values (finder )
401
396
@@ -414,20 +409,16 @@ def run_default(self, finder, **_kwargs):
414
409
error = 1.0 - total_similarity / len (self .cases )
415
410
return error
416
411
417
- def run_performance (self , finder , ** kwargs ) :
412
+ def run_performance (self , finder : Finder , ** kwargs : dict [ str , type ]) -> float :
418
413
"""
419
414
Run a match case and return error from the match as dissimilarity
420
415
and linear performance penalty.
421
416
422
417
:param finder: finder with match configuration to use for the run
423
- :type finder: :py:class:`finder.Finder`
424
- :param float max_exec_time: maximum execution time before penalizing
425
- the run by increasing the error linearly
426
418
:returns: error obtained as unity minus similarity
427
- :rtype: float
428
419
"""
429
420
self ._handle_restricted_values (finder )
430
- max_exec_time = kwargs .get ("max_exec_time" , 1.0 )
421
+ max_exec_time : float = kwargs .get ("max_exec_time" , 1.0 )
431
422
432
423
total_similarity = 0.0
433
424
for needle , haystack , maximize in self .cases :
@@ -449,18 +440,13 @@ def run_performance(self, finder, **kwargs):
449
440
error += max (total_time - max_exec_time , 0 )
450
441
return error
451
442
452
- def run_peak (self , finder , ** kwargs ) :
443
+ def run_peak (self , finder : Finder , ** kwargs : dict [ str , type ]) -> float :
453
444
"""
454
445
Run a match case and return error from the match as failure to obtain
455
446
high similarity of one match and low similarity of all others.
456
447
457
448
:param finder: finder with match configuration to use for the run
458
- :type finder: :py:class:`finder.Finder`
459
- :param peak_location: (x, y) of the match whose similarity should be
460
- maximized while all the rest minimized
461
- :type peak_location: (int, int)
462
449
:returns: error obtained as unity minus similarity
463
- :rtype: float
464
450
465
451
This run function doesn't just obtain the optimum similarity for the best
466
452
match in each case of needle and haystack but it minimizes the similarity
@@ -495,7 +481,7 @@ def run_peak(self, finder, **kwargs):
495
481
error = 1.0 - total_similarity / len (self .cases )
496
482
return error
497
483
498
- def _handle_restricted_values (self , finder ) :
484
+ def _handle_restricted_values (self , finder : Finder ) -> None :
499
485
if "threshold" in finder .params :
500
486
params = finder .params ["threshold" ]
501
487
if params ["blurKernelSize" ].value % 2 == 0 :
@@ -524,7 +510,7 @@ def _handle_restricted_values(self, finder):
524
510
diffs = {m : abs (m - params ["dt_mask_size" ].value ) for m in [0 , 3 , 5 ]}
525
511
params ["dt_mask_size" ].value = min (diffs , key = diffs .get )
526
512
527
- def _prepare_params (self , finder ) :
513
+ def _prepare_params (self , finder : Finder ) -> None :
528
514
# any similarity parameters will be reset to 0.0 to search optimally
529
515
finder .params ["find" ]["similarity" ].value = 0.0
530
516
finder .params ["find" ]["similarity" ].fixed = True
0 commit comments