ImageCompare.java

/**
 * Originally written by Pat Cullen
 * http://mindmeat.blogspot.com/2008/07/java-image-comparison.html
 *
 * Updated to use ImageIO by Gregory Graham
 */
package nz.co.gregs.dbvolution.utility;

import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;

public class ImageCompare {

	protected final BufferedImage original1;
	protected final BufferedImage original2;
	protected BufferedImage imageDiff = null;
	protected int comparex = 0;
	protected int comparey = 0;
	protected int factorA = 0;
	protected int factorD = 10;
	protected boolean match = false;
	protected int debugMode = 0; 
	private boolean compared = false;

	public ImageCompare(File file1, File file2) throws IOException {
		this(loadJPG(file1.getAbsolutePath()), loadJPG(file2.getAbsolutePath()));
	}

	public ImageCompare(Image img1, Image img2) {
		this(imageToBufferedImage(img1), imageToBufferedImage(img2));
	}

	protected ImageCompare(BufferedImage img1, BufferedImage img2) {
		this.original1 = img1;
		this.original2 = img2;
		autoSetParameters();
	}

	protected synchronized final void autoSetParameters() {
		compared = false;
		imageDiff = null;
		comparex = 10;
		comparey = 10;
		factorA = 10;
		factorD = 10;
	}

	public synchronized void setParameters(int x, int y, int factorA, int factorD) {
		compared = false;
		imageDiff = null;
		this.comparex = x;
		this.comparey = y;
		this.factorA = factorA;
		this.factorD = factorD;
	}

	// compare the two images in this object.
	public synchronized void compare() {
		// setup change display image
		imageDiff = imageToBufferedImage(original2);
		Graphics2D gc = imageDiff.createGraphics();
		gc.setColor(Color.RED);
		// convert to gray images.
		BufferedImage img1 = imageToBufferedImage(GrayFilter.createDisabledImage(original1));
		BufferedImage img2 = imageToBufferedImage(GrayFilter.createDisabledImage(original2));
		// how big are each section
		int blocksx = img1.getWidth() / comparex;
		int blocksy = img1.getHeight() / comparey;
		// set to a match by default, if a change is found then flag non-match
		this.match = true;
		// loop through whole image and compare individual blocks of images
		for (int y = 0; y < comparey; y++) {
			if (debugMode > 0) {
				System.out.print("|");
			}
			for (int x = 0; x < comparex; x++) {
				int b1 = getAverageBrightness(img1.getSubimage(x * blocksx, y * blocksy, blocksx - 1, blocksy - 1));
				int b2 = getAverageBrightness(img2.getSubimage(x * blocksx, y * blocksy, blocksx - 1, blocksy - 1));
				int diff = Math.abs(b1 - b2);
				if (diff > factorA) { // the difference in a certain region has passed the threshold value of factorA
					// draw an indicator on the change image to show where change was detected.
					gc.drawRect(x * blocksx, y * blocksy, blocksx - 1, blocksy - 1);
					this.match = false;
				}
				if (debugMode == 1) {
					System.out.print((diff > factorA ? "X" : " "));
				}
				if (debugMode == 2) {
					System.out.print(diff + (x < comparex - 1 ? "," : ""));
				}
			}
			if (debugMode > 0) {
				System.out.println("|");
			}
		}
		compared = true;
	}

	// returns a value specifying some kind of average brightness in the image.
	protected int getAverageBrightness(BufferedImage img) {
		Raster r = img.getData();
		int total = 0;
		for (int y = 0; y < r.getHeight(); y++) {
			for (int x = 0; x < r.getWidth(); x++) {
				total += r.getSample(r.getMinX() + x, r.getMinY() + y, 0);
			}
		}
		return total / ((r.getWidth() / factorD) * (r.getHeight() / factorD));
	}

	// returns true if image pair is considered a match
	public synchronized boolean match() {
		if (!compared) {
			compare();
		}
		return match;
	}

	// buffered images are just better.
	protected static BufferedImage imageToBufferedImage(Image img) {
		BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D g2 = bi.createGraphics();
		g2.drawImage(img, null, null);
		return bi;
	}

	// read a jpeg file into a buffered image
	protected static Image loadJPG(String filename) throws IOException {
		BufferedImage bi = ImageIO.read(new File(filename));
		return bi;
	}

}