2016. 3.26.
石立 喬

OpenMPとVisual C++による並列処理(5)

――― コンボリューションを用いた画像処理で、OpenMPの効果を確認する -----

 コンボリューションによる、より時間のかかる画像処理を取り上げ、OpenMPの効果を調べる。画像サイズを640 x 480と、やや大きめにして、処理の所要時間を増やし、同じ測定を100回繰り返して、Visual C++のデフォルトとOpenMPの付加とで、実行時間を比較した。
 「Visual Studio 2012 Expressの易しい使い方(7)」のプログラムにOpenMPを付加したもので、画像処理の詳細については、元の資料を参照されたい。

コンボリューション(convolution、畳み込み演算、積和演算)
 原画像の周辺部を除いて、X方向、Y方向にピクセルをスキャンし、注目するピクセルと、その周囲の近傍ピクセルに対して、カーネルと称するマトリックス値と積和演算を行い、結果を処理後のピクセル値とするものであり、カーネルの種類によって、平滑化、鮮鋭化、エッジ検出、ノイズ除去などが可能である。
 カーネルとして3x3のマトリックスを考えると、原画像の1個のピクセル当たり、9回の乗算と9回の加算が必要で、他のピクセルとは独立して実行できるので並列化に向いている。
 コンボリューションの詳細や、使用するカーネルについては、「Visual C++ 2010 Expressを用いた易しい画像処理(5)」、「Visual Studio 2012 Expressの易しい使い方(7)」に詳述してある。

調査方法
 処理内容 ---- 平滑化(ボカす)、鮮鋭化(シャープにする)、エッジ検出(線画のようにする)
 ビルド構成 --- DebugとRelease
の組み合わせに対して、平滑化と鮮鋭化には単純なコンボリューション関数makeConvolutionを使用し、エッジ検出にはコンボリューション後に非線形な処理を行うdetectEdges関数を使用して100回繰り返し、その都度の実行時間を測定し表示する。これを、Visual C++のデフォルトと、OpenMPを使用した場合とについて調べる。

作成したプログラム
 基本的には「・・・による並列処理(4)」で用いたプログラムとほぼ同じで、異なる点は1)実行速度測定の対象であるコンボリューション処理をユーザー関数として独立させたこと、 2)主要部分であるUserProgram関数内にコンボリューション用のカーネルを用意したことである。
 ユーザ関数として下記がある。
 makeConvolutiuon ---- カーネルを外部から与える、コンボリューション実行の汎用関数
 detectEdges --------- 2種の専用カーネルを内蔵し、コンボリューション実行後に、合成と非線形処理をするエッジ検出専用関数
図1は、プロジェクトの最初の部分で、必要なヘッダーファイルのインクルード、グローバル変数の宣言、関数プロトタイプ宣言がある。 


図1 プロジェクトの最初の部分


UserProgramの内容
 「OpenMPとVisual C++による並列処理(4)」で用いたものとほぼ同じであるが、カーネルの宣言部を追加し(図2参照)、被測定処理の部分に、makeConvolution関数またはdetectEdges関数を記述する(図3参照)。


図2 UserProgramの最初の部分、カーネルの宣言が追加されている



図3 UserProgramから、作成したユーザ関数を呼び出して実行時間を測定する


makeConvolution関数
 「Visual Studio 2012 Expressの易しい使い方(7)」の図10にあるものを、Win32独特の表記法を避けて記述したのが図4のプログラムでああり、内容は下記の通りである。
 1)3x3のカーネルを使用すると、画像周辺部の処理ができないので、周辺部は、原画像をコピーする。
 2)引数の原画像データ配列pdataを、周辺部を除いてxy方向にスキャンし、引数のカーネルkernelとでコンボリューションを行う
    ・原画像のピクセルデータ配列pdataをCOLORREF型のcolorに入れる
    ・colorをRGBに分解し、各々にkernelの係数を乗じ、r、g、bに足し込む
    ・得られたr、g、b値は、float型であるので、INT型にキャストし、0〜255の範囲外の恐れがあるので、補正してrr、gg、bbとする
    ・rr、gg、bbから処理後の画像ピクセルデータ配列pdata1を作成する



図4 makaConvolution関数の内部で並列化する


makeConvolution関数で平滑化処理をした結果
 図5は結果として得られた画面の一例を示す。処理後の平滑化(ボカす)画像を背景に(確認のためで、OKなら表示させないことができる)、左に文字での表示、右に棒グラフでの表示がある。第1回目の測定は大きい値が出ているが、次第に減少する傾向が見られる。


