とある人類

書きたいことがあるときに書き残すログ

ProcessingからPythonを実行したかった話

先日、とある機械学習をするのに画像データがある程度必要になったので、何か適当に画像を抜き取ってくる方法はないものかと探していました。

まぁ勝手にGo○gleさんの画像検索をスクレイピングして抜き出せばいいかなとか思っていたのだけれど、どうも規約違反らしいとのこと。 かと言って、他の画像検索サイトもおそらくグレーゾーンだろうと思ったので、それ用のAPI系を調べて見たけどどれも微妙……。

とりあえず今回はそれほど画像枚数はいらないのと、クエリに対して画像が返ってくるものがよかったので、G○ogleのcustom search apiを無料版で使ってみることに。

#! /usr/bin/python
# -*- coding:utf-8 -*-

import urllib
import json
import re
import requests
import os
import sys

def get_imageurl():
    query = sys.argv[1]
    print(query)
    id = ID
    api_key = API_KEY
    page_to = 6

    from_url = 'https://www.googleapis.com/customsearch/v1?'
    params = {
        'key': api_key,
        'q':query,
        'cx':id,
        'searchType':'image',
        'alt':'json',
        'lr' :'lang_ja',
    }

    start = 1
    f = open('imageURL.txt','w')

    for i in range(0, page_to):
        params['start'] = start
        req_url = from_url + urllib.urlencode(params)
        res = urllib.urlopen(req_url)
        dump = json.loads(res.read())
        for j in range(len(dump["items"])):
            f.write(dump["items"][j]["link"] + "\n")
        if not dump['queries'].has_key('nextPage'):
            break
        start = int(dump['queries']['nextPage'][0]['startIndex'])

    f.close()

def download_image(url, timeout = 10):
    response = requests.get(url, allow_redirects=False, timeout=timeout)
    if response.status_code != 200:
        e = Exception("HTTP status: " + response.status_code)
        raise e
    
    content_type = response.headers["content-type"]
    if 'image' not in content_type:
        e = Exception("Content-Type: " + content_type)
        raise e
    
    return response.content

def make_filename(base_dir, number, url):
    ext = ".png"
    filename = "%s%s"%(number, ext)
    
    fullpath = os.path.join(base_dir, filename)
    return fullpath

def save_image(filename, image):
    with open(filename, "wb") as fout:
        fout.write(image)

if __name__ == "__main__":
    get_imageurl()
    urls_txt = "imageURL.txt"
    query_dir = sys.argv[1]
    images_dir = "colorImageData/%s"%query_dir
    idx = 0
    abs_path = os.path.dirname(os.path.abspath(__file__))
    dir_path = "%s/%s"%(abs_path, images_dir)
    os.mkdir(dir_path)
    
    with open(urls_txt, "r") as fin:
        for line in fin:
            url = line.strip()
            filename = make_filename(dir_path, idx, url)
            
            print "%s" % (url)
            try:
                image = download_image(url)
                save_image(filename, image)
                idx += 1
            except KeyboardInterrupt:
                break
            except Exception as err:
                print "%s" % (err)

日頃Pythonを書かないので、見よう見まねで色々書いてみて、なんとか画像を取得できるようになった。

使い方はターミナルとかで、

python getImageData.py [query]

[query]の部分に検索したい画像のクエリを入れて実行する感じ。

取得するページ数を指定する変数がこれ。

page_to = [page]

今回は1クエリあたり50枚の画像を使いたい。 実行すると1ページあたり10枚の画像を取得してるっぽいけど、取得できない画像もあるので、多めに回しておいて、必要数を満たす戦法。

全体の動きとしては

1. APIを使って、[query]で画像検索をした結果をJSON形式で取得

2. JSONから画像のURL部分を抜き取って、imageURL.txtに書き込み

3. 書き込んだURLから画像を取得して、colorImageDataフォルダに保存

という感じ。

なんとも回りくどい感じになりましたが、まあ仕方ない。日頃Python書いてない人だから(言い訳) それに、ここまでは下準備ですから……。

では、このPythonのコードをProcessingで実行していきます。

ちなみに、"Processing Python" とかで検索すると、ProcessingのPython Modeについての記事がたくさん出てきて、そうじゃない……といった感じに。 また、Processingでは外部アプリケーションを実行できるlaunch()という命令があるようですが、これだと[query]を入力できないので、今回は使えそうにない……。

そこで今回は、ProcessBuilderというのを使っていきます。

import java.io.IOException;

void pythonExec() {
  try {
    ProcessBuilder builder = new ProcessBuilder("python", sketchPath()+"/python/getImageData.py", [query]);
    Process process = builder.start();
    
    int exitCode = process.waitFor();
    if (exitCode==0) {
      process.destroy();
    }
  } 
  catch (IOException e) {
    e.printStackTrace();
  }
  catch(InterruptedException ex) {
    ex.printStackTrace();
  }
}

はい、見ての通りjavaです。

このようにProcessBuilderを使うと、ターミナルでの入力形式に乗っ取って、Pythonを実行することができるようになり、[query]をパラメータとして設定できます。 あとはそのプロセスを実行して、waitFor()で終了判定を行い、プロセスを閉じるだけ。

この感じだと、他のターミナル上のコマンドも実行できそうで、いろいろ活用できそうですね!