Skip to content

Commit

Permalink
Add OpenCV Confidence level (#43)
Browse files Browse the repository at this point in the history
* Updated _recognize_images to use confidence

* updated gitignore

* Making confidence part of initialization

* Default confidence

* Added set confidence keyword

* Tests and bounds checkijng

* Had error

* Allow for not using confidence at all

* Unit test for confidence

* warning message if a user sets confidence but doesn't have opencv

* Added test for not having OpenCV

Co-authored-by: Jon Koser <[email protected]>
  • Loading branch information
2 people authored and matejc committed May 4, 2020
1 parent 85993c6 commit 666ca48
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ log.html
*-screenshot-*.png
*\$py.class
*.swp
*__pycache__
31 changes: 30 additions & 1 deletion src/ImageHorizonLibrary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ class ImageHorizonLibrary(_Keyboard,
ROBOT_LIBRARY_VERSION = VERSION

def __init__(self, reference_folder=None, screenshot_folder=None,
keyword_on_failure='ImageHorizonLibrary.Take A Screenshot'):
keyword_on_failure='ImageHorizonLibrary.Take A Screenshot',
confidence=None):
'''ImageHorizonLibrary can be imported with several options.
``reference_folder`` is path to the folder where all reference images
Expand All @@ -111,6 +112,11 @@ def __init__(self, reference_folder=None, screenshot_folder=None,
``keyword_on_failure`` is the keyword to be run, when location-related
keywords fail. If you wish to not take screenshots, use for example
`BuiltIn.No Operation`. Keyword must however be a valid keyword.
``confidence`` provides a tolerance for the ``reference_image``.
It can be used if python-opencv is installed and
is given as number between 0 and 1. Not used
by default.
'''

self.reference_folder = reference_folder
Expand All @@ -122,6 +128,8 @@ def __init__(self, reference_folder=None, screenshot_folder=None,
self.is_mac = utils.is_mac()
self.is_linux = utils.is_linux()
self.has_retina = utils.has_retina()
self.has_cv = utils.has_cv()
self.confidence = confidence

def _get_location(self, direction, location, offset):
x, y = location
Expand Down Expand Up @@ -232,3 +240,24 @@ def set_screenshot_folder(self, screenshot_folder_path):
See `library importing` for more specific information.
'''
self.screenshot_folder = screenshot_folder_path

def set_confidence(self, new_confidence):
'''Sets the confidence level for finding images
Applicable if opencv (python-opencv) is installed.
Allows for setting the value to None if you don't want
to use it or a value between 0 and 1 inclusive.
'''
if new_confidence is not None:
try:
new_confidence = float(new_confidence)
if not 1 >= new_confidence >= 0:
LOGGER.warn('Unable to set confidence to {}. Value '
'must be between 0 and 1, inclusive.'
.format(new_confidence))
else:
self.confidence = new_confidence
except TypeError as err:
LOGGER.warn("Can't set confidence to {}".format(new_confidence))
else:
self.confidence = None

9 changes: 8 additions & 1 deletion src/ImageHorizonLibrary/recognition/_recognize_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,14 @@ def try_locate(ref_image):
location = None
with self._suppress_keyword_on_failure():
try:
location = ag.locateOnScreen(ref_image)
if self.has_cv and self.confidence:
location = ag.locateOnScreen(ref_image,
confidence=self.confidence)
else:
if self.confidence:
LOGGER.warn("Can't set confidence because you don't "
"have OpenCV (python-opencv) installed.")
location = ag.locateOnScreen(ref_image)
except ImageNotFoundException as ex:
LOGGER.info(ex)
pass
Expand Down
8 changes: 8 additions & 0 deletions src/ImageHorizonLibrary/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ def has_retina():
# Will return 0 if there is a retina display
return call("system_profiler SPDisplaysDataType | grep 'Retina'", shell=True) == 0
return False

def has_cv():
has_cv = True
try:
import cv2
except ModuleNotFoundError as err:
has_cv = False
return has_cv
23 changes: 22 additions & 1 deletion tests/utest/test_main_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,25 @@ def test_set_reference_folder(self):
def test_set_screenshot_folder(self):
self.assertEqual(self.lib.screenshot_folder, None)
self.lib.set_screenshot_folder('/test/path')
self.assertEqual(self.lib.screenshot_folder, '/test/path')
self.assertEqual(self.lib.screenshot_folder, '/test/path')

def test_set_confidence(self):
self.assertEqual(self.lib.confidence, None)

self.lib.set_confidence(0)
self.assertEqual(self.lib.confidence, 0)

self.lib.set_confidence(0.5)
self.assertEqual(self.lib.confidence, 0.5)

self.lib.set_confidence(-1)
self.assertEqual(self.lib.confidence, 0.5)

self.lib.set_confidence(2)
self.assertEqual(self.lib.confidence, 0.5)

self.lib.set_confidence(1)
self.assertEqual(self.lib.confidence, 1)

self.lib.set_confidence(None)
self.assertEqual(self.lib.confidence, None)
19 changes: 18 additions & 1 deletion tests/utest/test_recognize_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
CURDIR = abspath(dirname(__file__))
TESTIMG_DIR = path_join(CURDIR, 'reference_images')


class TestRecognizeImages(TestCase):
def setUp(self):
self.mock = MagicMock()
Expand All @@ -23,6 +22,24 @@ def tearDown(self):
self.mock.reset_mock()
self.patcher.stop()

def test_find_with_confidence(self):
self.lib.reference_folder = path_join(CURDIR, 'symbolic_link')
self.lib.set_confidence(0.5)
self.lib.has_cv = True
self.lib.locate('mY_PiCtURe')
expected_path = path_join(CURDIR, 'symbolic_link', 'my_picture.png')
self.mock.locateOnScreen.assert_called_once_with(expected_path, confidence=0.5)
self.mock.reset_mock()

def test_find_with_confidence_no_opencv(self):
self.lib.reference_folder = path_join(CURDIR, 'symbolic_link')
self.lib.set_confidence(0.5)
self.lib.has_cv = False
self.lib.locate('mY_PiCtURe')
expected_path = path_join(CURDIR, 'symbolic_link', 'my_picture.png')
self.mock.locateOnScreen.assert_called_once_with(expected_path)
self.mock.reset_mock()

def test_click_image(self):
with patch(self.locate, return_value=(0, 0)):
self.lib.click_image('my_picture')
Expand Down

0 comments on commit 666ca48

Please sign in to comment.