AWS Lambdaによる流体画像処理の並列化

記事タイトルとURLをコピーする

Click here to read in English

2015年4月入社の白鳥です。

私は、サーバーワークスに入社する前は、流体力学という分野の研究をしていました。流体力学は、水などの液体や空気などの気体(まとめて流体という)が、どのように流れるかを研究する分野です。この分野で使われる計測法の一つに、粒子画像流速測定法(Particle Imaging Velocimetry, PIV)があります。カメラで流れの画像を撮影し、画像処理をすることで流れの様子を計測する方法です。PIVは、他の流れの計測法と比較して、広範囲の流れを計測できるというメリットがあり、流体力学の分野では広く使われています。一方で、画像処理に時間がかかるという短所があります。特に、長時間の撮影時など、処理する画像の枚数が多くなると、この問題が顕著になります。この記事では、AWS Lambdaを使って画像処理を並列化し、このPIVの短所の克服を試みます。

基本事項

PIVとは

PIVは、流体の流れを撮影し、流れを数値化する技術です。PIVの中にも、画像処理の仕方によりいろいろな種類がありますが、この記事では最も基本的な画像処理法である直接相互相関法を採用します。画像から流れを数値化するには、画像中のどのあたりの流体がどのあたりへ移動したのかを判断する必要があります。直接相互相関法では、この判断材料に画像の相関値を使います。詳しい説明は専門書に譲るとして、ここではこの計測法の入力と出力が何であるかをまとめておきます。

入力: 画像2枚, 入力パラメータ(格子間隔, 候補領域サイズ, 探査領域サイズ を数値で入力)

出力: 流れの速度分布データ(構造化されたデータ) 今回の構成では、画像はBMPファイル、入力パラメータと流れの速度分布データはCSVファイルに記録します。

AWS Lambdaとは

AWS Lambdaは、AWSが提供するコード実行環境です。あらかじめ、コードをLambda関数として保存し、トリガーとなる条件を設定しておきます。すると、トリガーとなる条件が満たされたとき、コードが実行されます。通常は、AWSのサービス同士をつないだり(例1, 例2)、AWSと外部のAPIを連携させる(例1, 例2)のに使われますが、今回は画像処理をするコードをLambda関数として設定し、そこそこがっつり計算させてみます。

なぜPIV+Lambdaか

PIVの直接相互相関法は、画像の相関値を繰り返し計算するため計算量が多く、処理に時間がかかります。処理時間を短くする方法としては、複数CPUや複数サーバーによる分散処理が考えられます。一般に流体の画像処理は、PCのローカル環境またはオンプレミスのサーバーで実行されます。よって処理の並列化のためには、サーバーへの初期投資や分散プログラミングの知識が必要となります。今回紹介する構成では、クラウド上で処理を行うため初期投資を抑えて実行環境を構築できます。さらにLambdaを使うことで、EC2やSQSを使って分散処理を行うよりも構築が容易になります。

構成の詳細

単発版

並列化した構成を紹介する前に、まずは2枚の画像から1枚の速度分布データを生成する手順を図1に示します。

architecture01

図1 画像処理単発版の構成 この構成では、S3にファイルがアップロードされたことをトリガーにして、Lambda関数を実行します。直接相互相関法のコードからLambda関数 "DccmOnLambda" (DCCMは直接相互相関法, Direct Cross-Correlation Methodの略)を作成し、S3へCSVファイルがアップロードされた際に発動するようイベントソースを設定しておきます。画像処理は次の手順で行います。

  • (1-1)2枚の画像ファイルを保存
    このときにLambda関数が発動しないよう、イベントソースのprefixやsuffixを設定
  • (1-2)入力パラメータの値を記入したCSVをS3に保存
  • (1-3)トリガーが引かれてDccmOnLambdaが発動
    DccmOnLambdaは、2枚の画像と1つのCSVを読み込み、計算を行う
  • (1-4)計算結果である速度分布データを書き出したCSVファイルを生成
  • (1-5)CSVをS3に保存
    このとき、再度DccmOnLambdaを再度発動させないよう、別フォルダに保存

なお、この構成を実装する上で、AWS公式のこちらのチュートリアルが参考になったので、紹介しておきます。

並列版

以上の構成を、並列処理ができるように拡張します。並列処理時の手順を図2に示します。

architecture02

図2 画像処理並列処理時の構成 1つのCSVファイルを読み込んで複数のCSVファイルを作成するLambda関数 "csvdivide" を作成します。csvdivideが読み込むファイルを親CSV, 書き出すファイルを子CSVと呼ぶことにします。並列画像処理は以下の手順で行います。

  • (2-1)処理したい画像をすべてS3に保存
  • (2-2)親CSVをS3に保存
  • (2-3)親CSVが保存されたことをトリガーにcsvdivideが発動
  • (2-4)複数の子CSVが生成される
  • (2-5)子CSVがS3にアップロードされる
  • (2-6)子CSVがそれぞれDccmOnLambdaを発動させる
  • (2-7)それぞれのDccmOnLambdaが結果をS3に保存

