Python程序中PIL Image “image file is truncated“问题分析与解决

Python程序中PIL Image "image file is truncated"问题分析与解决

 

解决方案

第一种、删除截断图片
第二种、添加两行代码,如下

from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

错误原因

首先看一下错误信息,大致意思是图像文件是损坏的,无法读取,后面37个bytes无法处理

OSError: image file is truncated (37 bytes not processed)

第一次运行程序时没有发生这样的错误,后面觉得图像文件的命名很杂乱,于是就写了一个重命名程序,对文件夹里的数据进行重新命名,程序如下所示

import os
path = './data/train/Image/'
# 获取该目录下所有文件,存入列表中
f = os.listdir(path)
n = 0
for i in f:
    # 设置旧文件名(就是路径+文件名)
    oldname = path + f[n]
    # 设置新文件名
    newname = path + 'Image_' + str(n + 1) + '.JPG'
    # 用os模块中的rename方法对文件改名
    os.rename(oldname, newname)
    print(oldname, '======>', newname)
    n += 1

这样文件夹中的图像命名就会变成“Image_序列”的格式,改完名后,再运行之前的程序,就报错了,思考了一下,因为我用的笔记本是Mac,Mac中每个文件夹里都有一个隐藏文件,保存着改文件夹里所有文件的位置排列等信息,会不会在改名时把这个文件也改成了JPG文件,所以写了一个程序来测试一下

import os
path = './data/'
# 获取该目录下所有文件,存入列表中
f = os.listdir(path)
for i in f:
	print(i)

输出结果中并没有想要的’ .DS_store’ 文件,看来这个想法是错误的,不是Mac的锅。
思考再三,用了一个蠢蠢的办法,把数据集里的图片一个个用keras.preprocessing.image.load_img()打开,如果出错,则把输出图片路径,经过尝试后,终于抓到一个罪魁祸首了,长成这个样子s这里插入图片描述
在本机上,图片中花了的部分其实是一片灰色,这里可能是由于markdown的图片的格式问题,总之这张图片就是导致出错的原因。我把这张图片从数据集中删除后,再运行程序就不会出错了。

这就解决了?

程序通了,按道理这是应该解决了的,可是心中总是有一些疑惑,为什么会出这种错误呢?正好微微有点闲空,就尽可能的深入一点去探一探吧,首先就是查看错误发生的代码处,代码文件是 ImageFile.py ,位于python的site-package文件夹内,下面是报错部分代码


try:
  # FIXME: This is a hack to handle TIFF's JpegTables tag.
  prefix = self.tile_prefix
except AttributeError:
  prefix = b""

for decoder_name, extents, offset, args in self.tile:
  decoder = Image._getdecoder(self.mode, decoder_name,
                              args, self.decoderconfig)
  seek(offset)
  decoder.setimage(self.im, extents)
  if decoder.pulls_fd:
      decoder.setfd(self.fp)
      status, err_code = decoder.decode(b"")
  else:
      b = prefix
      while True:
          try:
              s = read(self.decodermaxblock)
          except (IndexError, struct.error):  # truncated png/gif
              if LOAD_TRUNCATED_IMAGES:
                  break
              else:
                  raise IOError("image file is truncated")

          if not s:  # truncated jpeg
              self.tile = []

              # JpegDecode needs to clean things up here either way
              # If we don't destroy the decompressor,
              # we have a memory leak.
              decoder.cleanup()

              if LOAD_TRUNCATED_IMAGES:
                  break
              else:
                  raise IOError("image file is truncated "
                                "(%d bytes not processed)" % len(b))
  • 可以看到问题就由于最后四行代码
if s is truncated jpeg:
	if LOAD_TRUNCATED_IMAGES == True:  # 缺省值是False
		break
	else:
		raise IOError("image file is truncated " "(%d bytes not processed)" % len(b))

所以,如果不想花时间把数据集中的破损图片找出来后删除掉,只需要设置LOAD_TRUNCATED_IMAGES = True,在程序开头只需要加上两行代码就行了

from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

这样在遇到截断的JPEG时,程序就会跳过去,读取另一张图片了。

继续探索

被截断的图片跟原图有什么区别呢,读入程序里也只是一行行数字而已,就算是灰色的部分或者是花掉的部分,也是一串串数字才是,为什么就会被识别出来呢?
这就跟JPEG的编码相关了,网络上有许多相关介绍,这里就不一一叙述了,不过倒是有两点引起了我的注意

SOI,Start of Image,图像开始 标记代码 : 2字节 固定值0xFFD8
EOI,End of Image,图像结束 标记代码 : 2字节 固定值0xFFD9

意思就是,JPEG图像的二进制流是由 FF D8 开头,由 FF D9 结尾。验证一下,用UltraEdit打开两张正常图片,得到的结果是
在这里插入图片描述
在这里插入图片描述另外一张正常图片也是相同的开头和结尾,这里就不展示了。
那么来看一看截断图的字节流是什么样子的
在这里插入图片描述
在这里插入图片描述看来truncated image的原因找到了,这张图片的编码结尾不是按照EOI的规则来的(FF D9结尾),所以导致程序读图时无法识别这张图片。
尝试了一下在后面填上了 " FF D9 ",在放回数据集中,运行程序,也没有出错

truncated image

这样看来,出错的原因是图片的编码格式不是JPEG的格式,所以程序无法识别,EOI标识是结尾标识,所以丢失了EOI标识就表示图像后面部分的数据丢失,看起来就像被截断了一样,这也许就是用truncated的原因吧 (因为我一直在想为什么不用broken)

再试一次

由上面可以看到就算没有EOI标识,文件后缀名依旧是jpg,所以猜想是SOI决定了文件的格式,为了验证这一想法,用UltraEdit打开一张图片,然后从后往前删除一些字节数据,得到的结果是在这里插入图片描述
再把第一行数据删除,得到的结果是

Windows照片查看器无法打开此图片,因为文件可能已损坏、损毁或过大
  • 1

猜想验证了

总结

错误的原因是找出来了,而且也解决了,只是错误中的37的含义到现在还没能弄清楚,也许在未来的某一天会邂逅到答案吧

OSError: image file is truncated (37 bytes not processed)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值