LoginSignup
1
2

More than 3 years have passed since last update.

Pythonで画像ファイルの仕分け(2)

Posted at

まえがき

前回、大量の画像ファイルを年月フォルダに仕分けするところまではやった。その際に重複しているファイルを削除するための手がかりとして次のようなCSVファイルを各フォルダに生成した。

info.csv
IMG_2607.jpg,BCF3E765,1944106
IMG_2607(1).jpg,BCF3E765,1944106
IMG_2608.jpg,02B27221,3109397
IMG_2608(1).jpg,02B27221,3109397
010(8).jpg,E4A68AB2,3801239
010(9).jpg,3EBBC7BD,1841698
010(10).jpg,B9431E60,103645

左からファイル名、CRC32、ファイルサイズとなっておりCRC32とファイルサイズが一致するファイルはほぼ間違いなく同じファイルであろうということで削除対象にする。前記のファイルで言えば2行目と4行目が重複しているのでこれを次のように2グループに分けたい。

servived
IMG_2607.jpg,BCF3E765,1944106
IMG_2608.jpg,02B27221,3109397
010(8).jpg,E4A68AB2,3801239
010(9).jpg,3EBBC7BD,1841698
010(10).jpg,B9431E60,103645
delete
IMG_2607(1).jpg,BCF3E765,1944106
IMG_2608(1).jpg,02B27221,3109397

考え方としては単純に削除リスト ← 元のリスト - 重複しているリスト生き残りリスト ← 元のリスト - 削除リストで良いのだが色々こねくり回してみたものの納得のいく実装に辿り着けなかったので今回は

  1. 空の生き残りリストと削除リストを用意する
  2. 検査対象が生き残りリストにあれば削除リストに追加する
  3. 検査対象が生き残りリストになければ生き残りリストに追加する

といったシンプルな実装にする(やりたいことが込み入った内容ではないのでこれで十分かと思っている)

開発環境

  • OS : Windows10Home(1903)
  • Editor:Visual Studio Code : 1.49.1
  • Python : 3.8.3
    • pandas : 1.0.5

コード

Classify.py
import os
import sys
import pandas as pd

def classify(path, target):
    lines = pd.read_csv(os.path.join(path, target), header=None)

    d = {}
    servived_dict = {}      # 生き残るもの
    delete_dict = {}        # 削除対象

    # filename,crc32,filesizeを{filename, (crc32, filesize)}にする
    for i in range(len(lines)):
        (filename, crc32, filesize) = lines.values[i]
        d[filename] = (crc32, filesize)

    for key, value in d.items():
        if value in servived_dict.values():
            delete_dict[key] = value
        else:
            servived_dict[key] = value

    def output(full_path, dic):
        with open(full_path, mode='w') as f:
            for key in dic.keys():
                # 削除対象ファイルのフルパスだけ欲しいので
                f.write(os.path.join(path, key) + "\n")

    output(os.path.join(path, "servived.txt"), servived_dict)
    output(os.path.join(path, "delete.txt"), delete_dict)

if __name__ == "__main__":
    full_path = sys.argv[1]
    classify(os.path.dirname(full_path), os.path.basename(full_path))

最初のfor文にて後で扱いやすいように検査対象を{filename, (crc32, filesize)}に変換している。タプルにしない場合CRC32とファイルサイズそれぞれが含まれているか検査することになるのでそれは少々ダサいかなと。あと、servived_dictは存在意義があるがservived.txtに出力しても使い道がないので出力は不要(とは言えデバッグ時には有用だった)

あとがき

まだまだにわかPythonistaなので多分集合演算でサクっと終わってしまうような気もしているが今回はこれにて。次は各年月フォルダに生成されたdelete.txtを参照して削除処理ですか(何で書こうかな・・)

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2