OSError: image file is truncated (XX bytes not processed) 报错解决指南
一、前言
我在训练深度神经网络时遇到了如下报错
······此处省略一万字
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/PIL/Image.py", line 995, in convert
self.load()
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/PIL/ImageFile.py", line 290, in load
raise OSError(msg)
OSError: image file is truncated (61 bytes not processed)
关于这个报错,我在网络上找到了解决方案。首先我要感谢他们的无私奉献,为我的科研工作节省了不少时间。美中不足的是大部分文章都是知其然而不知其所以然,写了两行代码草草了事。
这是非常不严谨的,对于很多小白来说,直接会懵掉:代码啥意思?该写在哪里?
下面我来尝试解决这些问题,希望对大家有帮助。
二、可能的原因
这个错误其含义是:由于图像文件在读取过程中被截断,以下是一些可能的原因:
- 文件读取不完整:文件是不完整的,读取过程中可能会出现问题,导致文件未完全加载。
- PIL库的限制:
PIL(Python Imaging Library)
有一个默认的最大块大小(MAXBLOCK
),如果图像文件超过这个大小,可能会导致截断错误。 - 文件传输或存储问题:在文件传输或存储过程中,可能会出现数据损坏或丢失,导致读取时出现错误。
当然,一般遇到这个问题是由于第2个原因。不过我还是提供 原因1 和 原因2 为根源的问题解决方案,但第2个优先(出现频率高);如果问题由第3个原因引起,还请读者自查。
三、解决方法
3.1 PIL库限制 解决方案
我们需要设置 ImageFile.LOAD_TRUNCATED_IMAGES = True
来允许PIL忽略截断错误并继续加载图像。
那我代码开头添加以下两行:
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
完犊子了,加在哪?为了回答这个问题,我把我控制台的反馈再写完整一点:
OSError: Caught OSError in DataLoader worker process 0.
Original Traceback (most recent call last):
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/torch/utils/data/_utils/worker.py", line 287, in _worker_loop
data = fetcher.fetch(index)
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/torch/utils/data/_utils/fetch.py", line 32, in fetch
data.append(next(self.dataset_iter))
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/data/common.py", line 296, in __iter__
yield self.dataset[idx]
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/data/common.py", line 125, in __getitem__
data = self._map_func(self._dataset[cur_idx])
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/utils/serialize.py", line 26, in __call__
return self._obj(*args, **kwargs)
File "/liushuai2/PCP/FeatEnHancer-main/Featurized-QueryRCNN/queryrcnn/dataset_mapper.py", line 82, in __call__
image = utils.read_image(dataset_dict["file_name"], format=self.img_format)
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/data/detection_utils.py", line 187, in read_image
return convert_PIL_to_numpy(image, format)
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/data/detection_utils.py", line 78, in convert_PIL_to_numpy
image = image.convert(conversion_format)
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/PIL/Image.py", line 995, in convert
self.load()
File "/root/anaconda3/envs/FEHR/lib/python3.9/site-packages/PIL/ImageFile.py", line 290, in load
raise OSError(msg)
OSError: image file is truncated (61 bytes not processed)
从反馈的异常跟踪来看,最终是由 ImageFile.py
抛出了异常。但是这个文件是直接来自PIL库,直接改库代码?也不是不行,但是我们一般不这样干。
因此再往回看,发现
File "/liushuai2/PCP/FeatEnHancer-main/detectron2/detectron2/data/detection_utils.py", line 78, in convert_PIL_to_numpy
image = image.convert(conversion_format)
这两行告诉我了在自己的代码中,引起最终抛出异常的位置在 ”······/detection_utils.py
“ 这个文件中,那么我们就把上面我提到的那两行代码加到这个文件的代码正文之前的位置即可解决问题。
3.2 文件不完整 解决方案
首先得找到哪些文件不完整吧,下面的代码可以实现这个功能,你只需要提供需要检查的目录(重写directory
变量的值)即可:
import os
from PIL import Image
from concurrent.futures import ThreadPoolExecutor, as_completed
def is_image_valid(file_path):
try:
with Image.open(file_path) as img:
img.verify() # 验证图像文件的完整性
return True
except (IOError, SyntaxError) as e:
print(f"Invalid image file: {file_path} - {e}")
return False
def check_and_remove_invalid_images(directory, max_workers=8):
# 获取所有文件,默认文件夹中都是图片如果不是请读者自行加条件
image_files = [os.path.join(directory, f) for f in os.listdir(directory)]
valid_images = []
invalid_images = []
# 使用线程池执行验证任务
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交验证任务到线程池,得到线程 Future 对象和其执行检查的文件路径的映射。便于后续处理
future_to_file = {executor.submit(is_image_valid, file): file for file in image_files}
# 等待所有任务完成
for future in as_completed(future_to_file):
file = future_to_file[future] # 获取文件路径
# 如果检查通过(返回True),则将文件路径添加到有效列表中,否则将其添加到无效列表中
if future.result():
valid_images.append(file)
else:
invalid_images.append(file)
print(f"Deleting invalid image file: {file}")
os.remove(file)
# 返回有效和无效文件列表
return valid_images, invalid_images
if __name__ == "__main__":
directory = "/liushuai2/PCP/datasets/COCO2017/train2017"
valid_images, invalid_images = check_and_remove_invalid_images(directory)
print(f"Valid images: {len(valid_images)}")
print(f"Invalid images: {len(invalid_images)}")
if invalid_images:
print("Invalid image files:")
for file in invalid_images:
print(file)
上面的代码会检测出有问题的图像,并将其删除。这就需要你自己再去补充新图片咯。
下面还有一种方法,它用于解决图像并不是”真“的损坏的,它会用OpenCV读取并重新写入一次文件,一般可以解决问题:
import os
from PIL import Image
import cv2
from concurrent.futures import ThreadPoolExecutor, as_completed
def is_image_valid(file_path):
try:
with Image.open(file_path) as img:
img.verify() # 验证图像文件的完整性
return True
except (IOError, SyntaxError) as e:
print(f"Invalid image file: {file_path} - {e}")
return False
def try_rewrite_image(file_path):
try:
img = cv2.imread(file_path)
if img is not None:
# 使用 OpenCV 重写图像
cv2.imwrite(file_path, img)
print(f"Rewritten image file: {file_path}")
return True
else:
print(f"Failed to read image with OpenCV: {file_path}")
return False
except Exception as e:
print(f"Error rewriting image file: {file_path} - {e}")
return False
def check_and_fix_invalid_images(directory, max_workers=8):
image_files = [os.path.join(directory, f) for f in os.listdir(directory)]
valid_images = []
invalid_images = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交验证任务到线程池,得到线程 Future 对象和其执行检查的文件路径的映射。便于后续处理
future_to_file = {executor.submit(is_image_valid, file): file for file in image_files}
# 等待所有任务完成
for future in as_completed(future_to_file):
file = future_to_file[future] # 获取文件路径
# 如果检查通过(返回True),则将文件路径添加到有效列表中,否则将其添加到无效列表中,并尝试修复
if future.result():
valid_images.append(file)
else:
if try_rewrite_image(file):
valid_images.append(file)
else:
invalid_images.append(file)
print(f"Deleting invalid image file: {file}")
os.remove(file)
# 返回有效和无效文件列表
return valid_images, invalid_images
if __name__ == "__main__":
directory = "/liushuai2/PCP/datasets/COCO2017/train2017"
valid_images, invalid_images = check_and_fix_invalid_images(directory)
print(f"Valid images: {len(valid_images)}")
print(f"Invalid images: {len(invalid_images)}")
if invalid_images:
print("Invalid image files:")
for file in invalid_images:
print(file)