urllib, pathlibによる入力ファイルの処理

目的

機械学習やビッグデータの解析を行う際には,大量のデータを処理することがある.ここでは,大量のデータがWebサイト等に公開されているときに,ネットワークを介してローカルにダウンロードする方法と,ダウンロードした大量のデータを入力する方法を学ぶ.

説明

urllibパッケージ

Webサイト等に公開されているファイルをダウンロードする際にurllibパッケージを使用する.

テキストファイルのダウンロード

ここでは,著作権が切れた著作を集めたサイト「Project Gutenberg」からアーサー・コナン・ドイルの著作「The Adventures of Sherlock Holmes」をダウンロードしてみよう.

5行目でダウンロードするテキストファイルのURLを指定し,6行目でローカルに保存するテキストファイル名を指定している.1行目でurllibパッケージのrequestモジュールをインポートし,9行目でurlretrieve関数を使用してテキストファイルをダウンロードしている.

実行すると,「the_adventures_of_sherlock_holmes.txt」という名前のファイルができ,小説の内容が保存されているはずである.

2度目以降はダウンロードしないようにする

このプログラムは実行する度にデータをダウンロードしてしまう.特にファイルサイズが大きいデータの場合には,繰り返しダウンロードを行うのは無駄である.そこで,ファイル名によりローカルにそのファイルが存在するかどうかを確認し,そのファイルが存在しない場合にのみダウンロードするように書き換えてみよう.

1行目でosパッケージをインポートし,9行目でpathモジュールのexists関数を使用して,ローカルにファイルが存在するか確認している.ファイルが存在しないときにだけダウンロードするようにしている.

画像データセット(LFW)のダウンロード

画像認識を目的とした機械学習の研究では,大量の画像データを使用する.学習に使用する画像データセットは様々な形式で公開されていることが多い.ここでは,2種類の画像データセットをダウンロードしてみよう.

1つ目は,人の顔画像からなる「Labeled Faces in the Wild Home (LFW)」データセットである.このデータセットは,jpegファイルの集合で,tarというフォーマットでアーカイブされて(1つのファイルにまとめられて)おり,gzipという形式で圧縮されている.tarでアーカイブされ,gzipで圧縮されたファイルの拡張子は「tar.gz」か「tgz」である.LFWデータセットをダウンロードし,展開するPythonコードは以下のようになる.

6行目でダウンロードする画像データセットのURLを,7行目でローカルに保存するファイル名を指定している.9行目では,圧縮されたファイルを展開するときの展開先のディレクトリ名を指定している.11行目でファイルがあるかどうかを確認し,13行目でダウンロードしている.ダウンロードした「tgz」形式のファイルを展開するには,tarfileモジュールを使用する.3行目でモジュールをインポートし,17行目でopen関数を使用してtgzファイルをオープンしている.第2引数はモード「r:gz」でオープンすることを表しており,読み込みモードでgzip形式で圧縮されたファイルをオープンしている.18行目でextractallメソッドを使用して,指定したディレクトリにファイルを展開している.

実行すると,「images」ディレクトリが作成され,その中の「lfw」ディレクトリ中に様々な人の顔画像がjpeg形式で保存されるはずである.

画像データセット(COIL 20)のダウンロード

2種類目のデータセットは,「Columbia University Image Library (COIL-20)」である.このデータセットは,png形式の画像ファイルの集合で,zipという形式で圧縮されている.COIL-20データセットをダウンロードし,展開するPythonコードは以下のようになる.

LFWデータセットとの違いは,圧縮の形式がzip形式となっていることである.zip形式のファイルを展開するには,zipfileモジュールを使用する.3行目でモジュールをインポートし,17行目でZipFile関数を使用してzipファイルをオープンしている.18行目でextractallメソッドを使用して,指定したディレクトリにファイルを展開している.

実行すると,「images」ディレクトリ中に「coil-20-unproc」というディレクトリが作成され,その中に様々なオブジェクトの画像がpng形式で保存されているはずである.

pathlibモジュール

2種類の画像データセットをローカルにダウンロードすることができた.それぞれのデータセットには数多くの画像が含まれている.ここでは,pathlibモジュールを使用して,数多くのデータを読み込む方法を確認しよう.

一番上の階層のファイル・ディレクトリ一覧の取得

今,「images」という名前のディレクトリに画像データが保存されているとすると,imagesディレクトリに対してpathlibオブジェクトを生成し,pathlibオブジェクトのglobメソッドを使用すると,imagesディレクトリ中の様々なファイルやディレクトリにアクセスすることができる.

