Bob Jones
Bob Jones

Reputation: 171

How to compare multiple screenshots from two folders

I am trying to write a java code in selenium to compare expected and actual screen shots. I have created two folders

Now I am trying to compare these screenshots to make sure whether they match or not. Here is the following piece of code I have basically re-searched and written. Although it is reading the file from the individual folders, the comparison fails. Please advice ? Thanks

public class ImageComparison {

    static String workingDir = System.getProperty("user.dir");  
    static String COMBINATION = "combine";
    static String SUBTRACTION = "subtraction";
    static String IMAGE_FILE_TYPE = "png";

    public static File listFilesForFolder(final File folder) {
        for (final File fileEntry : folder.listFiles()) {
            if (fileEntry.isDirectory()) {
                listFilesForFolder(fileEntry);
            } else {
                System.out.println(fileEntry.getName());
            }
        }
        return folder;


    }

    final static File folderActual = new File(workingDir+ "\\ICScreenshots\\");
    final static File folderExpected = new File(workingDir+ "\\Screenshots\\");
    static File fActual = listFilesForFolder(folderActual);
    static File fExpected = listFilesForFolder(folderExpected);


    public static void main(String[] args) throws IOException {

        BufferedImage imgA = ImageIO.read(new File(fActual + "." + IMAGE_FILE_TYPE));
        BufferedImage imgB = ImageIO.read(new File(fExpected + "." + IMAGE_FILE_TYPE));
        System.out.println(ImageComparison.bufferedImagesEqual(imgA, imgB));
        ImageComparison.subtractImages(imgA, imgB);
        ImageComparison.combineImages(imgA, imgB);
    }

    private static void combineImages(BufferedImage image1, BufferedImage image2) throws IOException {
        BufferedImage result = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
        for (int x = 0; x < image1.getWidth(); x++)
            for (int y = 0; y < image1.getHeight(); y++) {
                result.setRGB(x, y, Math.abs(image2.getRGB(x, y) + image1.getRGB(x, y)));
            }
        ImageIO.write(result, IMAGE_FILE_TYPE, new File(COMBINATION + "." + IMAGE_FILE_TYPE));
    }

    private static void subtractImages(BufferedImage image1, BufferedImage image2) throws IOException {
        BufferedImage result = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
        for (int x = 0; x < image1.getWidth(); x++)
            for (int y = 0; y < image1.getHeight(); y++) {
                result.setRGB(x, y, Math.abs(image2.getRGB(x, y) - image1.getRGB(x, y)));
            }
        ImageIO.write(result, IMAGE_FILE_TYPE, new File(SUBTRACTION + "." + IMAGE_FILE_TYPE));
    }

    private static boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2) {
        if (!(img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight())) {
            return false;
        }
        for (int x = 0; x < img1.getWidth(); x++) {
            for (int y = 0; y < img1.getHeight(); y++) {
                if (img1.getRGB(x, y) != img2.getRGB(x, y))
                    return false;
            }
        }
        return true;
    }
}

I am getting the following error

Exception in thread "main" javax.imageio.IIOException: Can't read input file!
at javax.imageio.ImageIO.read(Unknown Source)
at fin.bi.test.ImageComparison.main(ImageComparison.java:36)

Upvotes: 1

Views: 272

Answers (1)

JeffC
JeffC

Reputation: 25611

I'm assuming you are somewhat new to coding so I'm going to list the different issues and what I did to solve them as a hopefully educational exercise.

  1. listFilesForFolder() is set to navigate down into subfolders but you said you didn't need that. It returns a single File but you stated you wanted to loop through all files. It also returns only the parent folder (not actual files) which is why you were getting the error. You were building a path with no filename so it was throwing because the expected file was a folder. I rewrote listFilesForFolder() to return a List<String> so we can iterate through it later.

  2. You were missing the code to loop through the list of files.

  3. I changed the name of the output file to include the original file name. Your code would have repeatedly written over the existing file, subtraction.png. It now outputs a file, subtraction..png.

  4. I added a parameter, File outputFile, to combineImages() and subtractImages() to be able to name the output images and avoid overwriting the output files.

Assuming a folder structure

ICScreenshots\P1.png
ICScreenshots\P2.png
Screenshots\P1.png
Screenshots\P2.png
Screenshots\P3.png

where the P1s are the same and the P2s are different

The output looks like

P1.png: true
P2.png: false
P3.png: does not exist in the ICScreenshots directory

The code is below

public class ImageComparison2
{

    static String workingDir = System.getProperty("user.dir");
    static String COMBINATION = "combination";
    static String SUBTRACTION = "subtraction";
    static String IMAGE_FILE_TYPE = "png";
    final static File folderActual = new File(workingDir + "\\ICScreenshots\\");
    final static File folderExpected = new File(workingDir + "\\Screenshots\\");

    public static List<String> listFilesForFolder(final File folder) throws IOException
    {
        List<String> files = new ArrayList<String>();
        try (Stream<Path> paths = Files.walk(folder.toPath()))
        {
            paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".png")).map(Path::getFileName)
                    .forEach(p -> files.add(p.toString()));
        }

        return files;
    }

    public static void main(String[] args) throws IOException
    {
        for (String s : listFilesForFolder(folderExpected))
        {
            File actualFile = new File(folderActual.getAbsolutePath() + "\\" + s);
            File expectedFile = new File(folderExpected.getAbsolutePath() + "\\" + s);
            if (actualFile.exists())
            {
                BufferedImage imgA = ImageIO.read(actualFile);
                BufferedImage imgB = ImageIO.read(expectedFile);
                boolean same = ImageComparison2.bufferedImagesEqual(imgA, imgB);
                System.out.println(s + ": " + same);
                if (!same)
                {
                    ImageComparison2.subtractImages(imgA, imgB, new File(COMBINATION + "." + s));
                    ImageComparison2.combineImages(imgA, imgB, new File(SUBTRACTION + "." + s));
                }
            }
            else
            {
                System.out.println(s + ": does not exist in the ICScreenshots directory");
            }
        }
    }

    private static void combineImages(BufferedImage image1, BufferedImage image2, File outputFile) throws IOException
    {
        BufferedImage result = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
        for (int x = 0; x < image1.getWidth(); x++)
            for (int y = 0; y < image1.getHeight(); y++)
            {
                result.setRGB(x, y, Math.abs(image2.getRGB(x, y) + image1.getRGB(x, y)));
            }
        ImageIO.write(result, IMAGE_FILE_TYPE, outputFile);
    }

    private static void subtractImages(BufferedImage image1, BufferedImage image2, File outputFile) throws IOException
    {
        BufferedImage result = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
        for (int x = 0; x < image1.getWidth(); x++)
            for (int y = 0; y < image1.getHeight(); y++)
            {
                result.setRGB(x, y, Math.abs(image2.getRGB(x, y) - image1.getRGB(x, y)));
            }
        ImageIO.write(result, IMAGE_FILE_TYPE, outputFile);
    }

    private static boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2)
    {
        if (!(img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()))
        {
            return false;
        }
        for (int x = 0; x < img1.getWidth(); x++)
        {
            for (int y = 0; y < img1.getHeight(); y++)
            {
                if (img1.getRGB(x, y) != img2.getRGB(x, y))
                    return false;
            }
        }
        return true;
    }
}

Upvotes: 1

Related Questions