Comparing 2 Images and marking differences with rectangles

Viewed 833

Edit: Updated the code, the code below now correctly draws rectangles around multiple shapes, but still has a minor issue of sometimes creating multiple rectangles on one single shape.

I have 2 Images, that i compare pixel by pixel with each other and i want my programm to create rectangles around the area of difference (multiple rectangles with multiple instances of differences). So far i managed to do this with a single rectangle, so if i had multiple "instances", they'd all be in one big rectangle. Now i'm trying to make the programm create multiple rectangles, but run into an IndexOutOfBoundsException.

The Programm itself overlays the 2 Images being compared with opacity and outputs the resulting overlaid image along with the rectangles into a new File. Both Images being compared have a consistent equal width and height.

I'm calling the rectangles i want to be drawn "regions" within the code. The Region List is being continiously updated while the comparison is running.

The first question i asked myself was, when does a point of difference (pixel difference) belong to a region? My attempt was to define a "tolerance", so as long as the pixel being compared is within the tolerance of the last found point of difference, it belongs to the same region. I quickly realized that this doesn't work as soon as i have a shape in form of a giant U on my image, with the top points being far enough apart to be not within the tolerance. And now i'm kind-of stuck, because i feel like i'm on the wrong path.

Below is the code i have so far:

private void compareImages() throws IOException{
    BufferedImage img1;
    BufferedImage img2;
    try {
        img1 = ImageIO.read(new File(path_to_img1));
        img2 = ImageIO.read(new File(path_to_img2));
    } catch (Throwable e) {
        System.out.println("Unable to load the Images!");
        return;
    }
    BufferedImage dest = new BufferedImage(img1.getWidth(), img1.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D gfx = dest.createGraphics();
    gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.65f));

    //Compare Images pixel by pixel
    int sX = 9999; //Start X
    int sY = 9999; //Start Y
    int eX = 0; //End X
    int eY = 0; //End Y
    boolean isDrawable = false;
    boolean loadedRegion = false;
    List<Rectangle> regions = new ArrayList<>();
    List<Rectangle> check_regions = new ArrayList<>();
    Rectangle tmp_comparison;
    int regionID = 0;
    int tolerance = 25;

    for (int i = 0; i < img1.getHeight(); i++) {
        for (int j = 0; j < img1.getWidth(); j++) {
            loadedRegion = false;
            regionID = 0;
            sX = 9999;
            sY = 9999;
            eX = 0;
            eY = 0;
            if ( img1.getRGB(j, i) != img2.getRGB(j, i) ){
                isDrawable = true;
                if (regions.size() != 0){
                    //Attempting to locate a matching existing Region
                    tmp_comparison = new Rectangle(j, i, 1, 1);
                    for (int trID = 0; trID<regions.size(); trID++){
                        if (tmp_comparison.intersects(check_regions.get(trID).getBounds()) == true) {
                            // Region found
                            sX = (int) regions.get(trID).getX();
                            sY = (int) regions.get(trID).getY();
                            eX = (int) regions.get(trID).getWidth();
                            eY = (int) regions.get(trID).getHeight();
                            regionID = trID;
                            loadedRegion = true;
                            break;
                        }
                    }
                }
                //Update Region Dimension
                if (j<sX){
                    sX = j;
                }
                if (j>eX){
                    eX = j;
                }
                if (i<sY){
                    sY = i;
                }
                if (i>eY){
                    eY = i;
                }
                if (regions.size() == 0 || loadedRegion == false){
                    regions.add(new Rectangle(sX, sY, eX, eY));
                    check_regions.add(new Rectangle(sX-tolerance, sY-tolerance, eX-sX+(tolerance*2), eY-sY+(tolerance*2)));
                } else {
                    regions.set(regionID, new Rectangle(sX, sY, eX, eY));
                    check_regions.set(regionID, new Rectangle(sX-tolerance, sY-tolerance, eX-sX+(tolerance*2), eY-sY+(tolerance*2)));
                }
            }
        }
    }
    // If there are any differences, draw the Regions
    // Regions are 10px bigger in all directions as compared to the actual rectangles of difference
    if (isDrawable == true){
        gfx.setPaint(Color.red);
        for (int i = 0; i<regions.size(); i++) {
            int dsX = 0;
            int dsY = 0;
            int deX = 0;
            int deY = 0;
            sX = (int) regions.get(i).getX();
            sY = (int) regions.get(i).getY();
            eX = (int) regions.get(i).getWidth();
            eY = (int) regions.get(i).getHeight();
            if (sX>=10){dsX = sX-10;}
            if (eX<=img1.getWidth()-10){deX = eX-sX+20;}
            if (sY>=10){dsY = sY-10;}
            if (eY<=img1.getHeight()-10){deY = eY-sY+20;}
            gfx.draw(new Rectangle2D.Double(dsX, dsY, deX, deY));
        }
    }
    gfx.drawImage(img1, 0, 0, null);
    gfx.drawImage(img2, 0, 0, null);
    gfx.dispose();
    File out = new File("C:\\output.png");
    ImageIO.write(dest, "PNG", out);
}

Below is the code that creates one big rectangle around all the differences found in the images being compared.

private void oneRectangle() throws IOException{
    BufferedImage img1;
    BufferedImage img2;
    try {
        img1 = ImageIO.read(new File(path_to_img1));
        img2 = ImageIO.read(new File(path_to_img2));
    } catch (Throwable e) {
        System.out.println("Unable to load the Images!");
        return;
    }
    BufferedImage dest = new BufferedImage(img1.getWidth(), img1.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D gfx = dest.createGraphics();
    gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.65f));

    //Compare Images pixel by pixel
    boolean isDrawable = false;
    int sX = 9999;
    int sY = 9999;
    int eX = 0;
    int eY = 0;

    for (int i = 0; i < img1.getHeight(); i++) {
        for (int j = 0; j < img1.getWidth(); j++) {
            if ( img1.getRGB(j, i) != img2.getRGB(j, i) ){
                isDrawable = true;                      
                if (j<sX){
                    sX = j;
                }
                if (j>eX){
                    eX = j;
                }
                if (i<sY){
                    sY = i;
                }
                if (i>eY){
                    eY = i;
                }
            }
        }
    }
    // Draw rectangle if there are any differences
    if (isDrawable == true){
        gfx.setPaint(Color.red);
        int dsX = 0;
        int dsY = 0;
        int deX = 0;
        int deY = 0;
        if (sX>=10){dsX = sX-10;}
        if (eX<=img1.getWidth()-10){deX = eX-sX+20;}
        if (sY>=10){dsY = sY-10;}
        if (eY<=img1.getHeight()-10){deY = eY-sY+20;}
        gfx.fill(new Rectangle2D.Double(dsX, dsY, deX, deY));
    }
    gfx.drawImage(img1, 0, 0, null);
    gfx.drawImage(img2, 0, 0, null);
    gfx.dispose();
    File out = new File("C:\\output.png");
    ImageIO.write(dest, "PNG", out);
}
0 Answers
Related