Java AWT Robot:为什么较小的屏幕捕获不等于整个屏幕捕获的子图像?

6uxekuva  于 5个月前  发布在  Java
关注(0)|答案(1)|浏览(54)

我在使用java.awt.Robot时遇到了一些奇怪的行为。如果我用它来拍摄两个屏幕截图,一个是整个屏幕,另一个只是屏幕的一个部分,屏幕的一个部分与原始屏幕截图的一个子图像具有不同的像素,与子图像具有相同的坐标。下面是我的代码:

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ScreenCapTest {
    public static void main(String[] args) throws AWTException, IOException {
        // Construct Robot
        Robot r = new Robot();
        
        // Get dimensions of screen
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        
        // Take screenshot
        BufferedImage screen = r.createScreenCapture(new Rectangle(0,0, (int)screenSize.getWidth(), (int)screenSize.getHeight()));
        
        // Take screenshot of small section of screen
        int x = 5;
        int y = 5;
        int w = 50;
        int h = 50;
        BufferedImage subscreen = r.createScreenCapture(new Rectangle(x,y,w,h));
        
        // Create a subimage of the same section of the screen from the original screenshot
        BufferedImage subimageOfScreen = screen.getSubimage(x,y,w,h);
        
        // Are they equal?
        System.out.println(imgEqual(subimageOfScreen, subscreen));
        
        // Output images for comparison
        ImageIO.write(subimageOfScreen, "png", new File("subimage.png"));
        ImageIO.write(subscreen, "png", new File("subscreen.png"));
    }
    
    public static boolean imgEqual(BufferedImage image1, BufferedImage image2) {
        int width;
        int height;
        boolean imagesEqual = true;

        if( image1.getWidth()  == ( width  = image2.getWidth() ) && 
            image1.getHeight() == ( height = image2.getHeight() ) ){

            for(int x = 0;imagesEqual == true && x < width; x++){
                for(int y = 0;imagesEqual == true && y < height; y++){
                    if( image1.getRGB(x, y) != image2.getRGB(x, y) ){
                        imagesEqual = false;
                    }
                }
            }
        } else {
            imagesEqual = false;
        }
        return imagesEqual;
    }
}

字符串
大多数情况下,它会报告false,这意味着从(5,5)到(5,5,5,5)的完整屏幕截图的子图像与从(5,5)到(5,5,5,5)的屏幕截图不同。奇怪的是,对于xywh的某些值,它会打印true
下面是一对BufferedImages的示例,代码将其打印为false。我可以看到它们略有不同,但我不明白为什么会存在这种行为。这是怎么回事?
完整屏幕截图的子图像:x1c 0d1x
屏幕同一部分的屏幕截图:

j7dteeu8

j7dteeu81#

修改你的代码一点。我不喜欢(原始)代码不断地创建一个屏幕图像的子图像来与实际的子图像进行比较。仍然没有帮助找到匹配。
我的下一步是从原始屏幕图像创建子图像。这是一种测试问题是否与逻辑或子图像有关的方法。在这种情况下,子图像匹配。
然后我回去测试两个机器人创建的图像,并注意到具有较小宽度/高度的子图像将匹配。
进一步测试和宽度似乎不会影响子图像的匹配。
然而,我注意到,高度确实造成了一个问题。在我的情况下,高度需要比屏幕的高度小31像素。巧合的是,31像素是我的Windows机器上的屏幕的高度。所以我的想法是,不知何故,当使用机器人的第二个图像没有正确创建。不知道为什么。
下面是我用来测试的代码:

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;

public class ScreenCapTest2 {
    public static void main(String[] args) throws AWTException {
        Robot r = new Robot();
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        BufferedImage screen = r.createScreenCapture(new Rectangle(0,0, (int)screenSize.getWidth(), (int)screenSize.getHeight()));

        int xOffset = 20;
        int yOffset = 50;
        BufferedImage subscreen = screen.getSubimage(xOffset, yOffset, (int)screenSize.getWidth()-xOffset, (int)screenSize.getHeight()-yOffset);
//        BufferedImage subscreen = r.createScreenCapture(new Rectangle(xOffset, yOffset, (int)screenSize.getWidth()-xOffset, (int)screenSize.getHeight()-yOffset));
//        BufferedImage subscreen = r.createScreenCapture( new Rectangle(xOffset, 49, 1900, 1000) );

        System.out.println( screenSize );
        System.out.println(indexOfSubImage(screen, subscreen));
    }

    public static Point indexOfSubImage(BufferedImage img, BufferedImage subimg) {
        for (int x=0; x <= img.getWidth() - subimg.getWidth(); x++) {
            for (int y=0; y <= img.getHeight() - subimg.getHeight(); y++) {
//                if (imgEqual(img.getSubimage(x, y, subimg.getWidth(), subimg.getHeight()), subimg)) {
                if (imgEqual(x, y, img, subimg)) {
                    return new Point(x,y);
                }
            }
        }
        return new Point(-1,-1);
    }

    public static boolean imgEqual(int offsetX, int offsetY, BufferedImage image1, BufferedImage image2) {

        for(int x = 0; x < image2.getWidth(); x++){
            for(int y = 0; y < image2.getHeight(); y++){
                if( image1.getRGB(x + offsetX, y + offsetY) != image2.getRGB(x, y) ){
                    return false;
                }
            }
        }

        return true;
    }
}

