Mercurial > hg > ThermostatQA
changeset 97:5960410c77e5
Refactoring - image processing is now separated from
GUI robot class.
author | Pavel Tisnovsky <ptisnovs@redhat.com> |
---|---|
date | Mon, 23 Sep 2013 17:57:39 +0200 |
parents | 4d9f9795d258 |
children | 994289841798 |
files | ChangeLog Makefile src/org/thermostat/qa/framework/GuiRobot.java src/org/thermostat/qa/framework/ImageProcessing.java |
diffstat | 4 files changed, 261 insertions(+), 184 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Fri Sep 20 14:43:12 2013 +0200 +++ b/ChangeLog Mon Sep 23 17:57:39 2013 +0200 @@ -1,3 +1,11 @@ +2013-09-23 Pavel Tisnovsky <ptisnovs@redhat.com> + + * Makefile: + * src/org/thermostat/qa/framework/GuiRobot.java: + * src/org/thermostat/qa/framework/ImageProcessing.java: + Refactoring - image processing is now separated from + GUI robot class. + 2013-09-20 Pavel Tisnovsky <ptisnovs@redhat.com> * src/org/thermostat/qa/reporter/IndexPageGenerator.java:
--- a/Makefile Fri Sep 20 14:43:12 2013 +0200 +++ b/Makefile Mon Sep 23 17:57:39 2013 +0200 @@ -66,6 +66,7 @@ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/TestStatus.class \ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/ThermostatGuiTest.class \ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/ThermostatTest.class \ + $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/ImageProcessing.class \ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/ThermostatUtilities.class \ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/ThermostatOutputTextsGenerator.class \ $(BUILD_DIR)/$(FRAMEWORK_PACKAGE)/GuiRobot.class \
--- a/src/org/thermostat/qa/framework/GuiRobot.java Fri Sep 20 14:43:12 2013 +0200 +++ b/src/org/thermostat/qa/framework/GuiRobot.java Mon Sep 23 17:57:39 2013 +0200 @@ -48,13 +48,26 @@ import javax.imageio.ImageIO; + + +/** + * Class used for automatic mouse and keyboard system events generation. This + * class also contains methods for capturing the screenshot and for finding some + * pattern in that screenshot. + * + * @author Pavel Tisnovsky + */ public class GuiRobot { - // threshold value for a ratio between correlation and autocorrelation values - private static final float CORRELATION_THRESHOLD = 0.9f; + /** + * Number of millisecond between two consecutive robot actions. + */ private static final int ROBOT_DELAY = 100; + /** + * Number of millisecond for key press event. + */ private static final int KEY_PRESS_DELAY = 10; Robot robot = null; @@ -127,7 +140,7 @@ public Rectangle findPattern(String markerFileName) throws IOException { BufferedImage marker = ImageIO.read(new File(markerFileName)); - return findPattern(marker, getScreenCapture()); + return ImageProcessing.findPattern(marker, getScreenCapture()); } public void highlightRectangle(Rectangle rect) @@ -144,11 +157,19 @@ int x = rectangle.x + rectangle.width / 2; int y = rectangle.y + rectangle.height / 2; - robot.mouseMove(x, y); - robot.delay(ROBOT_DELAY); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.delay(ROBOT_DELAY); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + click(x, y); + } + + /** + * @param x + * @param y + */ + private void click(int x, int y) { + this.robot.mouseMove(x, y); + this.robot.delay(ROBOT_DELAY); + this.robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + this.robot.delay(ROBOT_DELAY); + this.robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } public void pressKey(int keycode) @@ -206,181 +227,5 @@ graphics.setColor(Color.BLACK); graphics.draw(rect2); } - /** - * Create three-dimensional array with the same size as tested image - * dimensions (last dimension is used for storing RGB components). - * - * @param testImage - * tested image - * @return newly created two-dimensional array - */ - private static float[][][] createArrayForOneColorComponent(BufferedImage testImage) - { - return new float[testImage.getHeight()][testImage.getWidth()][3]; - } - - /** - * Conversion from BufferedImage into three dimensional float arrays. - * It's much faster to work with float arrays even if it's memory inefficient. - * - * @param testImage - * tested image - * @param array array to fill - */ - private static void convertImageToFloatArray(BufferedImage testImage, float[][][] array) - { - float redAverage = 0; - float greenAverage = 0; - float blueAverage = 0; - for (int y = 0; y < testImage.getHeight(); y++) - { - for (int x = 0; x < testImage.getWidth(); x++) - { - int c = testImage.getRGB(x, y); - // filter out alpha channel - c = c & 0xffffff; - redAverage += ((c >> 16) & 0xff); - greenAverage += ((c >> 8) & 0xff); - blueAverage += (c & 0xff); - } - } - int pixels = testImage.getWidth() * testImage.getHeight(); - redAverage /= pixels; - greenAverage /= pixels; - blueAverage /= pixels; - //System.out.println("red: " + redAverage); - //System.out.println("green: " + greenAverage); - //System.out.println("blue: " + blueAverage); - - for (int y = 0; y < testImage.getHeight(); y++) - { - for (int x = 0; x < testImage.getWidth(); x++) - { - int c = testImage.getRGB(x, y); - // filter out alpha channel - c = c & 0xffffff; - array[y][x][0] = ((c >> 16) & 0xff) - redAverage; - array[y][x][1] = ((c >> 8) & 0xff) - greenAverage; - array[y][x][2] = (c & 0xff) - blueAverage; - } - } - } - - public static Rectangle findPattern(BufferedImage marker, BufferedImage screen){ - Rectangle actionArea = new Rectangle(0,0,screen.getWidth(),screen.getHeight()); - Rectangle result = new Rectangle(0,0,0,0); - boolean found = false; - boolean ok = true; - - for(int x = actionArea.x; (x < (actionArea.x + actionArea.width - marker.getWidth()) ) && !found; x++){ - for(int y= actionArea.y; (y < (actionArea.y + actionArea.height - marker.getHeight()) ) && !found; y++){ - - for(int mx = 0; (mx < marker.getWidth()) && ok; mx++){ - for(int my = 0; (my < marker.getHeight()) && ok; my++){ - if(marker.getRGB(mx, my) != screen.getRGB(x+mx,y+my)){ - ok = false; - } - } - } - if( ok ){ - found = true; - result.x = x; - result.y = y; - result.height = marker.getHeight(); - result.width = marker.getWidth(); - }else{ - ok = true; - } - } - } - - if(found){ - return result; - }else{ - return null; - } - } - - private static Rectangle findBlurredPattern(BufferedImage marker, BufferedImage testImage) - { - int maxX = testImage.getWidth() - marker.getWidth() - 1; - int maxY = testImage.getHeight() - marker.getHeight() - 1; - int markerMaxX = marker.getWidth(); - int markerMaxY = marker.getHeight(); - - // it is much faster to work directly with color components stored as float values - float[][][] testImageArray = createArrayForOneColorComponent(testImage); - float[][][] markerImageArray = createArrayForOneColorComponent(marker); - - //System.out.println("Starting conversion"); - convertImageToFloatArray(testImage, testImageArray); - convertImageToFloatArray(marker, markerImageArray); - //System.out.println("Conversion done"); - - double autocorrelation = computeCorrelation(markerMaxX, markerMaxY, markerImageArray, markerImageArray, 0, 0); - - int bestX = -1; - int bestY = -1; - double bestCorrelation = -1; - boolean foundExact = false; - - for (int yoffset = 0; (yoffset < maxY) && !foundExact; yoffset++ ) - { - //System.out.println("Processing line: " + yoffset + " of " + (maxY - 1)); - for (int xoffset = 0; (xoffset < maxX) && !foundExact; xoffset++) - { - double correlation = computeCorrelation(markerMaxX, markerMaxY, testImageArray, markerImageArray, yoffset, xoffset); - if (correlation > bestCorrelation) - { - bestCorrelation = correlation; - bestX = xoffset; - bestY = yoffset; - if (correlation == autocorrelation) - { - foundExact = true; - } - } - } - } - - //System.out.println(autocorrelation); - //System.out.println(bestCorrelation); - //System.out.println(bestX + " " + bestY); - if (bestCorrelation / autocorrelation < CORRELATION_THRESHOLD) - { - return null; - } - return new Rectangle(bestX, bestY, marker.getWidth(), marker.getHeight()); - } - - /** - * Compute correlation for given two images and 2D offset. - * - * @param maxX - * @param maxY - * @param testImageArray - * @param markerImageArray - * @param yoffset - * @param xoffset - * @return - */ - private static double computeCorrelation(int maxX, int maxY, float[][][] testImageArray, - float[][][] markerImageArray, int yoffset, int xoffset) - { - double correlation = 0; - for (int y = 0; y < maxY; y++) - { - for (int x = 0; x < maxX; x++) - { - for (int rgbIndex = 0; rgbIndex < 3; rgbIndex++) - { - float colorComponent1 = markerImageArray[y][x][rgbIndex]; - float colorComponent2 = testImageArray[yoffset + y][xoffset + x][rgbIndex]; - correlation += colorComponent1 * colorComponent2; - } - } - } - return correlation; - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/thermostat/qa/framework/ImageProcessing.java Mon Sep 23 17:57:39 2013 +0200 @@ -0,0 +1,223 @@ +/* + + ThermostatQA - test framework for Thermostat Monitoring Tool + + Copyright 2013 Red Hat, Inc. + +This file is part of ThermostatQA + +ThermostatQA is distributed under the GNU General Public License, +version 2 or any later version (with a special exception described +below, commonly known as the "Classpath Exception"). + +A copy of GNU General Public License (GPL) is included in this +distribution, in the file COPYING. + +Linking ThermostatQA code with other modules is making a combined work +based on ThermostatQA. Thus, the terms and conditions of the GPL +cover the whole combination. + +As a special exception, the copyright holders of ThermostatQA give you +permission to link this code with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on ThermostatQA code. If you modify ThermostatQA, you may +extend this exception to your version of the software, but you are +not obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package org.thermostat.qa.framework; + +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +public class ImageProcessing { + + /** + * Threshold value for a ratio between correlation and autocorrelation values. + * This threshold is used by the algorithm for finding some pattern in a screenshot. + */ + private static final float CORRELATION_THRESHOLD = 0.9f; + + /** + * Create three-dimensional array with the same size as tested image + * dimensions (last dimension is used for storing RGB components). + * + * @param testImage + * tested image + * @return newly created two-dimensional array + */ + private static float[][][] createArrayForOneColorComponent(BufferedImage testImage) + { + return new float[testImage.getHeight()][testImage.getWidth()][3]; + } + + /** + * Conversion from BufferedImage into three dimensional float arrays. + * It's much faster to work with float arrays even if it's memory inefficient. + * + * @param testImage + * tested image + * @param array array to fill + */ + private static void convertImageToFloatArray(BufferedImage testImage, float[][][] array) + { + float redAverage = 0; + float greenAverage = 0; + float blueAverage = 0; + for (int y = 0; y < testImage.getHeight(); y++) + { + for (int x = 0; x < testImage.getWidth(); x++) + { + int c = testImage.getRGB(x, y); + // filter out alpha channel + c = c & 0xffffff; + redAverage += ((c >> 16) & 0xff); + greenAverage += ((c >> 8) & 0xff); + blueAverage += (c & 0xff); + } + } + int pixels = testImage.getWidth() * testImage.getHeight(); + redAverage /= pixels; + greenAverage /= pixels; + blueAverage /= pixels; + //System.out.println("red: " + redAverage); + //System.out.println("green: " + greenAverage); + //System.out.println("blue: " + blueAverage); + + for (int y = 0; y < testImage.getHeight(); y++) + { + for (int x = 0; x < testImage.getWidth(); x++) + { + int c = testImage.getRGB(x, y); + // filter out alpha channel + c = c & 0xffffff; + array[y][x][0] = ((c >> 16) & 0xff) - redAverage; + array[y][x][1] = ((c >> 8) & 0xff) - greenAverage; + array[y][x][2] = (c & 0xff) - blueAverage; + } + } + } + + public static Rectangle findPattern(BufferedImage marker, BufferedImage screen){ + Rectangle actionArea = new Rectangle(0,0,screen.getWidth(),screen.getHeight()); + Rectangle result = new Rectangle(0,0,0,0); + boolean found = false; + boolean ok = true; + + for(int x = actionArea.x; (x < (actionArea.x + actionArea.width - marker.getWidth()) ) && !found; x++){ + for(int y= actionArea.y; (y < (actionArea.y + actionArea.height - marker.getHeight()) ) && !found; y++){ + + for(int mx = 0; (mx < marker.getWidth()) && ok; mx++){ + for(int my = 0; (my < marker.getHeight()) && ok; my++){ + if(marker.getRGB(mx, my) != screen.getRGB(x+mx,y+my)){ + ok = false; + } + } + } + if( ok ){ + found = true; + result.x = x; + result.y = y; + result.height = marker.getHeight(); + result.width = marker.getWidth(); + }else{ + ok = true; + } + } + } + + if(found){ + return result; + }else{ + return null; + } + } + + private static Rectangle findBlurredPattern(BufferedImage marker, BufferedImage testImage) + { + int maxX = testImage.getWidth() - marker.getWidth() - 1; + int maxY = testImage.getHeight() - marker.getHeight() - 1; + int markerMaxX = marker.getWidth(); + int markerMaxY = marker.getHeight(); + + // it is much faster to work directly with color components stored as float values + float[][][] testImageArray = createArrayForOneColorComponent(testImage); + float[][][] markerImageArray = createArrayForOneColorComponent(marker); + + //System.out.println("Starting conversion"); + convertImageToFloatArray(testImage, testImageArray); + convertImageToFloatArray(marker, markerImageArray); + //System.out.println("Conversion done"); + + double autocorrelation = computeCorrelation(markerMaxX, markerMaxY, markerImageArray, markerImageArray, 0, 0); + + int bestX = -1; + int bestY = -1; + double bestCorrelation = -1; + boolean foundExact = false; + + for (int yoffset = 0; (yoffset < maxY) && !foundExact; yoffset++ ) + { + //System.out.println("Processing line: " + yoffset + " of " + (maxY - 1)); + for (int xoffset = 0; (xoffset < maxX) && !foundExact; xoffset++) + { + double correlation = computeCorrelation(markerMaxX, markerMaxY, testImageArray, markerImageArray, yoffset, xoffset); + if (correlation > bestCorrelation) + { + bestCorrelation = correlation; + bestX = xoffset; + bestY = yoffset; + if (correlation == autocorrelation) + { + foundExact = true; + } + } + } + } + + //System.out.println(autocorrelation); + //System.out.println(bestCorrelation); + //System.out.println(bestX + " " + bestY); + if (bestCorrelation / autocorrelation < CORRELATION_THRESHOLD) + { + return null; + } + return new Rectangle(bestX, bestY, marker.getWidth(), marker.getHeight()); + } + + /** + * Compute correlation for given two images and 2D offset. + * + * @param maxX + * @param maxY + * @param testImageArray + * @param markerImageArray + * @param yoffset + * @param xoffset + * @return + */ + private static double computeCorrelation(int maxX, int maxY, float[][][] testImageArray, + float[][][] markerImageArray, int yoffset, int xoffset) + { + double correlation = 0; + for (int y = 0; y < maxY; y++) + { + for (int x = 0; x < maxX; x++) + { + for (int rgbIndex = 0; rgbIndex < 3; rgbIndex++) + { + float colorComponent1 = markerImageArray[y][x][rgbIndex]; + float colorComponent2 = testImageArray[yoffset + y][xoffset + x][rgbIndex]; + correlation += colorComponent1 * colorComponent2; + } + } + } + return correlation; + } + +}