2
# Copyright (c) 2010 Canonical
4
# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
6
# This file is part of the Xpresser GUI automation library.
8
# Xpresser is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU Lesser General Public License version 3,
10
# as published by the Free Software Foundation.
12
# Xpresser is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
from xpresser.image import Image
24
from xpresser.opencvfinder import OpenCVFinder
26
from xpresser.lib.testing import TestCase
27
from xpresser.tests.images import get_image_path
30
class OpenCVFinderTest(TestCase):
33
self.screen_image = Image(filename=get_image_path("screen.png"))
34
self.red_square = Image(filename=get_image_path("red-square.png"))
35
self.red_circle = Image(filename=get_image_path("red-circle.png"))
36
self.red_ellipse = Image(filename=get_image_path("red-ellipse.png"))
37
self.green_square = Image(filename=get_image_path("green-square.png"))
38
self.yellow_square = Image(filename=get_image_path("yellow-square.png"))
39
self.yellow_circle = Image(filename=get_image_path("yellow-circle.png"))
40
self.red_circle_with_blue_circle = \
41
Image(filename=get_image_path("red-circle-with-blue-circle.png"))
42
self.finder = OpenCVFinder()
44
def test_loads_widths_and_heights(self):
45
self.finder.find(self.screen_image, self.red_ellipse)
46
self.assertEquals(self.screen_image.width, 300)
47
self.assertEquals(self.screen_image.height, 300)
48
self.assertEquals(self.red_ellipse.width, 40)
49
self.assertEquals(self.red_ellipse.height, 50)
51
def test_find_perfect_match(self):
52
match = self.finder.find(self.screen_image, self.green_square)
53
self.assertEquals(match.image, self.green_square)
54
self.assertEquals(match.x, 200)
55
self.assertEquals(match.y, 0)
56
self.assertEquals(match.similarity, 1.0)
58
def test_find_perfect_match_with_low_threshold(self):
59
self.green_square.similarity = 0.7
60
match = self.finder.find(self.screen_image, self.green_square)
61
self.assertEquals(match.image, self.green_square)
62
self.assertEquals(match.x, 200)
63
self.assertEquals(match.y, 0)
65
def test_find_all_with_perfect_match(self):
66
matches = self.finder.find_all(self.screen_image, self.green_square)
67
self.assertEquals(len(matches), 1)
68
self.assertEquals(matches[0].image, self.green_square)
69
self.assertEquals(matches[0].x, 200)
70
self.assertEquals(matches[0].y, 0)
71
self.assertEquals(matches[0].similarity, 1.0)
73
def test_find_all_with_low_threshold_containing_perfect_match(self):
74
self.red_circle.similarity = 0.8
75
matches = self.finder.find_all(self.screen_image, self.red_circle)
76
self.assertTrue(len(matches) > 1)
77
self.assertTrue(min(m.similarity for m in matches) >= 0.8)
79
def test_no_matches(self):
80
match = self.finder.find(self.screen_image,
81
self.red_circle_with_blue_circle)
82
self.assertEquals(match, None)
84
def test_fuzzy_match(self):
85
self.red_circle_with_blue_circle.similarity = 0.9
86
match = self.finder.find(self.screen_image,
87
self.red_circle_with_blue_circle)
88
self.assertEquals(match.image, self.red_circle_with_blue_circle)
89
self.assertEquals(match.x, 100)
90
self.assertEquals(match.y, 200)
92
def test_self_match(self):
94
This test will explore a bug in the Python OpenCV bindings. It
95
will handle the dimensions of the result matrix in a different
96
way when there's a single result.
98
match = self.finder.find(self.red_circle, self.red_circle)
99
self.assertEquals(match.x, 0)
100
self.assertEquals(match.y, 0)
102
def test_opencv_image_cache(self):
103
match = self.finder.find(self.red_circle, self.yellow_circle)
104
opencv_image = self.red_circle.cache.get("opencv_image")
105
self.assertEquals(match, None)
106
self.assertNotEquals(opencv_image, None)
107
self.assertEquals(type(opencv_image), opencv.CvMat)
109
# Let's ensure the cache is *actually* in use.
110
self.red_circle.cache["opencv_image"] = \
111
self.yellow_circle.cache["opencv_image"]
113
match = self.finder.find(self.red_circle, self.yellow_circle)
114
self.assertNotEquals(match, None)
116
def test_filtering_of_similar_matches(self):
118
This example would actually have hundreds of matches if there was
119
no filtering per proximity and match quality. The filtering
120
algorithm is not entirely trivial, and likely has other cases
121
which need to be covered by individual unit tests too.
123
self.red_circle.similarity = 0.8
124
matches = self.finder.find_all(self.screen_image, self.red_circle)
125
matches.sort(key=lambda match: -match.similarity)
126
self.assertEquals(len(matches), 2)
127
self.assertEquals(matches[0].x, 100)
128
self.assertEquals(matches[0].y, 200)
129
self.assertEquals(matches[1].x, 198)
130
self.assertEquals(matches[1].y, 100)
132
def test_find_with_array_image(self):
133
# Reset the image, including the cache (shouldn't be needed, but
134
# just to be 100% sure).
135
self.green_square.cache.clear()
136
filename = self.green_square.filename
137
self.green_square.filename = None
139
# Use gtk to transform the image into a numpy array, and set it
140
# back into the image.
141
pixbuf = gtk.image_new_from_file(filename).get_pixbuf()
142
self.green_square.array = pixbuf.get_pixels_array()
144
# Try to match normally.
145
match = self.finder.find(self.screen_image, self.green_square)
146
self.assertEquals(match.image, self.green_square)
147
self.assertEquals(match.x, 200)
148
self.assertEquals(match.y, 0)
149
self.assertEquals(match.similarity, 1.0)