如何在Java中检测损坏的图像(PNG,JPG)

15

我需要在Java中检测图像文件是否损坏。我只使用PNG、JPG格式的图片。使用Sanselan可以实现吗?或者可以使用ImageIO来完成吗?我尝试过使用ImageIO.read似乎可以工作。但我不确定它能够检测所有种类的图像错误。我想知道最佳实践是什么。


2
我建议检测图像中的每种错误(其中“错误”被定义为任何导致不完美行为的差异)的唯一方法是继续使用该图像。一个图像完全有可能遭受损坏,但仍然产生一个有效的文件,尽管它具有“错误的像素”。思考您想要检测的内容非常具体可能会很有用。 - Andrzej Doyle
2
ImageIO可以检测到被截断的PNG并抛出异常,但对于被截断的JPG,我无法让它抛出异常。 - Archimedes Trajano
你有没有找到解决方案? - Manu
4个回答

16

以下是我的解决方案,可以检查损坏的 GIF、JPG 和 PNG。它使用 JPEG EOF 标记检查截断的 JPEG,使用索引越界异常检查 GIF,并使用 EOFException 检查 PNG。

public static ImageAnalysisResult analyzeImage(final Path file)
        throws NoSuchAlgorithmException, IOException {
    final ImageAnalysisResult result = new ImageAnalysisResult();

    final InputStream digestInputStream = Files.newInputStream(file);
    try {
        final ImageInputStream imageInputStream = ImageIO
                .createImageInputStream(digestInputStream);
        final Iterator<ImageReader> imageReaders = ImageIO
                .getImageReaders(imageInputStream);
        if (!imageReaders.hasNext()) {
            result.setImage(false);
            return result;
        }
        final ImageReader imageReader = imageReaders.next();
        imageReader.setInput(imageInputStream);
        final BufferedImage image = imageReader.read(0);
        if (image == null) {
            return result;
        }
        image.flush();
        if (imageReader.getFormatName().equals("JPEG")) {
            imageInputStream.seek(imageInputStream.getStreamPosition() - 2);
            final byte[] lastTwoBytes = new byte[2];
            imageInputStream.read(lastTwoBytes);
            if (lastTwoBytes[0] != (byte)0xff || lastTwoBytes[1] != (byte)0xd9) {
                result.setTruncated(true);
            } else {
                result.setTruncated(false);
            }
        }
        result.setImage(true);
    } catch (final IndexOutOfBoundsException e) {
        result.setTruncated(true);
    } catch (final IIOException e) {
        if (e.getCause() instanceof EOFException) {
            result.setTruncated(true);
        }
    } finally {
        digestInputStream.close();
    }
    return result;
}

public class ImageAnalysisResult {
    boolean image;
    boolean truncated;
    public void setImage(boolean image) {
        this.image = image;
    }
    public void setTruncated(boolean truncated) {
        this.truncated = truncated;
    }
 }
}

2
JPEG 条件检查的两个子句难道不应该使用逻辑 OR 连接,而不是 AND 吗? - mnicky
你能否链接这个 ImageAnalysisResult 类? - Manu
请在您的回答中包含所有的导入语句。 - Manu
1
我也有同样的需求,所以基于这个答案,我建立了一个小型库!希望它能有所帮助:https://github.com/lamba92/KImageCheck - Lamberto Basti

6

如果是JPEG格式的图片,请使用以下方法:

JPEGImageDecoder decoder = new JPEGImageDecoder(new FileImageSource(f) ,new FileInputStream(f));
decoder.produceImage();

如果出现异常,就意味着图像已经损坏。

对于其他情况,只需使用new ImageIcon(file)来检查有效性。


如果它不是有效的 JPEG 图像,则会抛出 ImageFormatException 异常,否则它可以正常工作。有没有办法检查 MP4 文件是否损坏? - Sumanth Varada
使用 PNGImageDecoder 处理 PNG 格式,使用 GifImageDecoder 处理 gif 格式。 - Mir-Ismaili
在Java 9+中,import sun.awt.image.JPEGImageDecoder会导致以下错误:Package 'sun.awt.image' is declared in module 'java.desktop', which does not export it to module 'MODULE_NAME' - Mir-Ismaili

2
以下代码用于在Java中验证.jpeg、.png、.jpg和.tiff图像文件:
public boolean isValidImage(File f) {
  boolean isValid = true;
  try {
    ImageIO.read(f).flush();
  } catch (Exception e) {
    isValid = false;
  }
  return isValid;
}

在JPG格式(我没有测试其他格式)的情况下,这种方法只适用于内容没有头部的情况。因此,它无法检测带有头部的不完整图像。在这种情况下,“Archimides”的答案最好,因为它可以验证图像是否完整到结尾。 - lepe

1

如果图像无法解析,则文件已损坏,否则文件应该是有效的,但包含错误的像素,正如Andrzej所指出的那样。如果您无法定义如何找到“错误”的像素,则可能很难检测到它们。

如果您拥有有关基本图像的信息,例如直方图或甚至原始像素,则可以尝试将其与读取的图像进行比较。但请注意,由于压缩可能会出现一些错误,因此您需要添加一些容差值。

另外需要注意的是:Sanselan无法读取JPEG图像,因此您必须在此处使用ImageIO。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接