Help us understand the problem. What is going on with this article?

Pythonのurllibを使ってImageNetから画像をダウンロードする

概要

PythonでImageNetから画像をダウンロードする方法を解説する記事。

ImageNetの画像をダウンロードする方法は2つある。
一つはImageNet経由で一括ダウンロードする方法と、もう一つはImageNetが管理している画像元のURL一覧取得して、そのURLを使って自分でダウンロードする方法である。

前者は、非営利目的の研究/教育目的のみ利用可能。
なのでそれ以外の人は後者の方法をとらなければならない。
今回は後者の方法をPythonを使ってダウンロードする。

ダウンロードに必要な知識と実際のコードをそれぞれ解説していく。

ImageNetとは

スクリーンショット 2018-08-27 21.39.41.png
ImageNet

  • 研究目的で作成された画像のデータベース
  • 機械学習の学習データによく使用されている。
  • 画像の種類はWordNetで管理 WordNet(Wikipedia)
  • ImageNetが画像の著作権を保持しているわけではない。ImageNetは画像元のURLと画像のサムネイルを提供しているだけ。
  • 画像数:14,197,122
  • バウンディングボックス:1,034,908

WordnetIDとSynsetとは

ImageNetはWordNetという辞書で画像が管理されている。
具体的にはWordNetIDとSynsetという組み合わせで画像の種別を表現している。

例えば、WordNetID 「n02113335」 は、
Synset 「Poodle, poodle dog」 を表現している。

例から分かるように、Synsetは人間が分かる単語で表現され、WordNetIDはその単語と紐付いている番号('n' + 8桁の番号)である。

ImageNetのサイトで画像を探すときはSynsetで検索できる。またSynsetは親(上位概念)と子(下位概念)で構成されている。(「Poodle, poodle dog」の下位概念には「Toy poodle」がいる。)

スクリーンショット 2018-08-27 21.54.36.png
左のツリーがSynsetの階層構造。
WordNetIDは、右上の黄色いマークを押すと取得できる。

人間に分かりやすいSynsetだけ分かればいいのでは?と思うが、プログラムで画像をダウンロードする際にはWordNetIDでダウンロードしたい種別を選択する必要がある。

ダウンロード方法

プログラムでダウンロードするためにはImageNetが提供しているAPIを使う。APIとはあるURLのことで、このURLにWordNetIDをくっつけてアクセスすると、そのWordNetIDに関する情報が取得できる。
APIにはいろいろな種類がある。

ImageNet API

No. PageName URL
1 Synset検索画面のページ http://www.image-net.org/synset?wnid=***
2 指定したwnidの下位概念のwnid表示 http://www.image-net.org/api/text/wordnet.structure.hyponym?wnid=***
3 指定したwnidの下位概念のwnid表示(最下層まで探索) http://www.image-net.org/api/text/wordnet.structure.hyponym?wnid=***&full=1
4 指定したwnidに対応するSynset表示 http://www.image-net.org/api/text/wordnet.synset.getwords?wnid=***
5 指定したwnidに対応するファイル名と画像のURL表示 http://www.image-net.org/api/text/imagenet.synset.geturls.getmapping?wnid=***
6 指定したwnidに対応する画像のURL表示 http://www.image-net.org/api/text/imagenet.synset.geturls?wnid=***
  • 米印にはWordNetIDが入ります。
  • wnid = WordNetIDです。

順番に見ていくと、
1は「WordnetIDとSynsetとは」で見た検索画面にジャンプするURL。
2と3は、指定したWordNetIDの下位概念を表示する。2は直下のものしか表示しないが、3は最下層まで表示する。


スクリーンショット 2018-08-27 23.10.30.png

4は、指定したWordNetIDに対応するSynsetを表示するURL。


スクリーンショット 2018-08-27 23.11.08.png

5,6は、指定したWordNetIDに属する画像のURL一覧が表示される。5はファイル名も合わせて表示される。


スクリーンショット 2018-08-27 23.11.43.png

n20113335_17679というのがファイル名になっている。
バウンディングボックスなどはファイル名と紐付いているので、5番のAPIでアクセスするのがいいと思う。

ということで画像ダウンロードに使うのは5番です。
方法としては、
1.Pythonのurllibを使って5番のページに情報取得の要求を出す。
2.ファイル名とURLの一覧が手に入る。
3.取得したURLに対してさらに情報取得の要求を出す。
4.URL先の画像データが手に入る。
5.画像データをファイルに書く。
以下3-5を繰り返す。

コード

以下サンプルコード

1.ファイル名と画像元URLの取得

from urllib import request
IMG_LIST_URL="http://www.image-net.org/api/text/imagenet.synset.geturls.getmapping?wnid={}"

url = IMG_LIST_URL.format("n02113335")
with request.urlopen(url) as response:
    html = response.read() 

URLをurlopen()で開いて、read()するだけでページの情報が取得できます。この時点でhtmlには、バイナリ型で以下のような文字列が入っている。

n02113335_17679 http://farm1.static.flickr.com/194/467227983_ce131cca2a.jpg
n02113335_4957 http://static.flickr.com/164/388222083_d98ab2ec7e.jpg
n02113335_4907 http://www.dkimages.com/discover/previews/919/65004609.JPG
n02113335_4943 http://farm1.static.flickr.com/82/225053708_e1b941261a.jpg
n02113335_4942 http://farm1.static.flickr.com/17/19821754_6cb866105a.jpg
n02113335_4935 http://farm3.static.flickr.com/2397/2132261952_28dd898274.jpg
・
・
・

文字列として扱いたいのでdecodeする。(バイナリ型->文字列型)

data = html.decode()

あとはこれを1列目と2列目に分割すればファイル名と画像元のURLが取得できる。
例えば以下のようにする。

data = data.split()
# data = [fname_0, url_0, fname_1, url_1, .....]
fnames = data[::2]
urls = data[1::2]

2.画像の取得

先ほど取得した1枚目のURLを使って、1と同様にアクセスする。

url="http://farm1.static.flickr.com/194/467227983_ce131cca2a.jpg"
with request.urlopen(url) as response:
    img = response.read() 

このときimgにバイナリ形式の画像データが入っている。
画像なので、以下のようにそのままバイナリ形式でファイルに書けばダウンロード完了。

with open('n02113335_17679.jpg', 'wb') as f:
    f.write(img)

ImageNet_Downloader

ここまで紹介したImageNetAPIのラッパークラスを書いてみた。中身の処理は先ほど説明したことがメインなので割愛。以下、クラスの簡単な使い方。

Python-ImageNet_Downloader

import downloader
import os
root_dir = os.getcwd()
wnid = "n02113335"
api = downloader.ImageNet(root_dir)
api.download(wnid, verbose=True)

downloadでwnidを設定すると、そのwnidの画像をダウンロードし始める。以下のフォルダを作成して順次ダウンロードしていく。n02113335.txtは、ファイル名と画像のURLが書いてあるテキストファイル。

root/  
 ├ img/  
 │ └ n02113335/
 │       └ n02113335_xxx.jpg
 │       └ n02113335_xxx.jpg
 │       └ ....
 ├ list/  
 │ └ n02113335.txt

example.pyを使うと簡単にダウンロードできる。

python example.py <WordnetID> -v

下位層も含めてダウンロードしたい場合は-rをつける。

python example.py <WordnetID> -v -r

ダウンロードする枚数を指定した場合は、-limit <num>を指定する。

python example.py <WordnetID> -v -r -limit 100

まとめ

Pythonのurllibを使うと簡単にネット上の画像をダウンロードできる。

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away