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.

_______________________________________________
Mailing list: https://launchpad.net/~sikuli-driver
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~sikuli-driver
More help   : https://help.launchpad.net/ListHelp

Reply via email to