使用 Java 以编程方式确定 2 个图像是否相同

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16602807/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 23:26:20  来源:igfitidea点击:

Programmatically determine if 2 images look the same using Java

javaimage

提问by Eric

In JAVA I am trying to programmatically tell if 2 images are equal when displayed on the screen (AKA same image even though they have different color spaces. Is there a piece of code that will return a boolean when presented 2 images?

在 JAVA 中,我试图以编程方式判断 2 个图像在屏幕上显示时是否相等(AKA 相同的图像,即使它们具有不同的色彩空间。是否有一段代码会在呈现 2 个图像时返回布尔值?

One of the examples I have is a RGB PNG that I converted to a greyscale PNG. Both images look the same and I would like to prove this programmatically. Another example is two images where they display the exact same color pixels to the screen but the color used for 100% transparent pixels has changed.

我拥有的示例之一是我将其转换为灰度 PNG 的 RGB PNG。两个图像看起来相同,我想以编程方式证明这一点。另一个示例是两个图像,它们在屏幕上显示完全相同的颜色像素,但用于 100% 透明像素的颜色已更改。

采纳答案by Eric

I looked at all of the solutions and determined that they could tell you how different the images were or worked for some types of images, but not all of them. Here is the solution I came up with...

我查看了所有解决方案,并确定它们可以告诉您图像对于某些类型的图像有何不同或适用,但不是全部。这是我想出的解决方案......

package image.utils;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.ImageIcon;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility methods used to interact with images.
 */
public class ImageUtils {

    private final static Logger logger = LoggerFactory.getLogger(ImageUtils.class);

    private static final boolean equals(final int[] data1, final int[] data2) {
        final int length = data1.length;
        if (length != data2.length) {
            logger.debug("File lengths are different.");
            return false;
        }
        for(int i = 0; i < length; i++) {
            if(data1[i] != data2[i]) {

                //If the alpha is 0 for both that means that the pixels are 100%
                //transparent and the color does not matter. Return false if 
                //only 1 is 100% transparent.
                if((((data1[i] >> 24) & 0xff) == 0) && (((data2[i] >> 24) & 0xff) == 0)) {
                    logger.debug("Both pixles at spot {} are different but 100% transparent.", Integer.valueOf(i));
                } else {
                    logger.debug("The pixel {} is different.", Integer.valueOf(i));
                    return false;
                }
            }
        }
        logger.debug("Both groups of pixels are the same.");
        return true;
    }

    private static final int[] getPixels(final BufferedImage img, final File file) {

        final int width = img.getWidth();
        final int height = img.getHeight();
        int[] pixelData = new int[width * height];

        final Image pixelImg; 
        if (img.getColorModel().getColorSpace() == ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
            pixelImg = img;
        } else {
            pixelImg = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(img, null);
        }

        final PixelGrabber pg = new PixelGrabber(pixelImg, 0, 0, width, height, pixelData, 0, width);

        try {
            if(!pg.grabPixels()) {
                throw new RuntimeException();
            }
        } catch (final InterruptedException ie) {
            throw new RuntimeException(file.getPath(), ie);
        }

        return pixelData;
    }

    /**
     * Gets the {@link BufferedImage} from the passed in {@link File}.
     * 
     * @param file The <code>File</code> to use.
     * @return The resulting <code>BufferedImage</code>
     */
    @SuppressWarnings("unused")
    final static BufferedImage getBufferedImage(final File file) {
        Image image;

        try (final FileInputStream inputStream = new FileInputStream(file)) {
            // ImageIO.read(file) is broken for some images so I went this 
            // route
            image = Toolkit.getDefaultToolkit().createImage(file.getCanonicalPath());

            //forces the image to be rendered
            new ImageIcon(image);
        } catch(final Exception e2) {
            throw new RuntimeException(file.getPath(), e2);
        }

        final BufferedImage converted = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2d = converted.createGraphics();
        g2d.drawImage(image, 0, 0, null);
        g2d.dispose();
        return converted;
    }

    /**
     * Compares file1 to file2 to see if they are the same based on a visual 
     * pixel by pixel comparison. This has issues with marking images different
     * when they are not. Works perfectly for all images.
     * 
     * @param file1 First file to compare
     * @param file2 Second image to compare
     * @return <code>true</code> if they are equal, otherwise 
     *         <code>false</code>.
     */
    private final static boolean visuallyCompareJava(final File file1, final File file2) {
        return equals(getPixels(getBufferedImage(file1), file1), getPixels(getBufferedImage(file2), file2));
    }

    /**
     * Compares file1 to file2 to see if they are the same based on a visual 
     * pixel by pixel comparison. This has issues with marking images different
     * when they are not. Works perfectly for all images.
     * 
     * @param file1 Image 1 to compare
     * @param file2 Image 2 to compare
     * @return <code>true</code> if both images are visually the same.
     */
    public final static boolean visuallyCompare(final File file1, final File file2) {

        logger.debug("Start comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());

        if(file1 == file2) {
            return true;
        }

        boolean answer = visuallyCompareJava(file1, file2);

        if(!answer) {
            logger.info("The files \"{}\" and \"{}\" are not pixel by pixel the same image. Manual comparison required.", file1.getPath(), file2.getPath());
        }

        logger.debug("Finish comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());

        return answer;
    }

    /**
     * @param file The image to check
     * @return <code>true</code> if the image contains one or more pixels with
     *         some percentage of transparency (Alpha)
     */
    public final static boolean containsAlphaTransparency(final File file) {
        logger.debug("Start Alpha pixel check for {}.", file.getPath());

        boolean answer = false;
        for(final int pixel : getPixels(getBufferedImage(file), file)) {
            //If the alpha is 0 for both that means that the pixels are 100%
            //transparent and the color does not matter. Return false if 
            //only 1 is 100% transparent.
            if(((pixel >> 24) & 0xff) != 255) {
                logger.debug("The image contains Aplha Transparency.");
                return true;
            }
        }

        logger.debug("The image does not contain Aplha Transparency.");
        logger.debug("End Alpha pixel check for {}.", file.getPath());

        return answer;
    }
}

回答by TomP89

For grayscale images I've used Mean Square Error as a measure of how different two images are before. Just plug the corresponding pixels from each image into the formula.

对于灰度图像,我使用均方误差作为衡量两个图像之前不同的方法。只需将每个图像的相应像素插入公式即可。

Not only can this tell you if they are exactly the same, but also it can tell you how different two images are, albeit in a rather crude manner.

这不仅可以告诉您它们是否完全相同,还可以告诉您两个图像的不同之处,尽管方式相当粗糙。

https://en.wikipedia.org/wiki/Mean_squared_error

https://en.wikipedia.org/wiki/Mean_squared_error

EDIT:

编辑:

Note: This is C# code not Java (apologies but that's what I wrote it in originally), however it should be easily transferable.

注意:这是 C# 代码而不是 Java(抱歉,这是我最初编写的代码),但是它应该很容易转移。

//Calculates the MSE between two images
private double MSE(Bitmap original, Bitmap enhanced)
{
    Size imgSize = original.Size;
    double total = 0;

    for (int y = 0; y < imgSize.Height; y++)
    {
        for (int x = 0; x < imgSize.Width; x++)
        {
            total += System.Math.Pow(original.GetPixel(x, y).R - enhanced.GetPixel(x, y).R, 2);

        }

    }

    return (total / (imgSize.Width * imgSize.Height));
}

回答by agou

If you mean exactly the same, compare each pixel.

如果您的意思完全相同,请比较每个像素。

If you mean compare a RGB image and a greyscale image, you need to convert the RGB to greyscale first, for doing this, you need to know how you did RGB->Greyscale before, there're different ways of doing this and you could get different results.

如果您的意思是比较 RGB 图像和灰度图像,则需要先将 RGB 转换为灰度,为此,您需要知道之前是如何进行 RGB->灰度的,有不同的方法可以做到这一点,您可以得到不同的结果。

Edit, if the method it used in RGB->Greyscale is liner, you could work out a,b,c in the formula grey = a*R + b*G + c*Bby comparing 3 pixels.

编辑,如果它在RGB->灰度中使用的方法是线性的,您可以grey = a*R + b*G + c*B通过比较3个像素来计算公式中的a,b,c 。

回答by Ani

One of the easiest approach I tried is getting the pixel array of both images and comparing them with Arrays.equals method. Code sample :

我尝试过的最简单的方法之一是获取两个图像的像素阵列并将它们与 Arrays.equals 方法进行比较。代码示例:

package image_processing;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import javax.imageio.ImageIO;

public class CheckPixels {
    public static void main(String args[]) throws IOException {

        File pic1 = new File("D:\ani\img1.png");
        File pic2 = new File("D:\ani\img2.png");
        if (Arrays.equals(returnPixelVal(pic1), returnPixelVal(pic2))) {
            System.out.println("Match");
        } else {
            System.out.println("No match");
        }

    }

    public static byte[] returnPixelVal(File in) {

        BufferedImage img = null;
        File f = null;
        byte[] pixels = null;
        // read image
        try {
            f = in;
            img = ImageIO.read(f);
            pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
        } catch (IOException e) {
            System.out.println(e);
        }

        return pixels;

    }

}

回答by Java

You can try this

你可以试试这个

Example [Broken Link]

示例[断开的链接]

They have explained how they compared two images...

他们解释了他们如何比较两个图像......