字符串
第一个子图像在不同的测试用例中对我有效。
第二个子图像仅在使用(0,0)作为偏移量时有效。
第三个子图像似乎适用于所有“y”偏移量高达49。
编辑:
使用你的新代码,我尝试自动化测试:

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ScreenCapTest3{
    public static void main(String[] args) throws AWTException, IOException {

        for (int i = 0; i < 1920 - 50; i += 50)
            test( i );
    }

    public static void test(int xOffset) throws AWTException
    {
        // Construct Robot
        Robot r = new Robot();

        // Get dimensions of screen
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

        // Take screenshot
        BufferedImage screen = r.createScreenCapture(new Rectangle(0,0, (int)screenSize.getWidth(), (int)screenSize.getHeight()));

        // Take screenshot of small section of screen
        int x = xOffset;
        int y = 50;
//        int w = 1920 - xOffset;
//        int h = 1000;
        int w = 50;
        int h = 500;

        BufferedImage subscreen = r.createScreenCapture(new Rectangle(x,y,w,h));

        // Create a subimage of the same section of the screen from the original screenshot
        BufferedImage subimageOfScreen = screen.getSubimage(x,y,w,h);

        // Are they equal?
        System.out.println(xOffset + " : " + imgEqual(subimageOfScreen, subscreen));

        // Output images for comparison
//        ImageIO.write(subimageOfScreen, "png", new File("subimage.png"));
//        ImageIO.write(subscreen, "png", new File("subscreen.png"));
    }

    public static boolean imgEqual(BufferedImage image1, BufferedImage image2) {
        int width;
        int height;
        boolean imagesEqual = true;

        if( image1.getWidth()  == ( width  = image2.getWidth() ) &&
            image1.getHeight() == ( height = image2.getHeight() ) ){

            for(int x = 0;imagesEqual == true && x < width; x++){
                for(int y = 0;imagesEqual == true && y < height; y++){
                    if( image1.getRGB(x, y) != image2.getRGB(x, y) ){
//                      System.out.println(x + " : " + y);
                        imagesEqual = false;
                    }
                }
            }
        } else {
            imagesEqual = false;
        }
        return imagesEqual;
    }
}


我在命令提示符下使用:java ScreenCapTest3.java运行代码,得到了true/false的混合结果。
然后运行代码:java ScreenCapTest.java > out.txt
当我看out.txt时,一切都是真的。
所以在我看来,子图像中有一个微妙的变化,因为System.out.println(...)在捕获第二个图像之前显示在屏幕上?
另一个编辑:
创建了一个简单的框架来覆盖桌面。我用左边的按钮和中间的大图像来打破一个空框架的白色空间。
当你点击按钮时,我使用了一个计时器来延迟屏幕捕获过程,以允许按钮被绘制回原始状态。

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;

public class ScreenTest extends JPanel
{
    JList<String> list = new JList<>();
    DefaultListModel<String> model = new DefaultListModel<>();

    public ScreenTest()
    {
        setLayout( new BorderLayout() );

        JButton create = new JButton("Test Sub Image");
        add(create, BorderLayout.PAGE_START);

        JPanel west = new JPanel( new GridLayout(50, 5) );

        for (int i = 0; i < 250; i++)
            west.add( new JButton("just for decoration") );

        add(west, BorderLayout.LINE_START);

        JLabel center = new JLabel( new ImageIcon("grass.jpg") );
        add(center);

        add(new JScrollPane(list), BorderLayout.LINE_END);

        create.addActionListener((e) -> startTest());
    }

    private void startTest()
    {
        ActionListener al = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    for (int i = 0; i < 1920 - 500; i += 50)
                        test( i );

                    list.setModel( model );
                }
                catch (Exception e2) { System.out.println(e2); }
            }
        };

        Timer timer = new Timer(1000, al);
        timer.setRepeats( false );
        timer.start();
    }

    private void test(int xOffset) throws AWTException
    {
        try
        {
            // Construct Robot
            Robot r = new Robot();

            // Get dimensions of screen
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

            // Take screenshot
            BufferedImage screen = r.createScreenCapture(new Rectangle(0,0, (int)screenSize.getWidth(), (int)screenSize.getHeight()));

            // Take screenshot of small section of screen
            int x = xOffset;
            int y = 50;
            int w = 500;
            int h = 500;

            BufferedImage subscreen = r.createScreenCapture(new Rectangle(x,y,w,h));

            // Create a subimage of the same section of the screen from the original screenshot
            BufferedImage subimageOfScreen = screen.getSubimage(x,y,w,h);

            // Are they equal?
            model.addElement(xOffset + " : " + imgEqual(subimageOfScreen, subscreen));
        }
        catch (Exception e) { System.out.println(e); }
    }

    private boolean imgEqual(BufferedImage image1, BufferedImage image2)
    {
        int width;
        int height;
        boolean imagesEqual = true;

        if( image1.getWidth()  == ( width  = image2.getWidth() ) &&
            image1.getHeight() == ( height = image2.getHeight() ) ){

            for(int x = 0;imagesEqual == true && x < width; x++){
                for(int y = 0;imagesEqual == true && y < height; y++){
                    if( image1.getRGB(x, y) != image2.getRGB(x, y) ){
//                      System.out.println(x + " : " + y);
                        imagesEqual = false;
                    }
                }
            }
        } else {
            imagesEqual = false;
        }
        return imagesEqual;
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ScreenTest());
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}

相关问题