6行目で,imagesディレクトリを指定してpathlibのオブジェクトを生成し,images_directory_pathという名前をつけている.7行目でglobメソッドを使用し,imagesディレクトリ中のファイル・ディレクトリ一覧を取得している.globメソッドの引数「*」は,「一番上の階層にある全てのファイル・ディレクトリ」を表している.globメソッドはiteratorと呼ばれるオブジェクトを返すため,ここではlist関数を使用して,リストに変換している.リストに変換したファイル・ディレクトリ一覧にsub_filesという名前をつけている.8行目でファイル・ディレクトリ一覧を表示し,9行目でファイル・ディレクトリの数を表示している.

実行すると,以下のように,imagesディレクトリの一番上の階層にあるファイル・ディレクトリ名と,その数が表示される.

6,7行目の処理を1行にまとめて記述すると,以下のようになる.

2番目の階層のファイル・ディレクトリ一覧の取得

では,上から2番目の階層のファイル・ディレクトリ一覧を取得してみよう.

6行目のglobメソッドの引数が「*/*」となっている.「/」はディレクトリの区切りを表しており,一番上の階層の全て「*」の下「/」の全て「*」のファイル・ディレクトリ一覧を取得している.そのため,2番目の階層にあるファイル・ディレクトリ一覧が取得できる.

実行すると,以下のように,imagesディレクトリの上から2番目の階層にあるファイル・ディレクトリ名と,その数が表示される.

3番目の階層のファイル・ディレクトリ一覧の取得

同様に,上から3番目の階層のファイル・ディレクトリ一覧を取得するには,以下のように記述すればよい.

実行すると,以下のように,imagesディレクトリの上から3番目の階層にあるファイル・ディレクトリ名と,その数が表示される.

全ての階層にあるファイル・ディレクトリ一覧の取得

上のようにglobメソッドの引数を指定すると,ある階層にあるファイル・ディレクトリ一覧を取得することができた.次に,imagesディレクトリ以下の全ての階層にあるファイル・ディレクトリ一覧を取得する方法を確認しよう.

6行目でglobメソッドの引数に「**/*」と指定している.「**」は「imagesディレクトリ以下を再帰的に探索する」ことを表しており,その下「/」の全て「*」のファイル・ディレクトリを取得している.そのため,imagesディレクトリ以下全ての階層にあるファイル・ディレクトリ一覧が取得できる.

実行すると,以下のように,imagesディレクトリ以下にある全てのファイル・ディレクトリ名と,その数が表示される.

全ての階層にあるpng形式の画像ファイル名一覧の取得

では,imagesディレクトリ以下にある全てのファイルの中からpng形式のファイルだけを取り出してみよう.

6行目のように,globメソッドの引数に「**/*.png」と指定すると,拡張子が「.png」であるファイル名一覧を取得することができる.

実行すると,以下のように,COIL 20データセットのpng形式の画像ファイル名一覧と,その数が表示される.

全ての階層にあるjpeg形式の画像ファイル名一覧の取得

同様に,imagesディレクトリ以下にある全てのファイルの中からjpeg形式のファイルだけを取り出すには,以下のように記述すればよい.

6行目のglobメソッドの引数を「**/*.jpg」としている.

実行すると,以下のように,LFWデータセットのjpeg形式の画像ファイル名一覧と,その数が表示される.

ファイル名を辞書順にソート

以上のようにすれば,例えばLFWデータセットのjpeg形式の画像ファイル名一覧を取得することができた.結果を見るとわかる通り,この方法で取得したファイル名一覧は辞書順にソートされていない.globメソッドで取得したファイル名一覧には,リストをソートする関数sortedを使用することが多い.

実行すると,以下のように,ファイル名が辞書順にソートされていることがわかる.

データセットの画像を順に表示

では,LFWデータセットの画像を順にウィンドウに表示してみよう.

8行目で取得した画像ファイル名分だけ繰り返し,9行目で画像ファイルを読み込んでいる.pathlibモジュールで取得したファイル名一覧は一般的な文字列データではないため,as_posixメソッドを使用して,文字列データに変換している.11行目でウィンドウに読み込んだ画像を表示している.13,14行目で「q」キーを押すと,プログラムが終了するようにしている.

実行すると,LFWデータセットの画像が表示され,何かキーを押すと次の画像が表示され,「q」キーを押すとプログラムが終了するはずである.

ファイルが存在するかどうかの確認

以上のように,pathlibモジュールは指定したディレクトリ以下にあるファイル・ディレクトリ一覧を取得する際に使用されるが,pathlibモジュールにはその他にも様々な機能がある.ここでは,そのいくつかを確認しよう.

ファイルやディレクトリが存在するかどうかはosパッケージのpathモジュールのexists関数によって確認できた.pathlibモジュールを使っても同様のことができる.LFWデータセットをダウンロードするプログラムを書き換えると,以下のようになる.

