Skip to content

Commit ee60cda

Browse files
authored
Merge pull request #81 from AutomateThePlanet/navramov_MM
add precision threshold and grayscale options for OpenCV Service
2 parents c5271d7 + 5a395db commit ee60cda

File tree

10 files changed

+79
-22
lines changed

10 files changed

+79
-22
lines changed

bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/components/ActionImage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public Point getLocation() {
3232

3333
var encodedImage = findStrategy.getEncodedImage();
3434

35-
var location = OpenCvService.getLocation(encodedImage, true);
35+
var location = OpenCvService.getLocation(encodedImage);
3636
return new Point((int)location.x + encodedImage.getXOffset(), (int)location.y + encodedImage.getYOffset());
3737
}
3838

bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/findstrategies/ImageBase64FindStrategy.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@
1616
import com.microsoft.playwright.Locator;
1717
import com.microsoft.playwright.Page;
1818
import lombok.Getter;
19-
import solutions.bellatrix.core.utilities.SingletonFactory;
2019
import solutions.bellatrix.core.utilities.parsing.TypeParser;
2120
import solutions.bellatrix.playwright.components.common.webelement.WebElement;
22-
import solutions.bellatrix.playwright.services.JavaScriptService;
2321
import solutions.bellatrix.plugins.opencv.Base64Encodable;
2422
import solutions.bellatrix.plugins.opencv.OpenCvService;
2523

26-
import java.awt.*;
2724
import java.util.ArrayList;
2825
import java.util.List;
2926