動かしてみる

では、以上に示した構成を実装して動かしてみます。処理する画像として、東京大学の岡本孝司先生より提供されている標準粒子画像のNo.01 (2D Wall shear flow/Reference) を使います。4枚の連続する時刻の画像が提供されていますが、これを piv01_000001.bmp ~ piv01_000004.bmp と名付けます。GIFにすると以下のようになります。

piv01

図3 今回の画像処理に使う標準粒子画像

単発版

まずは画像を2枚のみ処理させ、DccmOnLambdaが動作する様子を見てみます。今回の画像処理の条件を下表に示します。

画像サイズ 256 pixel × 256 pixel
格子間隔 Grid Size 36 pixel
候補領域サイズ Interrogation Area 33 pixel × 33 pixel
探査領域サイズ Search Area 17 pixel × 17 pixel

図4のように、入力パラメータをCSVファイルに書いて保存します。候補領域サイズと探査領域サイズは奇数であった方がコードが書きやすいため、CSVに書かれた値を2倍して1を足すようコードを書いています。

lambdapiv_scshot_001

図4 入力CSVの書式 このCSVをS3に保存すると、DccmOnLambdaが発動します。Lambda関数実行中のログは、CloudWatchより見ることができます。図5のログより、Lambda関数が正常に起動し、現在画像処理中であることが分かります。

lambdapiv_scshot_002
図5 画像処理を単発実行時のLambdaのログ(計算完了前) 2分ほどで計算が終わりました。計算終了後のログは図6の通りです。今回の実行では、112100ms分のコンピューティング料金がかかると記されています。メモリは、設定した1536MB中28MBしか使わなかったようです。一見メモリを減らしても良さそうに見えますが、そうするとLambdaではCPUの計算能力も比例して減らされるため、計算時間が延びてしまいます。実際に、例えばメモリを半分にすると、計算時間が約2倍になる様子を確認できました。 lambdapiv_scshot_003

図6 画像処理を単発実行時のLambdaのログ(計算完了後) 書き出されたCSVをフリーのグラフソフトGraphRで開くと、図7のように流れの速度分布を表示できます。先ほど示した標準粒子画像のGIFと比べると、だいたい流れを表現できていることが分かります。誤ベクトル(明らかにおかしな方向を向いているベクトル)がいくつかありますが、これはPIVの原理上、一定数出てしまうものなので、まずまずの結果と言えるでしょう。入力パラメータである格子間隔を狭くすると、より多くのベクトルを使って細かく流れを表現できます。しかし計算に要する時間が長くなるので、AWS Lambdaの最長実行可能時間である5分を超えてしまう恐れが出てきます。 vector000001

図7 画像処理により得られた流れの分布

並列版

では、いよいよ並列処理をしてみます。図8のような親CSVをS3に保存します。

lambdapiv_scshot_004
図8 親CSVの書式 csvdivideが起動し、親CSVに書かれた情報をもとに子CSVを生成します。今回は画像が4枚あるため、1枚目と2枚目、2枚目と3枚目、3枚目と4枚目を入力とする計3つの処理を並行して行います。よってcsvdivideは3つの子CSVをS3に保存します。図9のcsvdivideのログにその様子が記されています。

lambdapiv_scshot_005
図9 CSVを分割するLambda関数の実行後のログ 子CSVが3つ保存されたので、それぞれがトリガーとなりDccmOnLambdaを起動します。CloudWatchのLog Groupsを見ると、3つの処理が同時に起動したことが分かります。

lambdapiv_scshot_006
図10 3つの画像処理が同時に始まったことを示すログ 画像処理終了後のそれぞれのログを以下に示します。

lambdapiv_scshot_007
図11 並列画像処理実行中のログA lambdapiv_scshot_008

図12 並列画像処理実行中のログB lambdapiv_scshot_009

図13 並列画像処理実行中のログC 画像処理により得られた3枚の速度分布をGIFにすると図14のようになります。通常、流れの速度分布の時間変化を得るのには時間と労力を要しますが、今回の検証では、ファイルをアップロードするだけで並列処理できるシステムを構築できたことになります。今回題材にした標準粒子画像では、流れがあまり変化していないですが、刻々と変化する流れを撮影した場合には、何枚もの流れの分布を短時間に得ることが重要になってきます。 vector

図14 並列画像処理により得られた流れの速度分布(クリックすると動く様子が見れます)

まとめ

AWS Lambdaを使って、流体画像処理を並列して行うシステムを構築しました。構築は比較的簡単で、AWSを使い始めて1年足らずの私でも構築できました。ただし、細かい流れの分布を計算したり、大きな画像を扱う場合には、Lambdaの最長タイムアウトである5分を超える可能性があるため、注意が必要です。