11行目でpathlibオブジェクトのexistsメソッドを使用して,ファイルの存在を確認している.

出力ディレクトリの作成とパスの追加

例えば画像処理をして結果をファイルに保存することを考えよう.1枚の画像を読み込み,1枚の画像を書き出す場合には,入力ファイル名と出力ファイル名を指定すればよいだろう.1枚の画像を読み込み,複数枚の画像を書き出す場合には,出力ファイル名ではなく,出力ディレクトリ名を指定して,その中にわかりやすい名前で複数のファイルを出力するほうがよいであろう.

例えば,カラー画像を1枚入力し,解像度を変更した画像と解像度を変更した後にグレースケールに変換した画像をディレクトリに出力するプログラムは以下のようになる.

7行目から12行目で,argparseモジュールを使用し,オプションで,入力画像ファイル名,出力ディレクトリ名,解像度を指定できるようにしている.14行目から16行目で,画像を読み込み,解像度を変更し,グレースケール画像に変換している.

18行目で出力ディレクトリのpathlibオブジェクトを生成している.19行目で,mkdirメソッドを使用し,出力ディレクトリを作成している.21,22行目で,joinpathメソッドを使用し,出力ディレクトリの下に「resize_image.png」というファイル名をもつパスと,「gray_resize_image.png」というファイル名をもつパスを作成し,それぞれに名前をつけている.24,25行目で,そのパスのas_posixメソッドを使用して,出力ファイル名を指定し,処理結果である2つの画像データを書き出している.

オプション引数を指定せずに実行すると,「out」というディレクトリができ,その中に「resize_image.png」というファイルと「gray_resize_image.png」というファイルができるはずである.

出力ディレクトリの作成(キーワード引数exist_ok)

上記のプログラムをオプション引数を指定せずに再度実行すると,以下のようなエラーメッセージが表示される.

このエラーは,19行目でmkdirメソッドを使用して「out」という名前のディレクトリを作る際に,既にそのディレクトリが存在するために出力される.既に存在していてもディレクトリを作るようにするには,以下のように書き換えればよい.

19行目のように,mkdirメソッドのキーワード引数exist_okにTrueを指定すると,ディレクトリが既に存在していてもディレクトリを作成することができ,エラーが表示されなくなる.

出力ディレクトリの作成(キーワード引数parents=True)

オプション引数を指定せずに上記のプログラムを実行すると,「out」という名前のディレクトリに処理結果の画像ファイルが出力された.オプション引数を指定して,出力するディレクトリを変更してみよう.ここではoutputという名前のディレクトリの下のresult0という名前のディレクトリの下に処理結果の画像を出力することにし,出力ディレクトリに「output/result0」と指定して実行してみよう.すると,以下のようなエラーが表示されるはずである.

19行目のmkdirメソッドは1つのディレクトリを作成することはできるが,outputディレクトリを作り,その下にresult0を作ることができないために,このようなエラーが出力される.階層構造を持つディレクトリを作成できるようにするには,以下のように書き換えれよい.

19行目のように,mkdirメソッドのキーワード引数parentsにTrueを指定すると,階層構造を持つディレクトリの親ディレクトリを含めて作成することができ,エラーが表示されなくなる.

課題

課題0

「Project Gutenberg」から「The Adventures of Sherlock Holmes」をダウンロードせよ.

課題1

「Project Gutenberg」から自分の好きな著作ををダウンロードして読め.

課題2

COIL-20データセットをダウンロードし,1枚ずつウィンドウに表示せよ.

課題3

COIL-20データセットをダウンロードし,1枚ずつ解像度を変更し,元の画像と解像度を変更した画像をウィンドウに表示せよ.

課題4

COIL-20データセットをダウンロードし,解像度を変更した画像を「000.png」「001.png」…のような連番のファイル名で,指定したディレクトリに書き出せ.

課題5

LFWデータセットをダウンロードし,1枚ずつウィンドウに表示せよ.

課題6

LFWデータセットをダウンロードし,1枚ずつ解像度を変更し,元の画像と解像度を変更した画像をウィンドウに表示せよ.

課題7

LFWデータセットをダウンロードし,1枚ずつグレースケール画像に変換し,元の画像とグレースケール画像をウィンドウに表示せよ.

課題8

LFWデータセットをダウンロードし,解像度を変更した画像を「00000.jpg」「00001.jpg」…のような連番のファイル名で,指定したディレクトリに書き出せ.

課題9

LFWデータセットをダウンロードし,グレースケール画像に変換し,「00000.jpg」「00001.jpg」…のような連番のファイル名で,指定したディレクトリに書き出せ.