by @sharkattack51

May 23, 2015

Pythonと連携する方法

Pythonライブラリの有能さを覚えると、外の言語を使っている時にPythonでサクッといきたい場面がある。 もちろん実行時の処理速度に問題がなければという前提で、非同期で処理できればメインとは別のプロセスで処理できるので受けれる恩恵は大きい。

例えば画像の変換の場合はPILなんか使うと相当楽だし、場合によってはOpenCVラッパーが使える。(もちろんネイティブライブラリよりは遅い)
RSSなんかのFeedのパースなんかもUniversalFeedParserが有能すぎるし、BeautifulSoupは便利だ。

例えばUnityやAIRなどの実行環境からPythonに処理を投げたい場合にどうするか。 データの受け渡しはどうするか。

方法

基本的にはイベント処理で実装したいので、Python側をローカルにアプリケーションサーバーとして立てておき、メインアプリ側からhttpリクエストを送る。

できるだけシンプルに実装したいのでWebフレームワークを使ってアプリケーションサーバーを立てる。 flaskBottleあたりが良いと思う。

# python

from flask import Flask

app = Flask(__name__)

@app.route("/", methods=['GET'])
def index():
	return "Hello"

if __name__ == "__main__":
	app.debug = True
	app.run()

これでルート http://localhost:5000/ にアクセスすると結果をとってこれる。 あとはhttpリクエストで非同期処理してメインアプリ側でイベント処理する。

// Unity-C# Python側からテキストを受け取る

IEnumerator Start()
{
	WWW www = new WWW("http://localhost:5000/");

	yield return www;

	Debug.log(www.text) //Hello
}

引数が欲しい場合は、クエリやRESTのAPIを生やして対応できるし、 画像の受け渡しが必要な場合はbyte配列にした上でformでやり取りする。

// Unity-C#

IEnumerator Start()
{
	WWWForm form = new WWWForm();
	form.AddBinaryData("post_data", texture.EncodeToPNG(), texture.name, "image/png");
	WWW www = new WWW("http://localhost:5000/test_api", form);

	yield return www;

	processedTex = www.texture;
}
# python PILを使った画像処理結果をC#側に返す

@app.route("/test_api", methods=["POST"])
def test():
	if request.method == "POST":

		# formデータの受け取り
		image_bytes = request.files["post_data"]

		# IOオブジェクトでbyteデータをImageに読み込む
		image = Image.open(StringIO(image_bytes.read()))
		
		# imageに対して加工処理
		# ...
		
		# 更新をbufferにpngで保存
		buf = StringIO()
		image.save(buf, "png")

		# bufferからレスポンス作成
		response = make_response(buf.getvalue())
		response.headers["Content-type"] = "Image"
		return response

まとめ

アプリケーションの実装はこまごました部分をスクリプト側に委譲していく設計もありだと思う。
俗にいうMicroservicesの一つではないか。

ちなみにUnityであればこういう手もある。
Unity上でPythonを使う①【アセット組み込み編】 - kitao’s blog