Skip to content

Commit 3dcf029

Browse files
committed
[WIP] Implement ByImage locator
1 parent 36f3c4b commit 3dcf029

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@
9696
<version>2.14.2</version>
9797
</dependency>
9898

99+
<dependency>
100+
<groupId>org.openpnp</groupId>
101+
<artifactId>opencv</artifactId>
102+
<version>[4.7.0,)</version>
103+
</dependency>
104+
99105
<dependency>
100106
<groupId>org.testng</groupId>
101107
<artifactId>testng</artifactId>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package aquality.selenium.elements.interfaces;
2+
3+
import aquality.selenium.browser.AqualityServices;
4+
import org.opencv.core.Core;
5+
import org.opencv.core.Mat;
6+
import org.opencv.core.MatOfByte;
7+
import org.opencv.core.Point;
8+
import org.opencv.imgcodecs.Imgcodecs;
9+
import org.opencv.imgproc.Imgproc;
10+
import org.openqa.selenium.*;
11+
12+
import java.io.File;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
public class ByImage extends By {
17+
private static boolean wasLibraryLoaded = false;
18+
private final Mat template;
19+
20+
private static void loadLibrary() {
21+
if (!wasLibraryLoaded) {
22+
nu.pattern.OpenCV.loadShared();
23+
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
24+
wasLibraryLoaded = true;
25+
}
26+
}
27+
28+
public ByImage(File file) {
29+
loadLibrary();
30+
this.template = Imgcodecs.imread(file.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
31+
}
32+
33+
public ByImage(byte[] bytes) {
34+
loadLibrary();
35+
this.template = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_UNCHANGED);
36+
}
37+
38+
@Override
39+
public List<WebElement> findElements(SearchContext context) {
40+
byte[] screenshotBytes = getScreenshot(context);
41+
Mat source = Imgcodecs.imdecode(new MatOfByte(screenshotBytes), Imgcodecs.IMREAD_UNCHANGED);
42+
Mat result = new Mat();
43+
Imgproc.matchTemplate(source, template, result, Imgproc.TM_CCOEFF_NORMED);
44+
45+
float threshold = 1 - AqualityServices.getConfiguration().getVisualizationConfiguration().getDefaultThreshold();
46+
Core.MinMaxLocResult minMaxLoc = Core.minMaxLoc(result);
47+
48+
if (minMaxLoc.maxVal < threshold) {
49+
AqualityServices.getLogger().warn(String.format("No elements found by image [%s]", template));
50+
return new ArrayList<>(0);
51+
}
52+
53+
return getElementsOnPoint(minMaxLoc.maxLoc, context);
54+
}
55+
56+
private List<WebElement> getElementsOnPoint(Point matchLocation, SearchContext context) {
57+
int centerX = (int)(matchLocation.x + (template.width() / 2));
58+
int centerY = (int)(matchLocation.y + (template.height() / 2));
59+
60+
JavascriptExecutor js;
61+
if (!(context instanceof JavascriptExecutor)) {
62+
AqualityServices.getLogger().debug("Current search context doesn't support executing scripts. " +
63+
"Will take browser js executor instead");
64+
js = AqualityServices.getBrowser().getDriver();
65+
}
66+
else {
67+
js = (JavascriptExecutor) context;
68+
}
69+
70+
//noinspection unchecked
71+
return (List<WebElement>) js.executeScript("return document.elementsFromPoint(arguments[0], arguments[1]);", centerX, centerY);
72+
}
73+
74+
private byte[] getScreenshot(SearchContext context) {
75+
byte[] screenshotBytes;
76+
77+
if (!(context instanceof TakesScreenshot)) {
78+
AqualityServices.getLogger().debug("Current search context doesn't support taking screenshots. " +
79+
"Will take browser screenshot instead");
80+
screenshotBytes = AqualityServices.getBrowser().getScreenshot();
81+
} else {
82+
screenshotBytes = ((TakesScreenshot) context).getScreenshotAs(OutputType.BYTES);
83+
}
84+
85+
return screenshotBytes;
86+
}
87+
}

0 commit comments

Comments
 (0)