Как распознать изображение в другом изображении? - PullRequest
0 голосов
/ 30 октября 2018

Так что я довольно новичок в Java, поэтому я не уверен, что этот мой способ сделать это на самом деле хорошая идея, но в основном я пытаюсь проверить наличие экземпляра изображения внутри другого изображения. Поэтому, чтобы проверить, сработает ли это, у меня было 200x148 jpg, я получил от него байты, затем получил байты со скриншота окна, получил байты и сравнил их.

Теперь, поскольку обычно первое изображение не было на этом снимке экрана, я открыл его в приложении для фотографий и вставил его, когда программа спала (до того, как он сделал снимок экрана). И да, я могу подтвердить, что первое изображение было на скриншоте, посмотрев на скриншот. Однако, когда я сравниваю строки (чтобы проверить, находится ли строка со всеми байтовыми данными изображения один в строке со всеми байтовыми данными изображения два), получается отрицательное значение.

Вот код, который я пытаюсь использовать:

public static void main(String[] args) throws IOException, HeadlessException, AWTException, InterruptedException  {
     // Get the first image
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ImageIO.write(ImageIO.read(new File("profile.jpg")), "jpg", baos);
     byte[] bytes = baos.toByteArray();

     String bytes1S = "";
     for (int i = 0; i < bytes.length; i++) {
         bytes1S += bytes[i];
     }
     // Give yourself enough time to open the other image
     TimeUnit.SECONDS.sleep(6);
     // Take the screenshot
     BufferedImage image = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
     ImageIO.write(image, "jpg", new File("screenshot.jpg"));
     baos = new ByteArrayOutputStream();
     ImageIO.write(ImageIO.read(new File("screenshot.jpg")), "jpg", baos);
     byte[] bytes2 = baos.toByteArray();
     String bytes2S = "";
     for (int i = 0; i < bytes2.length; i++) {
         bytes2S += bytes2[i];
     }
     // Check if the second String of bytes contains the first String of bytes.
     if (bytes2S.contains(bytes1S))
         System.out.println("Yes");
     else
         System.out.println("No");

}

И для справки вот первое изображение и снимок экрана, который он взял:

Первое изображение

Скриншот

В чем причина того, что оно не обнаруживает первое изображение на скриншоте, и есть ли лучший способ сделать это (желательно без другой библиотеки)?

1 Ответ

0 голосов
/ 31 октября 2018

Метод грубой силы состоит в том, чтобы просто загрузить оба изображения как BufferedImage объекты, а затем пройтись по «основному» изображению, попиксельно, и посмотреть, можно ли там найти «подизображение».

Я реализовал это некоторое время назад и опубликую приведенный ниже код как MCVE.

Но примечание : при сохранении изображений в виде файлов JPG они сжимаются, и это сжатие составляет с потерями . Это означает, что пиксели не будут иметь одинаковые цвета, даже если они были одинаковыми на экране. В приведенном ниже примере это решается прагматически, с «порогом», который определяет, насколько разными могут быть пиксели. Но это немного произвольно и не так надежно. (Более надежное решение потребует больше усилий).

Я бы настоятельно рекомендую сохранять изображения в формате PNG. Они используют сжатие без потерь. Так что для файлов PNG вы можете установить threshold=0 в приведенном ниже коде.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.function.IntBinaryOperator;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FindImageInImage
{
    public static void main(String[] args) throws Exception
    {
        BufferedImage mainImage = 
            ImageIO.read(new URL("https://i.stack.imgur.com/rEouF.jpg"));
        BufferedImage subImage = 
            ImageIO.read(new URL("https://i.stack.imgur.com/wISyn.jpg"));

        int threshold = 100;
        Point location = findImageLocation(
            mainImage, subImage, threshold);
        System.out.println("At " + location);

        SwingUtilities.invokeLater(() -> showIt(mainImage, subImage, location));
    }

    private static void showIt(
        BufferedImage mainImage, BufferedImage subImage, Point location)
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.getContentPane().add(new JPanel()
        {
            @Override
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.drawImage(mainImage, 0, 0, null);
                if (location != null)
                {
                    g.setColor(Color.RED);
                    g.drawRect(location.x, location.y, 
                        subImage.getWidth(), subImage.getHeight());
                }
            }
        });
        f.setSize(1500, 800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }


    static Point findImageLocation(
        BufferedImage mainImage, 
        BufferedImage subImage, 
        int threshold)
    {
        return findImageLocation(mainImage, subImage, (rgb0, rgb1) -> 
        {
            int difference = computeDifference(rgb0, rgb1);
            if (difference > threshold)
            {
                return 1;
            }
            return 0;
        });
    }

    private static int computeDifference(int rgb0, int rgb1)
    {
        int r0 = (rgb0 & 0x00FF0000) >> 16;
        int g0 = (rgb0 & 0x0000FF00) >> 8;
        int b0 = (rgb0 & 0x000000FF);

        int r1 = (rgb1 & 0x00FF0000) >> 16;
        int g1 = (rgb1 & 0x0000FF00) >> 8;
        int b1 = (rgb1 & 0x000000FF);

        int dr = Math.abs(r0 - r1);
        int dg = Math.abs(g0 - g1);
        int db = Math.abs(b0 - b1);

        return dr + dg + db;
    }

    static Point findImageLocation(
        BufferedImage mainImage, 
        BufferedImage subImage, 
        IntBinaryOperator rgbComparator)
    {
        int w = mainImage.getWidth();
        int h = mainImage.getHeight();
        for (int x=0; x < w; x++)
        {
            for (int y = 0; y < h; y++)
            {
                if (isSubImageAt(mainImage, x, y, subImage, rgbComparator))
                {
                    return new Point(x, y);
                }
            }
        }
        return null;
    }

    static boolean isSubImageAt(
        BufferedImage mainImage, int x, int y, 
        BufferedImage subImage, 
        IntBinaryOperator rgbComparator)
    {
        int w = subImage.getWidth(); 
        int h = subImage.getHeight();
        if (x + w > mainImage.getWidth())
        {
            return false;
        }
        if (y + h > mainImage.getHeight())
        {
            return false;
        }
        for (int ix=0; ix < w; ix++)
        {
            for (int iy = 0; iy < h; iy++)
            {
                int mainRgb = mainImage.getRGB(x + ix, y + iy);
                int subRgb = subImage.getRGB(ix, iy);
                if (rgbComparator.applyAsInt(mainRgb, subRgb) != 0)
                {
                    return false;
                }
            }
        }
        return true;
    }

}
...