sikuli-driver team mailing list archive
-
sikuli-driver team
-
Mailing list archive
-
Message #36459
Re: [Question #280855]: There are two buttons that are exactly the same, one is gray but one is yellow. I want it to chose the yellow and not the gray.
Question #280855 on Sikuli changed:
https://answers.launchpad.net/sikuli/+question/280855
Status: Open => Answered
TheGambler proposed the following answer:
I had exactly the same issue. SikuliX sometimes got the color difference
when using a minimum similarity of 0.99, but it was not stable enough
for my case. As I am using th SikuliX Java API I derived my own color
measuring class from Screen:
/**
* This class implements support for colored pattern matching if similarity has
* to be quite low.
*
* @author aguelle
*
*/
public class MinSimColoredScreen extends Screen {
public MinSimColoredScreen() {
super();
}
/**
* Constructor
*
* @param searchRegion
* The search region
*/
public MinSimColoredScreen(Region searchRegion) {
setSearchRegion(searchRegion);
}
static Logger log =
Logger.getLogger(MinSimColoredScreen.class.getName());
private final double MAX_COLOR_DIFFERENCE = 0.07;
private Region searchRegion;
@Override
public <PatternOrString> Match wait(PatternOrString target, double timeout)
throws FindFailed {
long startingTime = System.currentTimeMillis();
long timeoutMS = (long) (timeout * 1000);
boolean found = false;
Match match = null;
long usedTime = 0;
Color colorFoundImage = null;
Color colorTargetImage = null;
double colorDifference = -1;
while (usedTime < timeoutMS && found == false) {
match = super.wait(target, timeout);
Rectangle targetRect = match.getRect();
BufferedImage targetImage = match.getImage().get();
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
log.error("Thread Sleep failed.", e1);
}
// take screenshot of found region
BufferedImage foundImage = null;
try {
foundImage = new Robot().createScreenCapture(new Rectangle(
targetRect));
} catch (AWTException e) {
log.error("Creating screen capture failed.", e);
}
// compare average colors of target image and screenshot of the
// found region
colorFoundImage = new Color(ImageUtils.getAverageColor(foundImage));
colorTargetImage = new Color(
ImageUtils.getAverageColor(targetImage));
colorDifference = ImageUtils.getColorDifference(colorFoundImage,
colorTargetImage);
if (colorDifference <= MAX_COLOR_DIFFERENCE) {
found = true;
}
usedTime = System.currentTimeMillis() - startingTime;
}
if (!found) {
throw new FindFailed("Color does not match");
}
log.debug(target + " found!");
return match;
}
/**
* Sets the search region for the screen
*
* @param searchRegion
* The search region
*/
public void setSearchRegion(Region searchRegion) {
this.searchRegion = searchRegion;
setH(searchRegion.h);
setW(searchRegion.w);
this.setLocation(new Location(searchRegion.x, searchRegion.y));
}
public Region getSearchRegion() {
return searchRegion;
}
}
/**
* This class holds functions to manipulate images.
*
* @author aguelle
*
*/
public class ImageUtils {
private static final int COMMON_COLOR_MAX_DIMENSION = 100;
/**
* Converts a given Image into a BufferedImage
*
* @param img
* The Image to be converted
* @return The converted BufferedImage
*/
public static BufferedImage imageToBufferedImage(Image img) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null),
img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
/**
* Gets the average color by scaling the image to just one pixel.
*
* @param image
* The image
* @return The average color of the image
*/
public static Integer getAverageColor(BufferedImage image) {
Image scaledImaged = image.getScaledInstance(1, 1,
Image.SCALE_AREA_AVERAGING);
BufferedImage scaledBufferedImage = imageToBufferedImage(scaledImaged);
Integer color = scaledBufferedImage.getRGB(0, 0);
return color;
}
/**
* Gets the color with the highest amount from an image.
*
* @param image
* The image
* @return The color with the highest amount in the format RED GREEN BLUE
*/
public static Integer getMostCommonColor(BufferedImage image) {
BufferedImage scaledImage = scaleBufferedImageToDimension(image,
COMMON_COLOR_MAX_DIMENSION);
Map<Integer, Integer> colorStatistics =
buildColorStatisticsOfImage(scaledImage);
// sort map for highest amount
List<Map.Entry<Integer, Integer>> colorList = new LinkedList<Map.Entry<Integer, Integer>>(
colorStatistics.entrySet());
Collections.sort(colorList, new RGBStatisticsComparator());
// get highest amount entry
Map.Entry<Integer, Integer> lastPixel = (Map.Entry<Integer, Integer>) colorList
.get(colorList.size() - 1);
return lastPixel.getKey();
}
/**
* Scales a BufferedImage to a fixed squared dimension.
*
* @param image
* The image
* @param dimension
* The dimension
* @return A BufferedImage that fits in to the dimension of the square
*/
public static BufferedImage scaleBufferedImageToDimension(
BufferedImage image, int dimension) {
Image scaledImaged;
if (image.getWidth(null) > image.getHeight(null)) {
scaledImaged = image.getScaledInstance(dimension, -1,
Image.SCALE_DEFAULT);
} else {
scaledImaged = (BufferedImage) image.getScaledInstance(-1,
dimension, Image.SCALE_DEFAULT);
}
return imageToBufferedImage(scaledImaged);
}
/**
* Builds a statistical map with all found colors as keys within a given
* image. The values of the map represent the amount pixels found in the
* key-colour.
*
* @param image
* The image to operate on
* @return A Map with <colorcode, amount>
*/
private static Map<Integer, Integer> buildColorStatisticsOfImage(
BufferedImage image) {
Map<Integer, Integer> colorStatistics = new
HashMap<Integer, Integer>();
for (int i = 0; i < image.getWidth(); i++) {
for (int j = 0; j < image.getHeight(); j++) {
int rgb = image.getRGB(i, j);
Integer counter = (Integer)
colorStatistics.get(rgb);
if (counter == null) {
counter = 0;
}
counter++;
colorStatistics.put(new Integer(rgb), counter);
}
}
return colorStatistics;
}
/**
* Extracts the red green and blue portions as of a pixel and returns them
* as String.
*
* @param rgb
* The color of the pixel as bitfield
* @return A String of the decimal representations of RED, GREEN and BLUE
*/
public static String rgbBitfieldToString(int rgb) {
return getRed(rgb) + " " + getGreen(rgb) + " " + getBlue(rgb);
}
/**
* Extracts the red portion from a given RGB color
*
* @param rgb
* The color as bitfield
* @return the decimal portion of red
*/
public static int getRed(int rgb) {
return (rgb >> 16) & 0xff;
}
/**
* Extracts the green portion from a given RGB color
*
* @param rgb
* The color as bitfield
* @return the decimal portion of green
*/
public static int getGreen(int rgb) {
return (rgb >> 8) & 0xff;
}
/**
* Extracts the blue portion from a given RGB color
*
* @param rgb
* The color as bitfield
* @return the decimal portion of blue
*/
public static int getBlue(int rgb) {
return (rgb) & 0xff;
}
/**
* Loads a BufferedImage from a given file path
*
* @param path
* The path to the image
* @return A BufferedImage
* @throws IOException
* If the file could not be read
*/
public static BufferedImage getBufferedImageFromPath(String path)
throws IOException {
BufferedImage image = null;
ImageInputStream is = ImageIO.createImageInputStream(new File(path));
Iterator<ImageReader> iter = ImageIO.getImageReaders(is);
ImageReader imageReader = (ImageReader) iter.next();
imageReader.setInput(is);
image = imageReader.read(0);
return image;
}
/**
* Calculates the difference of two colors between 0.0 and 1.0. 1.0 is the
* biggest possible difference.
*
* @param c1
* First color
* @param c2
* Second color
* @return The difference between 0.0 and 1.0
*/
public static double getColorDifference(Color c1, Color c2) {
// max distance from (0,0,0) to (255,255,255)
double maxLength = Math.sqrt(65025 + 65025 + 65025);
double result = Math.pow((c1.getRed() - c2.getRed()), 2)
+ Math.pow(c1.getGreen() - c2.getGreen(), 2)
+ Math.pow(c1.getBlue() - c2.getBlue(), 2);
result = Math.sqrt(result);
// normalizing
result = result / maxLength;
return result;
}
/**
* Compares the amount of two color statistic entries.
*
* @author aguelle
*
*/
static class RGBStatisticsComparator implements
Comparator<Map.Entry<Integer, Integer>> {
@Override
public int compare(Map.Entry<Integer, Integer> entry1,
Map.Entry<Integer, Integer> entry2) {
return (entry1.getValue().compareTo(entry2.getValue()));
}
}
--
You received this question notification because your team Sikuli Drivers
is an answer contact for Sikuli.