import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.beans.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class Test {
private BufferedImage template;
private BufferedImage img;
private List<Point> positions = new ArrayList<>();
private class ImagePanel extends JComponent {
public Dimension getPreferredSize() { return new Dimension(img.getWidth(), img.getHeight()); }
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img, 0, 0, null);
g2.setColor(Color.GREEN);
g2.setStroke(new BasicStroke(2));
for (Point p : positions) {
Rectangle2D r = new Rectangle2D.Float(p.x, p.y,
template.getWidth(), template.getHeight());
g2.draw(r);
}
}
}
public Test(BufferedImage img, BufferedImage template) {
this.img = img;
this.template = template;
}
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ImagePanel p = new ImagePanel();
JButton button = new JButton("Find");
JProgressBar pbar = new JProgressBar();
button.addActionListener(e ->
new Thread(() -> find(p, ev-> pbar.setValue((int)ev.getNewValue()))).start());
JPanel controls = new JPanel();
controls.add(pbar);
controls.add(button);
frame.add(p);
frame.add(controls, BorderLayout.SOUTH);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
private void find(JComponent c, PropertyChangeListener l) {
int maxX = img.getWidth() - template.getWidth();
int maxY = img.getHeight() - template.getHeight();
int oldProgress = 0;
for (int i = 0; i < maxY; i++) {
for (int j = 0; j < maxX; j++) {
int progress = (i*maxX+j)*100/(maxY*maxX);
if (oldProgress != progress && progress % 10 == 0) {
final int old = oldProgress;
SwingUtilities.invokeLater(() -> l.propertyChange(
new PropertyChangeEvent(this, "progress", old, progress)));
}
oldProgress = progress;
if (matchesTemplate(j, i)) {
Point p = new Point(j, i);
SwingUtilities.invokeLater(() -> positions.add(p));
j += template.getWidth() - 1;
}
}
}
final int old = oldProgress;
SwingUtilities.invokeLater(() -> l.propertyChange(
new PropertyChangeEvent(this, "progress", old, 100)));
SwingUtilities.invokeLater(() -> c.repaint());
}
private boolean matchesTemplate(int x, int y) {
int maxY = template.getHeight();
int maxX = template.getWidth();
int n = maxX * maxY;
int maxMisses = 8*n / 100;
int misses = 0;
for (int cy = 0; cy < maxY; cy++) {
for (int cx = 0; cx < maxX; cx++) {
int templateCol = template.getRGB(cx, cy);
int imgCol = img.getRGB(x + cx, y + cy);
if (!similarColor(imgCol, templateCol, 25.0)) {
misses++;
if (misses > maxMisses) {
return false;
}
}
}
}
return true;
}
private boolean similarColor(int c1, int c2, double maxErr) {
int redDiff = ((c1 & 0xff0000) >>> 16) - ((c2 & 0xff0000) >>> 16);
int greenDiff = ((c1 & 0xff00) >>> 8) - ((c2 & 0xff00) >>> 8);
int blueDiff = (c1 & 0xff) - (c2 & 0xff);
double err = Math.sqrt(redDiff*redDiff + blueDiff*blueDiff + greenDiff*greenDiff);
return err <= maxErr;
}
public static void main(String[] args) throws Exception {
BufferedImage img = ImageIO.read(Test.class.getResource("mario.png"));
BufferedImage template = img.getSubimage(378, 27, 80, 69);
Test app = new Test(img, template);
SwingUtilities.invokeLater(() -> app.run());
}
}