@@ -37,7 +34,7 @@ public ImageBase64FindStrategy(Base64Encodable encodedImage) {
3734

3835
@Override
3936
public WebElement convert(Page page) {
40-
var location = OpenCvService.getLocation(encodedImage, false);
37+
var location = OpenCvService.getLocation(encodedImage);
4138

4239
var foundLocators = findElementsOn(location, page);
4340

@@ -48,7 +45,7 @@ public WebElement convert(Page page) {
4845

4946
@Override
5047
public WebElement convert(WebElement webElement) {
51-
var location = OpenCvService.getLocation(encodedImage, false);
48+
var location = OpenCvService.getLocation(encodedImage);
5249

5350
var foundLocators = findElementsOn(location, webElement.page());
5451

bellatrix.plugins.opencv/src/main/java/solutions/bellatrix/plugins/opencv/OpenCvService.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.opencv.imgcodecs.Imgcodecs;
99
import org.opencv.imgproc.Imgproc;
1010
import plugins.screenshots.ScreenshotPlugin;
11+
import solutions.bellatrix.core.configuration.ConfigurationService;
12+
import solutions.bellatrix.core.utilities.Log;
1113
import solutions.bellatrix.core.utilities.SingletonFactory;
1214

1315
import javax.imageio.ImageIO;
@@ -39,16 +41,24 @@ public static double getJavaMonitorScaling() {
3941
return scaleX;
4042
}
4143

44+
public static Point getLocation(Base64Encodable encodedImage) {
45+
OpenCvServiceSettings openCvServiceSettings = ConfigurationService.get(OpenCvServiceSettings.class);
46+
if (openCvServiceSettings == null) {
47+
openCvServiceSettings = new OpenCvServiceSettings();
48+
}
49+
50+
var precisionThreshold = openCvServiceSettings.getDefaultMatchThreshold();
51+
var shouldGrayscale = openCvServiceSettings.isShouldGrayscale();
52+
return getLocation(encodedImage, shouldGrayscale, precisionThreshold);
53+
}
4254
/**
4355
* @return the coordinates of the image found on the screen
4456
*/
45-
public static Point getLocation(Base64Encodable encodedImage, boolean shouldGrayScale) {
57+
public static Point getLocation(Base64Encodable encodedImage, boolean shouldGrayScale, double precisionThreshold) {
4658
var screenshotPlugin = SingletonFactory.getInstance(ScreenshotPlugin.class);
47-
59+
Log.info("Locating image using precision: %s; Should Grayscale = %s".formatted(precisionThreshold, shouldGrayScale));
4860
if (screenshotPlugin == null) {
49-
throw new IllegalArgumentException("It seems that the screenshot plugin isn't registered by the 'ScreenshotPlugin.class' key inside SingletonFactory's map or isn't registered at all!\n" +
50-
"Check the BaseTest class of your project where the plugins are registered. Register the specific screenshot plugin implementation as the base ScreenshotPlugin.class.\n" +
51-
"for example: addPluginAs(ScreenshotPlugin.class, WebScreenshotPlugin.class);");
61+
throw new IllegalArgumentException("Screenshot plugin not registered!");
5262
}
5363

5464
var screenshot = screenshotPlugin.takeScreenshot();
@@ -57,13 +67,17 @@ public static Point getLocation(Base64Encodable encodedImage, boolean shouldGray
5767

5868
Mat result = loadImages(encodedImage, screenshot, shouldGrayScale);
5969

60-
return getMatchLocation(encodedImage, result);
70+
return getMatchLocation(encodedImage, result, precisionThreshold);
6171
}
6272

63-
private static Point getMatchLocation(Base64Encodable encodedImage, Mat result) {
73+
private static Point getMatchLocation(Base64Encodable encodedImage, Mat result, double precisionThreshold) {
6474
BufferedImage bufferedImage;
65-
6675
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
76+
if (mmr.maxVal < precisionThreshold) {
77+
throw new RuntimeException("Match not found above threshold. Closest match at: %s".formatted(mmr.maxVal));
78+
}
79+
Log.info("Image located with precision: %s".formatted(mmr.maxVal));
80+
6781
Point matchLoc = mmr.maxLoc;
6882

6983
if (encodedImage.getXOffset() == 0 && encodedImage.getYOffset() == 0) {
@@ -73,13 +87,17 @@ private static Point getMatchLocation(Base64Encodable encodedImage, Mat result)
7387
throw new RuntimeException(e);
7488
}
7589

76-
double[] imageCenterCoordinates = {matchLoc.x / getJavaMonitorScaling() + (double)(bufferedImage.getWidth() / 2), matchLoc.y / getJavaMonitorScaling() + (double)(bufferedImage.getHeight() / 2)};
90+
double[] imageCenterCoordinates = {
91+
matchLoc.x / getJavaMonitorScaling() + (double)(bufferedImage.getWidth() / 2),
92+
matchLoc.y / getJavaMonitorScaling() + (double)(bufferedImage.getHeight() / 2)
93+
};
7794
matchLoc.set(imageCenterCoordinates);
7895
}
7996

8097
return matchLoc;
8198
}
8299

100+
83101
private static BufferedImage getImageWidthHeight(Base64Encodable encodedImage) throws IOException {
84102
String cleanBase64 = removePrefixFromBase64(encodedImage);
85103

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package solutions.bellatrix.plugins.opencv;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Getter;
5+
6+
@Getter
7+
public class OpenCvServiceSettings {
8+
@SerializedName("defaultMatchThreshold")
9+
private double defaultMatchThreshold = 0.8;
10+
@SerializedName("shouldGrayscale")
11+
private boolean shouldGrayscale = true;
12+
}

bellatrix.web/src/main/java/solutions/bellatrix/web/components/ActionImage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public Point getLocation() {
3030

3131
var encodedImage = findStrategy.getEncodedImage();
3232

33-
var location = OpenCvService.getLocation(encodedImage, true);
33+
var location = OpenCvService.getLocation(encodedImage);
3434
return new Point((int)location.x + encodedImage.getXOffset(), (int)location.y + encodedImage.getYOffset());
3535
}
3636

bellatrix.web/src/main/java/solutions/bellatrix/web/findstrategies/ImageBase64FindStrategy.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,39 @@
44
import org.openqa.selenium.By;
55
import org.openqa.selenium.SearchContext;
66
import org.openqa.selenium.WebElement;
7+
import solutions.bellatrix.core.configuration.ConfigurationService;
78
import solutions.bellatrix.core.utilities.Log;
89
import solutions.bellatrix.core.utilities.SingletonFactory;
910
import solutions.bellatrix.plugins.opencv.Base64Encodable;
1011
import solutions.bellatrix.plugins.opencv.OpenCvService;
11-
import solutions.bellatrix.web.services.App;
12+
import solutions.bellatrix.plugins.opencv.OpenCvServiceSettings;
1213
import solutions.bellatrix.web.services.JavaScriptService;
1314

1415
import java.util.List;
15-
import java.util.Objects;
1616

1717
@Getter
1818
public class ImageBase64FindStrategy extends FindStrategy {
1919
private final Base64Encodable encodedImage;
20+
private final boolean shouldGrayScale;
21+
private final double precisionThreshold;
2022

2123
public ImageBase64FindStrategy(Base64Encodable encodedImage) {
2224
super(encodedImage.getBase64Image());
2325
this.encodedImage = encodedImage;
26+
var serviceSettings = ConfigurationService.get(OpenCvServiceSettings.class);
27+
if (serviceSettings == null) {
28+
serviceSettings = new OpenCvServiceSettings();
29+
}
30+
31+
this.shouldGrayScale = serviceSettings.isShouldGrayscale();
32+
this.precisionThreshold = serviceSettings.getDefaultMatchThreshold();
33+
}
34+
35+
public ImageBase64FindStrategy(Base64Encodable encodedImage, boolean shouldGrayScale, double precisionThreshold) {
36+
super(encodedImage.getBase64Image());
37+
this.shouldGrayScale = shouldGrayScale;
38+
this.precisionThreshold = precisionThreshold;
39+
this.encodedImage = encodedImage;
2440
}
2541

2642
@Override
@@ -46,7 +62,7 @@ public static By byImageBase64(Base64Encodable encodedImage) {
4662

4763
@Override
4864
public List<WebElement> findElements(SearchContext context) {
49-
var location = OpenCvService.getLocation(base64EncodedImage, false);
65+
var location = OpenCvService.getLocation(base64EncodedImage);
5066
Log.info("Coordinates located: %s", location.toString());
5167
return SingletonFactory.getInstance(JavaScriptService.class).<List<WebElement>>genericExecute("return document.elementsFromPoint(%s, %s);".formatted(location.x, location.y));
5268
}

bellatrix.web/src/main/java/solutions/bellatrix/web/services/ComponentCreateService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,8 @@
1515

1616
import solutions.bellatrix.core.utilities.InstanceFactory;
1717
import solutions.bellatrix.plugins.opencv.Base64Encodable;
18-
import solutions.bellatrix.web.components.ActionImage;
1918
import solutions.bellatrix.web.components.WebComponent;
2019
import solutions.bellatrix.web.findstrategies.*;
21-
import solutions.bellatrix.web.infrastructure.DriverService;
2220

2321
import java.util.ArrayList;
2422
import java.util.List;
@@ -42,6 +40,10 @@ public <TComponent extends WebComponent> TComponent byImage(Class<TComponent> co
4240
return by(componentClass, new ImageBase64FindStrategy(encodedImage));
4341
}
4442

43+
public <TComponent extends WebComponent> TComponent byImage(Class<TComponent> componentClass, Base64Encodable encodedImage, boolean shouldGrayscale, double matchPrecision) {
44+
return by(componentClass, new ImageBase64FindStrategy(encodedImage, shouldGrayscale, matchPrecision));
45+
}
46+
4547
public <TComponent extends WebComponent> TComponent byAttributeContaining(Class<TComponent> componentClass, String attributeName, String value) {
4648
return by(componentClass, new AttributeContainingWithFindStrategy(attributeName, value));
4749
}
@@ -106,6 +108,10 @@ public <TComponent extends WebComponent> List<TComponent> allByImage(Class<TComp
106108
return allBy(componentClass, new ImageBase64FindStrategy(encodedImage));
107109
}
108110

111+
public <TComponent extends WebComponent> List<TComponent> allByImage(Class<TComponent> componentClass, Base64Encodable encodedImage, boolean shouldGrayscale, double matchPrecision) {
112+
return allBy(componentClass, new ImageBase64FindStrategy(encodedImage, shouldGrayscale, matchPrecision));
113+
}
114+
109115
public <TComponent extends WebComponent> List<TComponent> allByAttributeContaining(Class<TComponent> componentClass, String attributeName, String value) {
110116
return allBy(componentClass, new AttributeContainingWithFindStrategy(attributeName, value));
111117
}

framework-tests/bellatrix.web.tests/src/main/resources/testFrameworkSettings.dev.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
"shopUrl": "http://demos.bellatrix.solutions/cart/",
111111
"accountUrl": "http://demos.bellatrix.solutions/account/"
112112
},
113+
"openCvServiceSettings": {
114+
"shouldGrayscale": true,
115+
"defaultMatchThreshold": "0.9"
116+
},
113117
"testPagesSettings": {
114118
"anchorLocalPage": "testpages/anchor/anchor.html",
115119
"buttonLocalPage": "testpages/button/button.html",

framework-tests/bellatrix.web.tests/src/main/resources/testFrameworkSettings.qa.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
"shopUrl": "http://demos.bellatrix.solutions/cart/",
111111
"accountUrl": "http://demos.bellatrix.solutions/account/"
112112
},
113+
"openCvServiceSettings": {
114+
"shouldGrayscale": true,
115+
"defaultMatchThreshold": "0.9"
116+
},
113117
"testPagesSettings": {
114118
"anchorLocalPage": "testpages/anchor/anchor.html",
115119
"buttonLocalPage": "testpages/button/button.html",

framework-tests/bellatrix.web.tests/src/test/java/opencv/OpenCvTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void beforeEach() throws Exception {
2222

2323
@Test
2424
public void actionPerformed_when_convertBase64ToImage_and_clickImage() {
25-
var falcon9Image = app().create().byImage(Anchor.class, EncodedImageDemo.FALCON_9);
25+
var falcon9Image = app().create().byImage(Anchor.class, EncodedImageDemo.FALCON_9, false, 0.99);
2626

2727
app().navigate().to("http://demos.bellatrix.solutions/");
2828
falcon9Image.click();

0 commit comments

Comments
 (0)