JPEG, PNG, GIF などの破損をチェックする Python コード例を書きました。
画像は、『Pillow (PIL) ライブラリ』でチェックすることができました。
画像形式によっては、破損検出に使える仕組みが無かったりもしたので、簡易的なチェックになります。
それでも、今まで気づかなかった破損画像を、いくつか見つけることができました。
※ もっと厳密に、広くエラーを検出したいときは、ImageMagick を使用した方法が便利でした。
⇒ 【Python】画像の破損をチェックするコード例【ImageMagick】
ImageMagick なら、Pillow の im.verify()
で検出できなかった破損でも検出することができました。
破損画像のチェック方法
画像の破損を調べるところの Python コードです。
画像が壊れていた時は、Image.open()
や im.verify()
のところで、例外が発生しました。
実際には、例外を try 文でキャッチして、画面に表示したり、ログファイルに記録したりして活用します。
画像ファイルの場合
ファイルをバイナリーモードで開いてから、Pillow ライブラリで読み込みました。
from pathlib import Path
from PIL import Image # Pillow ライブラリ
# 画像ファイルをバイナリーモードで開きます。
image_file = Path(r'(画像ファイルのパス)')
with image_file.open('rb') as f:
# PIL で画像データを開きます。
# PIL が『画像形式を判定できなかった場合』は、
# UnidentifiedImageError の例外が発生しました。
im = Image.open(f, 'r')
# 画像データの破損をチェックします。
# ベリファイに失敗した場合は、
# SyntaxError などの例外が発生しました。
im.verify()
バイナリデータの場合
まず、変数に入っている画像のバイナリーデータを、バイナリーストリームに変換します。
それを Pillow ライブラリで読み込みました。
この方法は、zip ファイルの中にあった画像を調べるときに使用しました。
Python の zipfile ライブラリを使用したら、画像ファイルのバイナリーデータが返ってきたんですよね。
それで必要になりました。
from io import BytesIO
from PIL import Image # Pillow ライブラリ
# 画像のバイナリーデータ image_data を、
# 『バイナリーストリーム』に変換します。
with BytesIO(image_data) as buffer:
# PIL で画像データを開きます。
# PIL が『画像形式を判定できなかった場合』は、
# UnidentifiedImageError の例外が発生しました。
im = Image.open(buffer, 'r')
# 画像データの破損をチェックします。
# ベリファイに失敗した場合は、
# SyntaxError などの例外が発生しました。
im.verify()
Pillow マニュアルの場所
Pillow ライブラリは、画像データをチェックするために使用しました。
(Pillow) Windows Installation 『Pillow』のインストール方法。
(Pillow) PIL.Image.open(fp, mode='r', formats=None)
画像ファイルを開く。
(Pillow) Image.verify()
画像の内容をチェックする。
Python マニュアルの場所
(Python) class io.BytesIO([initial_bytes])
バイツアイオー(バイツイオ)。
(Python) class pathlib.Path(*pathsegments)
パスオブジェクトを作成。
(Python) Path.glob(pattern)
ファイルを列挙する。
(Python) Path.is_file()
パスがファイルか否かを判定する。
(Python) PurePath.suffix
パスから拡張子の部分を取得。
(Python) re.match(pattern, string, flags=0)
正規表現で拡張子を判定するときに使用。
(Python) re.IGNORECASE
正規表現で大文字と小文字を『区別しない』ようにするためのフラグ。
(Python) Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
ファイルを開く。
エフ f
から始まる文字列 f''
は、『エフストリング (f-string)』と呼ばれるものでした。
(Python) フォーマット済み文字列リテラル (f-string)
(Python) getattr(object, name[, default])
ゲットアトリビュート関数。object から属性 (Attribute) を取得する。
(Python) traceback.format_exception_only(etype, value)
フォーマット エクセプション オンリー。エラーメッセージの最後の行だけを取得。
(Python) str.rstrip([chars])
末尾から指定した文字を除去。
(Python) print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
普通のプリント関数。
(Python) instance.__class__
特殊属性
(Python) definition.__name__
特殊属性
コード例
『フォルダの中の画像ファイル』を列挙して、画像データをチェックしていく Python コード例です。
"""
画像データ (jpg, png, gif など) の
破損をチェックする Python コード例。
check_image_files.py
"""
from pathlib import Path
import re
from traceback import format_exception_only
from PIL import Image # Pillow ライブラリ
def main():
"""メイン関数"""
print('start')
# (1/4) 画像ファイルの場所を決めます。
src_dir = Path(r'F:\project\sample_images')
print(f'(src_dir) {src_dir}')
# (2/4) フォルダの中のファイルを列挙します。
for p in src_dir.glob('*'):
# ファイルでなければ、スキップします。
if not p.is_file():
continue
# 拡張子が画像でなければ、スキップします。
# p.suffix: パスの拡張子の部分。
# re.IGNORECASE: 大文字小文字を無視。
if not re.match(r'^\.(?:jpg|png|gif)$', p.suffix, flags=re.IGNORECASE):
continue
# (3/4) 画像ファイルを開きます。
with p.open('rb') as f:
# (4/4) 画像データをチェックします。
try:
# PIL で画像データを開きます。
# PIL が『画像形式を判定できなかった場合』は、
# UnidentifiedImageError の例外が発生しました。
im = Image.open(f, 'r')
# 画像データの破損をチェックします。
# ベリファイに失敗した場合は、
# SyntaxError などの例外が発生しました。
im.verify()
except Exception as e:
# 何らかのエラーがあった。
print(f'NG {p.name} {e.__class__.__name__}')
# e に msg 属性があれば取得、無ければ None にしておきました。
msg = getattr(e, 'msg', None)
print(f'(message) {msg}')
# トレースバックの最後の行だけを取得してみました。
ex_text = format_exception_only(type(e), e)[0].rstrip('\n')
print(f'(exception) {ex_text}')
else:
# エラーは検出できなかった。
print(f'ok {p.name}')
print('end')
return
if __name__ == '__main__':
main()
実行結果
実行結果です。
画像は Windows 10 の『ペイント』で保存したものを使用しました。
NG 判定になっている画像は、バイナリーエディタを使用して、データをわざと書き換えたものです。
ヘッダー部分を書き換えたり、末尾の部分を削ったりしてみました。
start
(src_dir) F:\project\sample_images
ok image_g1.gif
NG image_g2.gif UnidentifiedImageError
(message) None
(exception) PIL.UnidentifiedImageError: cannot identify image file
<_io.BufferedReader name='F:\\project\\sample_images\\image_g2.gif'>
ok image_j1.jpg
NG image_j2.jpg UnidentifiedImageError
(message) None
(exception) PIL.UnidentifiedImageError: cannot identify image file
<_io.BufferedReader name='F:\\project\\sample_images\\image_j2.jpg'>
ok image_p1.png
NG image_p2.png SyntaxError
(message) broken PNG file (chunk b'')
(exception) File "<string>", line None
NG image_p3.png SyntaxError
(message) broken PNG file (bad header checksum in b'IDAT')
(exception) File "<string>", line None
end
画像データの破損は時々ありました
画像に限らず、データの破損に気が付くことって、時々あるんですよね。
原因としては、その時々の状況からして、SD カードや USB 機器の調子が悪かったり、突然の停電であったり、Web からのダウンロードが中途半端で完了していたり、といった感じでした。
zip ファイルで圧縮しておけば、セブンジップ 7-Zip (7z) というソフトで、簡単に破損チェックをすることができたんですけれどもね。
zip ファイルの場合は、ファイルの一つ一つに CRC32 というチェックサムが付いていたので、そういったことが可能になるようでした。
実際に、バイナリエディタで適当に1箇所だけ、わざとゼロに書き換えてみました。
そして、7-Zip のアーカイブテストを実行したら、該当するファイルのところで『CRC が違います』と教えてくれました。
ですが、素の状態の画像ファイルには、そういった仕組みがあまり無いようでした(画像形式による感じ)。
なので、『何とかチェックできないかな?』と思って、破損チェックのコードを書いてみました。
画像形式によっては、検知できなかった破損も多々ありましたが、検知できた破損もありました。
Python 用の Pillow ライブラリは便利でしたね。
バイナリエディタを使って、部分的に書き換えたり、データを削ってみたりした画像をチェックしてみました。
検知できた破損画像の例です。
- 画像データの先頭付近にある『マジックナンバーとかシグネチャとか呼ばれている領域』が壊れていたケース。
- 画像の『ヘッダー領域』が壊れていたケース。
- PNG 画像で、データの後半が壊れていたケース。
このような感じでした。
しかしながら、表示は乱れたけれど『読み込み自体は出来てしまった』というケースが、多々ありました。
それでも、破損部分によっては、検出が可能でした。
簡易的なチェックツールとしては十分でしたね。
試しに、データフォルダの中身をまとめてチェックしてみたところ、今まで気づいていなかった破損画像が、いくつか見つかりました。
実際に画像を開いてみたら、途中からグレーや黒一色になっていたり、開くこと自体ができなかったりしました。
壊れたデータを持っていても意味がないので、サクッと削除しました。
スッキリです。