図5 平滑化された画像(背景)と、Debug構成のOpenMP使用で得られた結果の一例


平滑化処理の実行時間の測定結果
 Debug構成では、VC++デフォルトの平均6400μsecに対して、OpenMPでは平均2800μsecと約44%に短縮された。
 Release構成では、VC++デフォルトが平均2500μsec、OpenMPが平均1000μsecと約40%になった。
 これらの測定結果の例を、図6と図7にグラフで表示する。VC++デフォルトは、測定回数と共に実行時間が短縮しないのが通例であるが、この場合は減少が著しい。


図6 Debug構成での測定結果の一例


図7 Release構成での測定結果の一例


detectEdges関数
 「Visual Studio 2012 Expressの易しい使い方(7)」の図12〜図13にあるものを、Win32独特の表記法を避けて記述したのが図8のプログラムで、得られた結果の一例を図9に示す。図8のプログラムで、354行末の「\」は、355行に続くことを意味する。単に改行すると、#pragma文としてエラーになる。
 プログラムの内容は下記の通りである。
 1)エッジ検出のカーネルは、外部から受け取らず、内部に記述する。Sobelタイプを縦横合計2個用意する。
 1)3x3のカーネルを使用すると、画像周辺部の処理ができないので、周辺部は、エッジなし(白)に設定する。
 2)引数の原画像データ配列pdataを、周辺部を除いてxy方向にスキャンし、カーネルkernel_vとkernel_hとでコンボリューションを行う
    ・原画像のピクセルデータpdataをCOLORREF型のcolorに入れる
    ・colorをRGB別に分解する
    ・各々にkernel_vの係数を乗じ、r_v、g_v、b_vに足し込む
    ・同様にkernel_hの係数を乗じ、r_h、g_h、b_hに足し込む
    ・r_vとr_hの二乗和の平方根をエッジ強度edge_rとする。他のカラー成分も同様。
    ・edge_r、edge_g、edge_bの平均を求め、これが一定以上だとエッジと判定して、処理後画像データ配列pdata1に書き込む。



図8 detectEdges関数の内部で並列化する



図9 エッジ検出画像(背景)と、Release構成のOpenMP使用で得られた結果の一例


エッジ検出処理の実行時間の測定結果
 Debug構成では、VC++デフォルトの平均10800μsecに対して、OpenMPでは平均4100μsecと約38%に短縮された。
 Release構成では、VC++デフォルトが平均4430μsec、OpenMPが平均2100μsecと約47%になった。
 これらの測定結果の例を、図10と図11にグラフで表示する。

図10 Release構成での測定結果の一例



図11 Release構成での測定結果の一例


オーバーヘッドの比率と、並列化で得られる短縮率の関係
 実行すべき処理の内、オーバーヘッドの割合をover_headとすると、並列化可能な残りの部分は 1 - over_headとなる。これを、スレッド数nで割ると、並列化の結果として得られる時間(短縮率)parallelは、
     parallel = over_head + (1 - over_head) / n
となり、parallelからover_headを求める式に変換すると、
     over_head = (n * parallel - 1) / (n - 1)
となる。
 スレッド数n = 4とし、実測で得られた結果から、短縮率parallel = 0.4とすると、over_head = 0.2となり、20%程度のオーバーヘッドはやむを得ないものと考えられる。スレッド数が4であるからと言って、実行時間が25%になることを期待するのは無理な話で、40%なら良しとしなければいけないであろう。

その他、確認したこと
 1)ここで紹介した平滑化カーネルの他に、鮮鋭化カーネル、ラプラシアンカーネルも試みたが、実行時間に有意な差はなかった。
 2)makeConvolution関数を呼び出す前後の時間差と、関数内部のコンボリューション実行部のみの時間差を比較したが、差は誤差の範囲であった。detectEdges関数についても、同様であった。
 3)画像周辺部の処理(コピーしたり白にしたり)にもforループが使われているので、この並列化も試みたが、全体に対する時間短縮の寄与度が低く、必要なしと判断した。

結 論
 画像処理の中でも、実行時間が比較的長いコンボリューション処理について、OpenMP使用の効果を調べた。大きいバラツキはあるが、大体40%程度に時間が短縮できた。この値からオーバーヘッドの比率を計算すると、約20%となり、納得できる数字かなと思われる。これ以上の時間短縮には、よりスレッド数が多いCPUの使用が必要である。




「Visual C++の勉強部屋」(目次)