
imagerecognition.ImageRecognition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mobile-opencv-image-recognition-library Show documentation
Show all versions of mobile-opencv-image-recognition-library Show documentation
Library for image recognition that can be used in mobile testing with for example Appium
The newest version!
package imagerecognition;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import org.apache.commons.io.FilenameUtils;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import objects.ImageLocation;
import objects.ImageRecognitionSettings;
import objects.ImageSearchResult;
import objects.PlatformType;
public class ImageRecognition {
private static Logger logger = LoggerFactory.getLogger(ImageRecognition.class);
private static AkazeImageFinder imageFinder = new AkazeImageFinder();
private static void log(String message) {
logger.info(message);
}
static {
AkazeImageFinder.setupOpenCVEnv();
}
/**
* Find the location of the reference image on the screen
*
* @param searchedImageFilePath Path to the reference image file to be searched
* @param sceneImageFilePath Path to the scene file in which the image is going to be searched for
* @param platform Defines the platform (phone operating system) that is in use. PlatformType.ANDROID for Android and PlatformType.IOS for iOS
* @return Returns the location of the image or null if image has not been found
*/
public static ImageLocation findImage(String searchedImageFilePath, String sceneImageFilePath, PlatformType platform) throws Exception {
ImageRecognitionSettings setting = new ImageRecognitionSettings();
return findImage(searchedImageFilePath, sceneImageFilePath, setting, platform);
}
/**
* Find the location of the reference image on the screen
* @param searchedImageFilePath Path to the reference image file to be searched
* @param sceneImageFilePath Path to the scene file in which the image is going to be searched for
* @param settings Image recognition related settings
* @param platform Defines the platform (phone operating system) that is in use. PlatformType.ANDROID for Android and PlatformType.IOS for iOS
* @return Returns the location of the image or null if image has not been found
* @throws Exception
*/
public static ImageLocation findImage(String searchedImageFilePath, String sceneImageFilePath, ImageRecognitionSettings settings, PlatformType platform) throws Exception {
log("Searching for " + searchedImageFilePath);
log("Searching in " + sceneImageFilePath);
ImageLocation imgLocation = imageFinder.findImage(searchedImageFilePath, sceneImageFilePath, settings.getTolerance());
if (imgLocation != null) {
Size screenSize = getScreenSize(platform, sceneImageFilePath);
if (platform.equals(PlatformType.IOS)) {
imgLocation = scaleImageRectangleForIos(screenSize, imgLocation, sceneImageFilePath);
}
Point center = imgLocation.getCenter();
if (!isPointInsideScreenBounds(center, screenSize)) {
log("Screen size is (width, height): " + screenSize.width + ", " + screenSize.height);
log("WARNING: Coordinates found do not match the screen --> image not found.");
imgLocation = null;
}
}
return imgLocation;
}
private static ImageLocation scaleImageRectangleForIos(Size screenSize, ImageLocation imageLocation, String sceneImageFilePath) {
//for retina devices we need to recalculate coordinates
double sceneHeight = imageFinder.getSceneHeight(sceneImageFilePath);
double sceneWidth = imageFinder.getSceneWidth(sceneImageFilePath);
int screenHeight = (int) screenSize.height;
int screenWidth = (int) screenSize.width;
// Make sure screenshot size values are "landscape" for comparison
if (sceneHeight > sceneWidth) {
double temp = sceneHeight;
sceneHeight = sceneWidth;
sceneWidth = temp;
}
// Make sure screen size values are "landscape" for comparison
if (screenHeight > screenWidth) {
int temp = screenHeight;
screenHeight = screenWidth;
screenWidth = temp;
}
if ((screenHeight coordinates have been recalculated");
}
else {
log("Recalculating coordinates for x2 retina displays..");
imageLocation.divideCoordinatesBy(2);
log("Device with Retina display rendered at x2 => coordinates have been recalculated");
}
}
return imageLocation;
}
private static boolean isPointInsideScreenBounds(Point center, Size screenSize) {
return !((center.x >= screenSize.width) || (center.x < 0) || (center.y >= screenSize.height) || (center.y < 0));
}
/**
* Checks whether image disappears from screen before a predefined timeout.
*
* @param searchedImageFilePath Path to the reference image file to be searched
* @param screenshotBaseDirectory Path to the directory in which the screenshots should be stored
* @param platform Defines the platform (phone operating system) that is in use. PlatformType.ANDROID for Android and PlatformType.IOS for iOS
* @return True if the image cannot be found or disappears successfully. False if the image can be found and the function timeouts.
* @throws Exception
*/
public static boolean hasImageDissappearedFromScreenBeforeTimeout(String searchedImageFilePath,
String screenshotBaseDirectory, PlatformType platform) throws Exception {
log("==> Trying to find image: " + searchedImageFilePath);
int retry_counter=0;
long start = System.nanoTime();
while (((System.nanoTime() - start) / 1e6 / 1000 < 300)) {
String screenshotName = FilenameUtils.getBaseName(searchedImageFilePath) + "_screenshot_"+retry_counter;
String screenShotFile = ImageRecognition.takeScreenshot(screenshotName, screenshotBaseDirectory, platform);
if ((findImage(searchedImageFilePath, screenShotFile, platform)) == null) {
log("Image has successfully disappeared from screen.");
return true;
}
sleep(3);
retry_counter++;
}
logger.warn("Image did not disappear from screen");
return false;
}
/**
* Extract text from an image.
*
* @param imageInput Path to the image file in which a text should be found
* @return The found text in the image.
*/
public static String getTextStringFromImage(String imageInput) {
String[] tesseractCommand = {"tesseract", imageInput, "stdout"};
String value = "";
try {
ProcessBuilder p = new ProcessBuilder(tesseractCommand);
Process proc = p.start();
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
value += line;
}
} catch (Throwable t) {
t.printStackTrace();
}
return value;
}
/**
* @param searchedImageFilePath Path to the reference image file to be searched
* @param screenshotBaseDirectory Path to the directory in which the screenshots should be stored
* @param settings Image recognition related settings
* @param platform Defines the platform (phone operating system) that is in use. PlatformType.ANDROID for Android and PlatformType.IOS for iOS
* @return ImageSearchResult, an object containing information about the location of the found image and a screenshot from the moment the reference image was found.
* @throws InterruptedException
* @throws IOException
* @throws Exception
*/
public static ImageSearchResult findImageOnScreen(String searchedImageFilePath, String screenshotBaseDirectory, ImageRecognitionSettings settings, PlatformType platform) throws InterruptedException, IOException, Exception {
ImageSearchResult imageSearchResult = findImageLoop(searchedImageFilePath, screenshotBaseDirectory, settings, platform);
if (imageSearchResult.isFound() && settings.isCrop()) {
log("Cropping image..");
imageFinder.cropImage(imageSearchResult);
log("Cropping image.. Succeeded!");
}
return imageSearchResult;
}
private static ImageSearchResult findImageLoop(String searchedImagePath, String screenshotBaseDirectory, ImageRecognitionSettings settings, PlatformType platform) throws InterruptedException, IOException, Exception {
long start_time = System.nanoTime();
ImageSearchResult imageSearchResult = new ImageSearchResult();
String imageName = FilenameUtils.getBaseName(searchedImagePath);
for (int i = 0; i < settings.getRetries(); i++) {
String screenshotName = imageName + "_screenshot_"+i;
String screenshotFile = takeScreenshot(screenshotName,screenshotBaseDirectory, platform);
ImageLocation imageLocation = ImageRecognition.findImage(searchedImagePath, screenshotFile, settings, platform);
if (imageLocation!=null){
long end_time = System.nanoTime();
int difference = (int) ((end_time - start_time) / 1e6 / 1000);
log("==> Find image took: " + difference + " secs.");
imageSearchResult.setImageLocation(imageLocation);
imageSearchResult.setScreenshotFile(screenshotFile);
return imageSearchResult;
}
retryWait(settings);
}
log("==> Image not found");
return imageSearchResult;
}
private static void retryWait(ImageRecognitionSettings settings) throws InterruptedException {
if (settings.getRetryWaitTime() > 0) {
log("retryWait given, sleeping " + settings.getRetryWaitTime() + " seconds.");
sleep(settings.getRetryWaitTime());
}
}
private static void sleep(int seconds) throws InterruptedException {
Thread.sleep(seconds * 1000);
}
public static String takeScreenshot(String screenshotName, String screenshotBaseDirectory, PlatformType platform) throws Exception {
long start_time = System.nanoTime();
String screenshotFile = screenshotBaseDirectory + screenshotName + ".png";
String screenShotFilePath = System.getProperty("user.dir") + "/" + screenshotFile;
if (platform.equals(PlatformType.IOS)) {
takeIDeviceScreenshot(screenShotFilePath);
} else if (platform.equals(PlatformType.ANDROID)) {
takeAndroidScreenshot(screenShotFilePath);
} else{
throw new Exception("Invalid platformType: "+platform);
}
long end_time = System.nanoTime();
int difference = (int) ((end_time - start_time) / 1e6 / 1000);
logger.info("==> Taking a screenshot took " + difference + " secs.");
return screenshotFile;
}
private static void takeAndroidScreenshot(String screenShotFilePath) throws IOException, InterruptedException {
log("Taking android screenshot...");
log(screenShotFilePath);
String[] cmd = new String[]{"screenshot2", "-d", screenShotFilePath};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null)
log(line);
int exitVal = p.waitFor();
if (exitVal != 0) {
log("screenshot2 process exited with value: " + exitVal);
}
}
private static void takeIDeviceScreenshot(String screenShotFilePath) throws Exception {
String udid = getIosUdid();
String[] cmd = new String[]{"idevicescreenshot", "-u", udid, screenShotFilePath};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null)
log(line);
int exitVal = p.waitFor();
if (exitVal != 0) {
log("idevicescreenshot process exited with value: " + exitVal);
}
cmd = new String[]{"sips", "-s", "format", "png", screenShotFilePath, "--out", screenShotFilePath};
p = Runtime.getRuntime().exec(cmd);
exitVal = p.waitFor();
if (exitVal != 0) {
log("sips process exited with value: " + exitVal);
}
}
private static Size getScreenSize(PlatformType platform, String sceneImageFilePath) throws Exception {
if (platform.equals(PlatformType.IOS)) {
return getIosScreenSize(sceneImageFilePath);
} else {
return getAndroidScreenSize();
}
}
private static Size getIosScreenSize(String sceneImageFilePath) throws Exception {
String udid = getIosUdid();
String productType = getIosProductType(udid);
try {
return getIosScreenSizePointsFromPropertiesFile(productType);
} catch(UnsupportedOperationException e){
logger.warn("Current device not included in the ios-screen-size.properties-file. Assuming x3 Retina display.");
logger.warn("Add the devices screen size information to the ios-screen-size.properties-file");
int screenHeight = (int) (imageFinder.getSceneHeight(sceneImageFilePath)/3);
int screenWidth = (int) (imageFinder.getSceneWidth(sceneImageFilePath)/3);
return new Size(screenWidth, screenHeight);
}
}
private static String getIosProductType(String udid) throws IOException, InterruptedException, Exception {
String[] cmd = new String[]{"ideviceinfo", "-u", udid, "--key", "ProductType"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
int exitVal = p.waitFor();
if (exitVal != 0) {
throw new Exception("ideviceinfo process exited with value: " + exitVal);
}
return in.readLine();
}
private static String getIosUdid() throws Exception {
String udid = System.getenv("UDID");
if (udid==null){
throw new Exception("$UDID was null, set UDID environment variable and try again");
}
return udid;
}
private static Size getIosScreenSizePointsFromPropertiesFile(String productType) throws UnsupportedOperationException, Exception {
Properties screenSizeProperties = fetchProperties();
String screenDimensionString = (String) screenSizeProperties.get(productType);
if (screenDimensionString == null){
throw new UnsupportedOperationException("ios-screen-size.properties is missing entry for: " + productType);
}
String screenDimensions[] = screenDimensionString.split(" x ");
if (screenDimensions.length!=2){
throw new Exception("Invalid ios-screen-size.properties file syntax for line: " + productType);
}
int width = Integer.parseInt(screenDimensions[0]);
int height = Integer.parseInt(screenDimensions[1]);
return new Size(width, height);
}
private static Properties fetchProperties() throws Exception {
Properties iosScreenSizeProperties = new Properties();
InputStream input = null;
try {
String filename = "ios-screen-size.properties";
input = ImageRecognition.class.getClassLoader().getResourceAsStream(filename);
if (input == null) {
throw new Exception("ios-screen-size.properties does not exist");
}
iosScreenSizeProperties.load(input);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return iosScreenSizeProperties;
}
private static Size getAndroidScreenSize() throws IOException, InterruptedException {
String adb = "adb";
String[] adbCommand = {adb, "shell", "dumpsys", "window"};
ProcessBuilder p = new ProcessBuilder(adbCommand);
Process proc = p.start();
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line;
String[] size = null;
while ((line = br.readLine()) != null) {
if (!line.contains("OriginalmUnrestrictedScreen")) { //we do this check for devices with android 5.x+ The adb command returns an extra line with the values 0x0 which must be filtered out.
if (line.contains("mUnrestrictedScreen")) {
String[] tmp = line.split("\\) ");
size = tmp[1].split("x");
}
}
}
int width = Integer.parseInt(size[0]);
int height = Integer.parseInt(size[1]);
return new Size(width, height);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy