如何检查文件是否为有效的图像文件?

153

我目前正在使用PIL。

from PIL import Image
try:
    im=Image.open(filename)
    # do stuff
except IOError:
    # filename not an image file

然而,尽管这足以覆盖大多数情况,但一些图像文件例如xcf、svg和psd未被检测到。Psd文件会抛出OverflowError异常。

我能不能想办法也包括它们呢?


27
跨不同语言关闭重复问题并不是特别常见的做法。如果你找不到其他相似的 Python 问题,请将其保持开放状态,因为可能会有一些针对 Python 的特定解决方案,而这些解决方案可能没有出现在你发布的问题中。 - Paolo Bergantino
是的,首先我真的希望有一个我不知道的Python库:P 然后正如Ben指出的那样,仅仅使用魔数并不能验证整个图像。 - Sujoy
@devinb,同意,除非有人提出更好的重构方法,否则我只会获取魔数并完成它 :) - Sujoy
xcf和psd并不是真正的图像,它们是包含(通常有很多)图像的项目文件...但你可能可以为svg辩护。 - mgalgs
PIL 能够检测图像文件的缺陷/错误,但是你需要进行至少一次图像操作才能检测出一些类型的错误,例如我应用了 PIL 的转置变换。仅仅像你建议的那样加载有时无法检测到错误。详见下面我的回答。 - Fabiano Tarlao
显示剩余2条评论
11个回答

255
你可以使用内置的imghdr模块。根据它的文档:
imghdr模块可以确定文件或字节流中包含的图像类型。
以下是使用方法:
>>> import imghdr
>>> imghdr.what('/tmp/bass')
'gif'

注意:imghdr在Python 3.11之后已被弃用,因为它仅支持少量文件格式。

6
是的,imghdr 能够处理大多数的图片格式,但并非所有格式都能被它识别。就像我最初遇到的 SVG、XCF 和 PSD 文件一样,这些也无法在 imghdr 中被识别出来。 - Sujoy
2
你的回答实际上更好,谢谢。就像上面有人说的那样,“...但是解决问题的99%通常比根本没有解决要好得多...” - RinkyPinku
4
需要注意的是:imghdr.what(path) 方法如果给定的 path 不是已识别的图片文件类型,则返回 None列表 中列出了目前已被识别的图片文件类型,包括:rgbgifpbmpgmppmtiffrastxbmjpegbmppngwebpexr - patryk.beza
3
我发现有时候 imghdr.what(path) 会返回 None,即使文件是有效的图像,特别是对于 JPEG 格式的图像。 - GuillaumeDufay
4
小心!有效的hdr并不意味着有效的图像(例如,图像字节可能已经被破坏!) - Filippo Mazza
显示剩余7条评论

56

除了Brian提出的建议外,您还可以使用PIL的verify方法来检查文件是否损坏。

im.verify()

尝试确定文件是否损坏,而不实际解码图像数据。如果此方法发现任何问题,则引发适当的异常。此方法仅在新打开的图像上有效;如果已经加载了图像,则结果是未定义的。此外,如果您需要在使用此方法后加载图像,则必须重新打开图像文件。属性


1
主要问题在于SVG、XCF和PSD文件无法使用Image.open()打开,因此无法通过im.verify()进行验证。 - Sujoy
29
我的天,PIL文档实在太糟糕了。什么是“合适的异常”? - Timmmm
我曾看到验证损坏的 PNG 文件时会引发 SyntaxError 错误。 - Carl
有没有一种方法可以在不实际解码图像数据的情况下进行验证? - Trevor Boyd Smith
6
源代码似乎没有验证任何东西! https://pillow.readthedocs.io/en/latest/_modules/PIL/Image.html#Image.verify - Massimo
显示剩余5条评论

31

除了PIL图像检查之外,您还可以添加文件名扩展名检查,如下所示:

filename.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif'))

请注意,这仅检查文件名是否具有有效的图像扩展名,它不会实际打开图像以查看其是否有效。这就是为什么您需要另外使用PIL或其他答案中建议的库。


6
如果文件的后缀名不正确,会怎样呢?例如,一个文本文件保存为 .jpg 扩展名,或者反之。 - hafiz031
3
@hafiz031 要获取实际格式,您可以执行以下操作:from PIL import Image img = Image.open(filename) print(img.format) 然后像这样检查它:img.format.lower() in ['png', 'jpg', 'jpeg', 'tiff', 'bmp', 'gif'] - tsveti_iko
很遗憾,这对我没有起作用。它仍然将一个损坏的图像识别为JPEG图像。最终我用以下方法解决了这个问题(我正在使用OpenCv):https://dev59.com/5FTTa4cB1Zd3GeqPvLxe#63421847 - hafiz031

16
很多时候,各种文件格式的前几个字符都是一个特殊标识。除了上述异常检查之外,您可以检查这个特殊标识。

14
如果他真的在测试“有效”的图像,仅凭魔数(magic number)的存在并不能保证文件未被截断,因此这并不足够。 - Ben Blank
1
非常好的建议,现在我只需要弄清楚那些数字是什么。谢谢 :) - Sujoy
@ben,哎呀我还没想到那个。确实是一个好点。 - Sujoy
@Ben,你希望一个库如何推断文件已被截断? - DevinB
9
@Ben Blank:确实,但是将问题解决99%通常比根本不解决要好。 - Brian R. Bondy

14

一种选项是使用filetype包。

安装

python -m pip install filetype

优势

  1. 快速:仅加载图像的前几个字节即可完成工作(检查魔数
  2. 支持不同的MIME类型:图像、视频、字体、音频、档案文件。

示例

filetype >= 1.0.7

import filetype

filename = "/path/to/file.jpg"

if filetype.is_image(filename):
    print(f"{filename} is a valid image...")
elif filetype.is_video(filename):
    print(f"{filename} is a valid video...")

文件类型 <= 1.0.6

import filetype

filename = "/path/to/file.jpg"

if filetype.image(filename):
    print(f"{filename} is a valid image...")
elif filetype.video(filename):
    print(f"{filename} is a valid video...")

官方存储库的其他信息:https://github.com/h2non/filetype.py


文件类型程序也支持webp格式。太好了! - Nav

12

更新

我还在我的Python脚本这里的GitHub链接中实现了以下解决方案。

我还验证了损坏的文件(jpg)通常不是“损坏”的图像文件,即损坏的图片文件有时仍然是合法的图片文件,原始图像丢失或被修改,但您仍然能够加载它而没有错误。但是,文件截断总是会导致错误。

结束更新

您可以使用Python Pillow(PIL)模块来检查文件是否为有效和完整的图像文件,支持大多数图像格式。

如果您希望检测损坏的图像,@Nadia Alramli正确地建议使用im.verify()方法,但是这不能检测所有可能的图像缺陷,例如,im.verify无法检测到被截断的图像(大多数查看器通常会加载灰色区域)。

Pillow也能够检测这些类型的缺陷,但您必须应用图像操作或图像解码/重新编码才能触发检查。最后,我建议使用此代码:

from PIL import Image

try:
  im = Image.load(filename)
  im.verify() #I perform also verify, don't know if he sees other types o defects
  im.close() #reload is necessary in my case
  im = Image.load(filename) 
  im.transpose(Image.FLIP_LEFT_RIGHT)
  im.close()
except: 
  #manage excetions here

在图像出现缺陷的情况下,此代码将引发异常。请注意,im.verify的速度约比执行图像操作快100倍(我认为翻转是其中一种较便宜的变换)。使用此代码,您将以标准Pillow或Pillow-SIMD模块(现代2.5Ghz x86_64 CPU)每秒约10 MBytes/sec或40 MBytes/sec验证一组图像。
对于其他格式(例如xcf)等,您可以使用Imagemagick包装器Wand,代码如下所示:检查Wand文档:此处,安装:此处
im = wand.image.Image(filename=filename)
temp = im.flip;
im.close()

然而,根据我的实验,Wand无法检测到被截断的图像。我认为它会将缺少的部分加载为灰色区域而不提示。

我了解到Imagemagick有一个外部命令identify可以完成这项工作,但我还没有找到以编程方式调用该函数的方法,也没有测试过这种方法。

我建议始终进行初步检查,检查filesize是否为零(或非常小),这是一个非常便宜的想法:

import os

statfile = os.stat(filename)
filesize = statfile.st_size
if filesize == 0:
  #manage here the 'faulty image' case

7
在Linux上,您可以使用python-magic,它使用libmagic来识别文件格式。
据我所知,libmagic会查看文件并试图向您提供关于文件的更多信息,例如位图尺寸、格式版本等。因此,您可能将其视为“有效性”的表面测试。
对于“有效”的其他定义,您可能需要编写自己的测试。

6

您可以使用Python绑定到libmagic的python-magic,然后检查mime类型。这样做无法告诉您文件是否损坏或完好,但应该能够确定图片的类型。


2

从Fabiano和Tiago的答案进行调整。

from PIL import Image

def check_img(filename):
    try:
        im = Image.open(filename)
        im.verify()
        im.close()
        im = Image.open(filename) 
        im.transpose(Image.FLIP_LEFT_RIGHT)
        im.close()
        return True
    except: 
        print(filename,'corrupted')
        return False

if not check_img('/dir/image'):
    print('do something')

-2

可以使用图像的扩展名来检查图像文件,如下所示。

import os
for f in os.listdir(folderPath):
    if (".jpg" in f) or (".bmp" in f):
        filePath = os.path.join(folderPath, f)
 

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