19.04.2013 Views

543-pdf

543-pdf

543-pdf

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

GPGPUによる高速画像処理<br />

~リアルタイム画像処理への挑戦~<br />

名古屋大学大学院情報科学研究科<br />

出口 大輔


リアルタイム画像処理<br />

2


発表の流れ<br />

GPGPUを始める前に<br />

GPGPUの基礎知識<br />

CUDAの使い方<br />

CUDAを使う前に<br />

プログラミングの予備知識<br />

CUDAを使って“Hello World”<br />

GPGPUにチャレンジ<br />

行列積の計算<br />

テンプレートマッチング<br />

ガウシアンフィルタ<br />

SIFT特徴量の計算<br />

3


~GPGPUって何?~<br />

4


GPGPUって?<br />

GPGPUは何の略?<br />

General-Purpose computation on GPUs<br />

GPUを汎用計算に利用しようという試み<br />

現在は「GPUコンピューティング」とも呼ばれる<br />

どうしてGPGPUが注目されているのか?<br />

5


ピ<br />

ー<br />

ク<br />

性<br />

能<br />

CPUとGPUの性能比較<br />

1600<br />

1400<br />

1200<br />

1000<br />

800<br />

600<br />

400<br />

200<br />

[GFLOP/s]<br />

Geforece 8800GTX<br />

Geforece GTX280<br />

Geforece GTX480<br />

Core2 Duo<br />

3.0GHz<br />

Quad Core<br />

Xeon 3.2GHz<br />

NVIDIA GPU<br />

0<br />

1900 1900 1900 1900 1900 1900 1900 1900 1900<br />

2003 2004 2005 2006 2007 2008 2009 2010<br />

※NVIDIA CUDA Programming Guide 4.0より引用<br />

6<br />

Intel<br />

CPU


GPGPUって?<br />

GPGPUは何の略?<br />

General-Purpose computation on GPUs<br />

GPUを汎用計算に利用しようという試み<br />

現在は「GPUコンピューティング」とも呼ばれる<br />

どうしてGPGPUが注目されているのか?<br />

GPUの計算性能が飛躍的に向上<br />

最新のGPUは 1.5 TFLOPS 以上の演算性能(CPUの約10倍)<br />

GPUはCPUと比較して並列計算に優れている<br />

GeForce GTX580 では 512 コアによる並列計算が可能<br />

手頃な価格で入手可能<br />

GeForce GTX580 は約5万円で購入可能<br />

7


GPGPUの活用例<br />

動画像処理<br />

CyberLink PowerDirector 7<br />

ビデオエフェクトのレンダリングを高速化<br />

TMPGEnc 4.0 XPress<br />

フィルター処理・デコード処理の高速化<br />

画像処理<br />

Adobe Photoshop CS4 / CS5,Adobe Premire CS5<br />

各種フィルタ処理の高速化<br />

OpenCV<br />

各種画像処理の高速化<br />

数値計算<br />

MATLAB<br />

FFTの高速化<br />

8


GPUの歴史: 1999年以前<br />

1970年 ~ 1990年<br />

初期開発の時代<br />

ソフトウェアによるグラフィックス処理<br />

プログラム可能なGPUに関する初期研究<br />

Ikonas System [1], Pixel Machine [2]<br />

[1] J. N. England, “A system for interactive modeling of physical curved surface<br />

objects,” Proceedings of SIGGRAPH 78, pp.336-340. 1978<br />

[2] M. Potmesil and E. M. Hoffert, “The Pixel Machine: A Parallel Image<br />

Computer,” Proceedings of SIGGRAPH89, pp.69-78, 1989<br />

1990年 ~ 1999年<br />

GPU技術の黎明期<br />

3Dグラフィックス・アクセラレータの開発<br />

グラフィックス向けライブラリの開発<br />

OpenGL(1992年~), DirectX(1995年~)<br />

9


GPUの歴史: 1999年~<br />

“GPU”の誕生<br />

NVIDIA社の GeForce 256 の登場<br />

ハードウェア T&L ※ をサポート<br />

CPU負荷を大幅に削減<br />

グラフィックスパイプライン<br />

ハードウェア固定の処理<br />

自由表現な表現は不可<br />

※Transform & Lighting<br />

頂点処理<br />

(ハードウェア固定)<br />

ジオメトリ処理<br />

(クリッピング等)<br />

ラスタライズ処理<br />

ピクセル処理<br />

(ハードウェア固定)<br />

画面出力<br />

10


GPUの歴史: 2003年~<br />

プログラマブルシェーダの登場<br />

Vertex Shader<br />

頂点座標の変換処理<br />

Pixel Shader<br />

画素の輝度計算処理<br />

シェーダ言語の進化<br />

アセンブラから高級言語へ<br />

Cg, HLSL, GLSL<br />

柔軟な映像表現が可能に<br />

グラフィックス以外への応用<br />

GPGPUへの関心が高まる<br />

Vertex Shader<br />

(プログラマブル)<br />

ジオメトリ処理<br />

(クリッピング等)<br />

ラスタライズ処理<br />

Pixel Shader<br />

(プログラマブル)<br />

画面出力<br />

11


GPUの歴史: 2007年~<br />

GPGPUの開発環境の整備<br />

CUDA (NVIDIA)<br />

ATI Stream (AMD)<br />

OpenCL<br />

DirectCompute<br />

GPGPU時代の到来<br />

物理シミュレーション<br />

数値計算<br />

信号解析<br />

画像処理・認識<br />

・・・<br />

統合型シェーダ<br />

Vertex Shader<br />

(プログラマブル)<br />

Geometry Shader<br />

(プログラマブル)<br />

ラスタライズ処理<br />

Pixel Shader<br />

(プログラマブル)<br />

画面出力<br />

12


GPUの歴史: 2009年~<br />

Fermiアーキテクチャの登場<br />

汎用計算向けのアーキテクチャ ※<br />

L1/L2キャッシュの搭載<br />

複数カーネルの同時実行のサポート<br />

倍精度浮動小数点演算の高速化<br />

ECCメモリのサポート<br />

アトミックなメモリ操作の高速化<br />

C++のフルサポート<br />

GPGPU関連のライブラリ&ツールの充実<br />

CUBLAS, CUFFT, Thrust, NPP<br />

Parallel Nsight, Visual Profiler<br />

13<br />

※ビデオカードによって<br />

サポート状況は異なる


~CUDAを使う前に~<br />

14


CUDAって何?<br />

CUDA(Compute Unified Device Architecture)<br />

発音は「クーダ」もしくは「キューダ」<br />

NVIDIA社が提供するGPUを利用する<br />

ための統合開発環境<br />

GeForce 8以降のハードウェアで利用可能<br />

グラフィックス処理APIの知識は不要<br />

CPUでプログラムを実行する感覚<br />

C/C++を用いてプログラムの開発が可能<br />

既存のアルゴリズムの移植も比較的容易<br />

15


CUDAが利用可能な環境<br />

CUDA対応のグラフィックスカード<br />

GeForce GTX 580, GTX 480, GTX 260, 8800シリーズ,他<br />

Quadro Plex 2200 D2, FX 5800, FX 5600, 5000, 6000, 他<br />

Tesla S2050, C2070, S1070, C1060, S870, D870, C870<br />

※TeslaはHPCに特化した製品であり映像出力を持たない<br />

OSの対応状況(CUDA 4.0)<br />

Windows XP(32bit, 64bit)<br />

Windows Vista(32bit, 64bit)<br />

Windows Server 2008(32bit, 64bit)<br />

Linux(32bit, 64bit)<br />

Mac OS<br />

16


CUDAを使うための準備(Windows編)<br />

「CUDA ZONE」から次をダウンロード<br />

NVIDIA Driver<br />

CUDA対応のビデオドライバー<br />

CUDA Toolkit 4.0 ※<br />

コンパイラ(nvcc)<br />

CUBLASやCUFFT<br />

ライブラリ<br />

ドキュメント<br />

CUDA SDK 4.0<br />

サンプル<br />

※ 2011年5月25日にリリース<br />

17<br />

http://www.nvidia.com/object/cuda_home.html


CUDAを動かしてみよう!!<br />

Volumetric Particle Shadows<br />

Image Denoising<br />

18<br />

※「CUDA SDK」内のサンプルの実行結果


~プログラミングの予備知識~<br />

19


CUDA対応のGPU(G80, GT200)<br />

ストリーミング・マルチプロセッサ(SM)<br />

8 個のスカラープロセッサ(SP)<br />

16KBの共有メモリ<br />

スレッド間の同期機構<br />

マルチプロセッサ内でのみ可能<br />

マルチプロセッサ間での同期には<br />

CPU処理が必要<br />

GeForce GTX280 の場合<br />

30 基のマルチプロセッサを搭載<br />

1GByte以上のグローバルメモリ<br />

マルチプロセッサ #1<br />

SP SP SP SP<br />

SP SP SP SP<br />

マルチプロセッサ #2<br />

SP SP SP SP<br />

SP SP SP SP<br />

20


CUDA対応のGPU(Fermi)<br />

ストリーミング・マルチプロセッサ(SM)<br />

32 個のスカラープロセッサ(SP)<br />

48KBの共有メモリ<br />

スレッド間の同期機構<br />

マルチプロセッサ内でのみ可能<br />

マルチプロセッサ間での同期には<br />

CPU処理が必要<br />

GeForce GTX480 の場合<br />

15 基のマルチプロセッサを搭載<br />

1GByte以上のグローバルメモリ<br />

SM #1<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

SM #2<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

SP SP SP SP SP SP SP SP<br />

21


CUDAのプログラミングモデル<br />

GPUの特徴<br />

多数のスレッドが高い並列性をもって処理を実行<br />

GPU のみでプログラムを実行できない<br />

CPUとの連携が不可欠<br />

CUDAにおける計算の流れ<br />

GPUを並列演算可能なデバイスとして扱う<br />

複数のスレッドを同時に実行できる外部CPU<br />

階層的にスレッドを管理<br />

スレッドのまとまり = ブロック<br />

ブロックのまとまり = グリッド<br />

問題を分割して計算する際に便利<br />

22


CUDAにおけるスレッド管理<br />

スレッドを3次元的に配置<br />

各スレッドの ID を X, Y, Z の3要素で表現<br />

スレッドのまとまりをブロックとして管理<br />

グリッド<br />

ブロック(0,0,0)<br />

ブロック(0,0,1)<br />

スレッド<br />

(0,0,0)<br />

(0,0,1)<br />

スレッド<br />

(0,1,0) (1,0,0) (1,0,1)<br />

スレッド<br />

(1,0,0) (0,1,0) (0,1,1)<br />

スレッド<br />

(1,1,0)<br />

(1,1,1)<br />

スレッド<br />

(2,0,0) (0,2,0) (0,2,1)<br />

スレッド<br />

(2,1,0)<br />

(1,2,0) (1,2,1)<br />

ブロック(1,0,0)<br />

ブロック(1,0,1)<br />

スレッド<br />

(0,0,0)<br />

(0,0,1)<br />

スレッド<br />

(0,1,0) (1,0,0) (1,0,1)<br />

スレッド<br />

(1,0,0) (0,1,0) (0,1,1)<br />

スレッド<br />

(1,1,0)<br />

(1,1,1)<br />

ブロック(0,1,0)<br />

ブロック(0,1,1)<br />

ブロック(1,1,0)<br />

ブロック(1,1,1)<br />

スレッド<br />

(2,0,0) (0,2,0) (0,2,1)<br />

スレッド<br />

(2,1,0) (1,2,0) (1,2,1)<br />

23


階層的スレッドの利用方法<br />

一般的な画像処理で利用する場合<br />

ブロック内のスレッド数を決定<br />

16×16 = 256 スレッド<br />

画像内にブロックを配置<br />

ブロック内のスレッド<br />

が各画素を処理<br />

計算範囲の求め方<br />

ブロックID<br />

スレッドID<br />

ブロック内スレッド数<br />

T 00 T 10<br />

T 01<br />

T 0M<br />

T N0<br />

T NM<br />

ブロック#1<br />

T 00 T 10 T N0<br />

T 01<br />

T 0M<br />

T NM<br />

24


CUDAにおける計算の流れ<br />

処理1<br />

処理2<br />

グリッド 1<br />

ブロック(0,0,0)<br />

スレッド<br />

(0,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(1,1,0)<br />

スレッド<br />

(2,0,0)<br />

スレッド<br />

(2,1,0)<br />

ブロック(1,0,0)<br />

ブロック(0,1,0) ブロック(1,1,0)<br />

グリッド 2<br />

スレッド<br />

(0,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(1,1,0)<br />

ブロック (0,0,0) ブロック (1,0,0)<br />

スレッド<br />

(2,0,0)<br />

スレッド<br />

(2,1,0)<br />

25


CUDAのメモリモデル<br />

ブロック (0,0,0)<br />

ローカルメモリ<br />

レジスタ<br />

スレッド (0,0,0)<br />

共有メモリ<br />

ローカルメモリ<br />

レジスタ<br />

スレッド(1,0,0)<br />

ブロック(1,0,0)<br />

コンスタントメモリ<br />

テクスチャメモリ<br />

グローバルメモリ<br />

ローカルメモリ<br />

レジスタ<br />

共有メモリ<br />

ローカルメモリ<br />

レジスタ<br />

スレッド(0,0,0) スレッド(1,0,0)<br />

26


CUDAで利用可能なメモリ<br />

グローバルメモリ<br />

大量のメモリを利用可能<br />

低速なメモリアクセス(400~600クロック必要)<br />

Fermiアーキテクチャではキャッシュ機構(L1/L2)を搭載<br />

共有メモリ<br />

レジスタと同じ速度でアクセス可能<br />

マルチプロセッサ1基あたり16KB(Fermiは最大48KB)<br />

Fermiの場合は一部をL1キャッシュとして利用可能<br />

テクスチャメモリ<br />

キャッシュ機構による高速なアクセスが可能<br />

ハードウェア線形補間や正規化テクスチャ座標が利用可能<br />

コンスタントメモリ<br />

キャッシュ機構による高速なアクセスが可能<br />

マルチプロセッサ1基あたり64KB<br />

27


各GPUで使用できる機能の違い<br />

使用できる機能を Compute Capability で区別<br />

現在 1.0 ~ 2.1 のGPUが存在<br />

Compute Capability による機能の違い<br />

1.0:初期リリース<br />

GeForce 8800GTX, Quadro FX 5600, 他<br />

1.1:アトミックなメモリ操作のサポート<br />

GeForce 9800 GTX, Quadro FX 3700, 他<br />

1.3:倍精度浮動小数点演算のサポート<br />

GeForce GTX 280, Quadro FX 5800, 他<br />

2.x :グローバルメモリのキャッシュをサポート<br />

GeForce GTX480,Quadro 5000,他<br />

28


Compute Capabilityによる機能の違い<br />

Compute Capability<br />

1.0 1.1 1.2 1.3 2.x<br />

3次元グリッド × ○<br />

倍精度浮動小数 × ○<br />

32ビット整数の<br />

アトミック演算 × ○<br />

64ビット整数の<br />

アトミック演算 × ○<br />

単精度浮動小数の<br />

アトミック演算 × ○<br />

29


CUDAにおける制限事項<br />

1ブロックあたりの<br />

スレッド数<br />

1マルチプロセッサ<br />

あたりのスレッド数<br />

1マルチプロセッサ<br />

あたりのレジスタ数<br />

Compute Capability<br />

1.0 1.1 1.2 1.3 2.x<br />

コンスタントメモリ 64KB<br />

512 1024<br />

768 1024 1536<br />

8192 16384 32768<br />

共有メモリ 16KB 48KB<br />

2Dテクスチャ 2 16 ×2 15 2 16 ×2 16<br />

30


~ CUDAを使って“Hello World”~<br />

31


CUDAを使って“Hello World!!”<br />

Hello World!! を表示するサンプル(main.cu)<br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

char *dData, hData[ 15 ];<br />

}<br />

cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 );<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( dData );<br />

cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost );<br />

printf( "%s", hData );<br />

cudaFree( dData );<br />

return( 0 );<br />

32


CUDAを使って“Hello World!!”<br />

Hello World!! を表示するサンプル(main.cu)<br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

char *dData, hData[ 15 ];<br />

}<br />

cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 );<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( dData );<br />

cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost );<br />

printf( "%s", hData );<br />

cudaFree( dData );<br />

return( 0 );<br />

_ _global_ _, threadIdx<br />

CUDAで拡張された部分<br />

dim3, <br />

33


CUDAを使って“Hello World!!”<br />

Hello World!! を表示するサンプル(main.cu)<br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

char *dData, hData[ 15 ];<br />

}<br />

cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 );<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( dData );<br />

cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost );<br />

printf( "%s", hData );<br />

cudaFree( dData );<br />

return( 0 );<br />

34


CUDAの言語拡張(1)<br />

言語拡張により追加された修飾子<br />

関数に対する修飾子<br />

_ _global_ _ CPU から呼び出され,GPU で実行される関数<br />

_ _device_ _ GPU から呼び出され,GPU で実行される関数<br />

_ _host_ _ CPU から呼び出され,CPU で実行される関数<br />

変数に対する修飾子<br />

_ _constant_ _ GPU 上のコンスタントメモリに存在する変数<br />

_ _shared_ _ GPU 上の共有メモリに存在する変数<br />

_ _device_ _ GPU 上のメモリに存在する変数<br />

35


CUDAを使って“Hello World!!”<br />

Hello World!! を表示するサンプル(main.cu)<br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

char *dData, hData[ 15 ];<br />

}<br />

cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 );<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( dData );<br />

cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost );<br />

printf( "%s", hData );<br />

cudaFree( dData );<br />

return( 0 );<br />

36


CUDAの言語拡張(2)<br />

CUDAで利用可能な組み込み型<br />

dim3 整数 x, y, z からなる 3 次元ベクトル<br />

(スレッド数やブロック数の指定に利用)<br />

uchar2, int2, float2, … x, y からなる 2 次元ベクトル<br />

uchar3, int3, float3, … x, y, z からなる 3 次元ベクトル<br />

uchar4, int4, float4, … x, y, z, w からなる 4 次元ベクトル<br />

GPU内のスレッドを識別する組み込み変数<br />

gridDim グリッドの次数<br />

blockIdx スレッドが属するブロックのインデックス<br />

blockDim スレッドが属するブロックの次数<br />

threadIdx ブロック内のスレッドのインデックス<br />

37


gridDim<br />

= 2×2×2<br />

スレッドを識別する組み込み変数<br />

38<br />

グリッド<br />

ブロック(0,1,1)<br />

ブロック(1,0,1)<br />

スレッド<br />

(0,0,1)<br />

スレッド<br />

(0,1,1)<br />

スレッド<br />

(0,2,1)<br />

スレッド<br />

(1,0,1)<br />

スレッド<br />

(1,1,1)<br />

スレッド<br />

(1,2,1)<br />

ブロック(1,1,1)<br />

ブロック(0,1,0) ブロック(1,1,0)<br />

ブロック(1,0,0)<br />

スレッド<br />

(0,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(0,2,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(1,1,0)<br />

スレッド<br />

(1,2,0)<br />

スレッド<br />

(0,0,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(2,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(1,1,0)<br />

スレッド<br />

(2,1,0)<br />

ブロック(0,0,1)<br />

スレッド<br />

(0,0,1)<br />

スレッド<br />

(0,1,1)<br />

スレッド<br />

(0,2,1)<br />

スレッド<br />

(1,0,1)<br />

スレッド<br />

(1,1,1)<br />

スレッド<br />

(1,2,1)<br />

ブロック(0,0,0)<br />

blockIdx<br />

blockDim<br />

= 3×2×2<br />

スレッド<br />

(0,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(0,2,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(1,1,0)<br />

スレッド<br />

(1,2,0)<br />

スレッド<br />

(1,0,0)<br />

スレッド<br />

(2,0,0)<br />

スレッド<br />

(0,1,0)<br />

スレッド<br />

(1,1,0)<br />

スレッド<br />

(2,1,0)<br />

スレッド<br />

(0,0,0)<br />

threadIdx


C/C++プログラミングとの相違点<br />

CPUとGPUで実行するコードを明確に区別<br />

CPUで実行する場合は “__host__”(省略可)を付与<br />

GPUで実行する場合は “__global__” を付与<br />

CPUとGPUで処理可能なメモリ空間の違い<br />

CPUとGPU間でメモリ転送が必要<br />

スレッド数を指定してGPU上の関数を呼び出し<br />

関数名>( … );<br />

ブロック数とスレッド数は実行時に指定可能<br />

問題サイズに合わせて調整可能<br />

39


CUDAを使って“Hello World!!”<br />

Hello World!! を表示するサンプル(main.cu)<br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

char *dData, hData[ 15 ];<br />

}<br />

cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 );<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( dData );<br />

cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost );<br />

printf( "%s", hData );<br />

cudaFree( dData );<br />

return( 0 );<br />

GPU上のメモリを解放<br />

GPU上で実行される関数<br />

GPU上にメモリを確保<br />

ブロック内に配置する<br />

スレッド数 15×1 = 15<br />

総ブロック数 1×1 = 1<br />

CPUからGPUへメモリ転送<br />

40


スレッドのレジスタ数を調査<br />

Visual Studio 2008 コマンドプロンプトを起動<br />

コマンドプロンプト上で main.cu をコンパイル<br />

C:¥Your¥Path>nvcc main.cu --ptxas-options=-v --compile<br />

main.cu<br />

ptxas info : Compiling entry function '_Z5helloPc' for 'sm_10'<br />

ptxas info : Used 2 registers, 8+16 bytes smem, 15 bytes cmem[0]<br />

レジスタ数<br />

合計レジスタ数をチェック<br />

2 × 15 = 30<br />

レジスタ数 生成スレッド数 合計<br />

41


~Thrustライブラリの使い方~<br />

42


Thrustライブラリって?<br />

CUDAで利用できるテンプレートライブラリ<br />

CUDAとOpenMPに対応したC++ STLの並列版<br />

URL: http://thrust.googlecode.com/<br />

Thrustライブラリの特徴<br />

コンテナ<br />

CPUとGPUのデータをコンテナとして管理<br />

アルゴリズム<br />

コンテナに対してアルゴリズムを適用可能<br />

直感的なインターフェース<br />

CUDAの複雑なAPIに関する知識は不要<br />

メモリの確保/解放/転送がとても簡単<br />

43


Thrustライブラリの使い方<br />

GPU上で「1,2,3,…,100」の総和を計算<br />

#include <br />

#include <br />

#include <br />

#include <br />

int main( int argc, char *argv[] )<br />

{<br />

// CPU上のメモリを確保<br />

thrust::host_vector< int > hvec( 100 );<br />

}<br />

// データを初期化 1, 2, 3, ...<br />

thrust::sequence( hvec.begin( ), hvec.end( ), 1 );<br />

// GPU上のメモリを確保<br />

thrust::device_vector< int > dvec( 100 );<br />

// CPU -> GPU のメモリ転送<br />

dvec = hvec;<br />

int val = thrust::reduce( dvec.begin( ), dvec.end( ), 0, thrust::plus< int >( ) );<br />

printf( "%d¥n", val );<br />

return( 0 );<br />

44


コンテナの使い方(1)<br />

メモリの確保<br />

thrust::host_vector< int > hvec( 20 );<br />

CPU上のメモリに要素数20の「int」配列を確保<br />

thrust::device_vector< float > dvec( 100 );<br />

GPU上のメモリに要素数100の「float」配列を確保<br />

メモリの解放<br />

コンテナオブジェクトの消滅時に自動解放<br />

CPUの場合:free( )<br />

GPUの場合:cudaFree( )<br />

明示的なメモリ解放:dvec.clear( )<br />

45


コンテナの使い方(2)<br />

メモリの転送<br />

代入操作でメモリ転送が可能<br />

thrust::host_vector< float > hvec( 20 );<br />

thrust::device_vector< float > dvec( 20 );<br />

dvec = hvec;<br />

コンテナの各要素へのアクセス<br />

配列と同様にアクセス可能<br />

hvec[ 5 ] = 100.0f;<br />

dvec[ 1 ] = 23.4f; 内部でcudaMemcpyが呼ばれるので注意<br />

46


イテレータ(1)<br />

コンテナの要素を指すポインタ<br />

STLのイテレータと同じように使用可能<br />

thrust::device_vector< int > dvec( 4 );<br />

thrust::device_vector< int >::iterator ite = dvec.begin( );<br />

dvec.begin( ) dvec.end( )<br />

dvec<br />

0 1 2 3<br />

dvec.begin( ) + 3<br />

47


イテレータ(2)<br />

コンテナの各要素へのアクセス<br />

thrust::device_vector< int > dvec( 4 );<br />

thrust::device_vector< int >::iterator ite = dvec.begin( );<br />

*ite = 10;<br />

++ite;<br />

*ite = 25;<br />

dvec[ 0 ] = 10 と等価<br />

dvec[ 1 ] = 25 と等価<br />

48


アルゴリズムと使用方法<br />

CPUとGPUのデータに対して同じ様に適用可能<br />

GPUの場合はGPUを使用して並列処理される<br />

アルゴリズムの使用例<br />

コンテナのデータすべてに1を代入する<br />

thrust::fill( dvec.begin( ), dvec.end( ), 1 );<br />

1 1 1 1 1 1 1 1<br />

コンテナのデータに 1, 2, 3, … の値で初期化する<br />

thrust::sequence( dvec.begin( ), dvec.end( ) );<br />

1 2 3 4 5 6 7 N<br />

49


Parallel Reduction(1)<br />

コンテナ内の要素を1つの値に集約する処理<br />

GPUのスレッドが並列に処理を実行<br />

例:総和計算の場合<br />

GPU上のデータ<br />

1 2 3 4 5 6 7 8<br />

3 7 11 15<br />

10 26<br />

36<br />

36<br />

CUDAスレッドの同期<br />

CUDAスレッドの同期<br />

50


Parallel Reduction(2)<br />

Thrustライブラリを用いた集約処理<br />

総和の計算<br />

int val = thrust::reduce( dvec.begin( ), dvec.end( ) );<br />

1 2 3 4 5 6 7 10 55<br />

最大値の計算<br />

int val = thrust::reduce( dvec.begin( ), dvec.end( ),<br />

1 10 3 8 5 9 7 2<br />

6 10<br />

51<br />

0, thrust::maximum< int >( ) );


CUDAとThrustを組み合わせる<br />

Hello World!! を表示するサンプル<br />

#include <br />

#include <br />

__global__ void hello( char *data )<br />

{<br />

char *text = "Hello World!!¥n";<br />

data[ threadIdx.x ] = text[ threadIdx.x ];<br />

}<br />

int main( int argc, char *argv[] )<br />

{<br />

thrust::host_vector< char > hvec( 15 );<br />

thrust::device_vector< char > dvec( 15 );<br />

}<br />

dim3 nThreads( 15, 1 );<br />

dim3 nBlocks( 1, 1 );<br />

hello>( thrust::raw_pointer_cast( &dvec[ 0 ] ) );<br />

hvec = dvec;<br />

printf( "%s", &hvec[ 0 ] );<br />

return( 0 );<br />

CPUからGPUへメモリ転送<br />

GPU上のメモリを指す<br />

ポインタを取得<br />

52


GPGPUにチャレンジ<br />

行列積の計算<br />

CUDAにおける基本的な実装方法<br />

共有メモリ<br />

テンプレートマッチング<br />

2次元テクスチャメモリ<br />

ハードウェア線形補間<br />

正規化テクスチャ座標<br />

Parallel Reduction アルゴリズム<br />

ガウシアンフィルタ<br />

1次元テクスチャメモリ<br />

コンスタントメモリ<br />

SIFT特徴量の計算<br />

1・2次元テクスチャメモリ<br />

コンスタントメモリ<br />

54


~行列積の計算~<br />

55


GPUによる行列積の計算<br />

行列積計算の特徴<br />

各要素はすべて独立に計算可能<br />

GPUによる並列計算が可能<br />

同じメモリ領域へ頻繁にアクセス<br />

メモリアクセス速度がボトルネック<br />

GPU上での実装方法<br />

1. 行列積の計算式に忠実な実装(GPU#1)<br />

2. 共有メモリを利用した高速化(GPU#2)<br />

ブロック単位(部分行列)で行列積を計算<br />

共有メモリをキャッシュとして利用<br />

56


行列積C=A×Bの流れ<br />

1. 行列A, B, CのメモリをGPU上に確保<br />

2. 行列A, BのデータをGPUに転送<br />

3. 行列Cの各要素c mnを計算<br />

N 次正方行列を仮定<br />

ただし,N は16の倍数<br />

c<br />

mn<br />

<br />

N<br />

<br />

k1<br />

mk<br />

b<br />

4. 計算結果をCPUに転送<br />

a<br />

kn<br />

m行<br />

amk<br />

N<br />

N<br />

n列<br />

行列A 行列C<br />

bkn<br />

57<br />

行列B<br />

cmn


処理の流れ<br />

CPU側の処理<br />

行列の初期化<br />

GPU側の処理<br />

CPU<br />

行列Cの各要素を計算<br />

行<br />

列<br />

の<br />

初<br />

期<br />

化<br />

GPU<br />

メ<br />

モ<br />

リ<br />

確<br />

保<br />

CPU GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU<br />

行<br />

列<br />

積<br />

の<br />

計<br />

算<br />

GPU CPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

58


行列積計算のCPU処理(ソース)<br />

int main( int argc, char *argv[] )<br />

{<br />

int N = 512; // 行列A, B, Cのサイズ<br />

float *hA, *hB, *hC; // CPU(host)側で利用するメモリへのポインタ<br />

float *dA, *dB, *dC; // GPU(device)側で利用するメモリへのポインタ<br />

}<br />

/* CPU側のメモリを確保*/<br />

/* GPU側のメモリを確保*/<br />

/* CPU側のメモリをGPU側へ転送 */<br />

/* 実行するGPUのスレッド数,ブロック数を設定 */<br />

/* GPUのカーネルを実行し,C=A×B の結果を dC に格納 */<br />

/* GPUの計算結果をCPU側へ転送 */<br />

/* CPUとGPUそれぞれのメモリを解放 */<br />

return( 0 );<br />

59


行列積計算のCPU処理(1)<br />

行列積の計算で使用する変数<br />

int N = 512; // 行列A, B, Cのサイズ<br />

float *hA, *hB, *hC; // CPU(host)側で利用するメモリへのポインタ<br />

float *dA, *dB, *dC; // GPU(device)側で利用するメモリへのポインタ<br />

CPUとGPUでメモリを確保<br />

CPU:malloc, new, cudaMallocHost でメモリを確保<br />

GPU:cudaMalloc によりグローバルメモリを確保<br />

/* CPU側のメモリを確保 */<br />

hA = ( float * )malloc( N * N * sizeof( float ) );<br />

hB = ( float * )malloc( N * N * sizeof( float ) );<br />

hC = ( float * )malloc( N * N * sizeof( float ) );<br />

/* GPU側のメモリを確保 */<br />

cudaMalloc( ( void ** )&dA, N * N * sizeof( float ) );<br />

cudaMalloc( ( void ** )&dB, N * N * sizeof( float ) );<br />

cudaMalloc( ( void ** )&dC, N * N * sizeof( float ) );<br />

60


行列積計算のCPU処理(2)<br />

CPUとGPU間のメモリ転送<br />

CPU→GPU<br />

cudaMemcpy( dst, src, size, cudaMemcpyHostToDevice );<br />

GPU→CPU<br />

転送元<br />

転送先 転送バイト数<br />

cudaMemcpy( dst, src, size, cudaMemcpyDeviceToHost );<br />

この部分を変更<br />

転送方向<br />

CPUとGPUで確保したメモリの解放<br />

CPU:free, delete, cudaFreeHost でメモリを解放<br />

GPU:cudaFree でグローバルメモリを解放<br />

61


行列積計算のCPU処理(3)<br />

GPU上で処理を実行<br />

計算範囲の設定<br />

N 次正方行列を仮定<br />

ただし N は16の倍数<br />

行列Cをサイズ16×16のブロックに分割<br />

ブロック内の各要素を各スレッドが計算<br />

合計M×M 個のブロックを配置<br />

各スレッドが c mn を計算<br />

n = threadIdx.x + blockIdx.x × blockDim.x;<br />

m = threadIdx.y + blockIdx.y × blockDim.y;<br />

GPU上で関数を実行<br />

multiply>( … );<br />

16<br />

16<br />

(threadIdx.y, threadIdx.x)<br />

行列C<br />

62<br />

(blockIdx.y, blockIdx.x)<br />

行方向のブロック数 (=16)


行列積計算のCPU処理(まとめ)<br />

int main( int argc, char *argv[] )<br />

{<br />

int N = 512; // 行列A, B, Cのサイズ<br />

float *hA, *hB, *hC; // CPU(host)側で利用するメモリへのポインタ<br />

float *dA, *dB, *dC; // GPU(device)側で利用するメモリへのポインタ<br />

}<br />

/* CPU側のメモリを確保*/<br />

/* GPU側のメモリを確保*/<br />

/* CPU側のメモリをGPU側へ転送 */<br />

/* 実行するGPUのスレッド数,ブロック数を設定 */<br />

dim3 nThreads( 16, 16 );<br />

dim3 nBlocks( N / nThreads.x, N / nThreads.y );<br />

/* GPUのカーネルを実行し,C=A×B の結果を dC に格納 */<br />

multiply>( dA, dB, dC, N );<br />

/* GPUの計算結果をCPU側へ転送 */<br />

/* CPUとGPUそれぞれのメモリを解放 */<br />

return( 0 );<br />

GPU上で処理を実行<br />

GPU上のメモリを指定<br />

63


実装方法(GPU#1)<br />

行列積C=A×Bの各要素c mnを計算<br />

要素c mnをGPU上で並列計算<br />

__global__ void multiply1( float *dA, float *dB, float *dC, int N )<br />

{<br />

int n = threadIdx.x + blockIdx.x * blockDim.x;<br />

int m = threadIdx.y + blockIdx.y * blockDim.y;<br />

}<br />

c<br />

mn<br />

<br />

N<br />

<br />

k1<br />

a<br />

mk<br />

float sum = 0.0f;<br />

for( int k = 0 ; k < N ; k++ )<br />

{<br />

sum += dA[ m + k * N ] * dB[ k + n * N ];<br />

}<br />

dC[ m + n * N ] = sum;<br />

b<br />

kn<br />

m行<br />

amk<br />

N<br />

N<br />

n列<br />

行列A 行列C<br />

bkn<br />

64<br />

行列B<br />

cmn


実装方法(GPU#2)<br />

各要素c mnを部分行列(ブロック)の積で計算<br />

ブロックサイズが16×16の場合<br />

c<br />

mn<br />

N<br />

<br />

16<br />

t1<br />

16(<br />

t1)<br />

k16t<br />

ブロック内で同じメモリを参照<br />

高速な共有メモリを利用<br />

a<br />

mk<br />

グローバルメモリへの<br />

アクセスを削減<br />

2×N×16 2 回<br />

16分の1<br />

◦<br />

2×N×16 回<br />

b<br />

kn<br />

16<br />

16<br />

N<br />

amk<br />

N<br />

bkn<br />

16<br />

cmn<br />

16<br />

65<br />

行列B<br />

行列A 行列C


実装方法(GPU#2)<br />

__global__ void multiply2( float *dA, float *dB, float *dC, int N )<br />

{<br />

int n = threadIdx.x + blockIdx.x * blockDim.x;<br />

共有メモリの宣言<br />

int m = threadIdx.y + blockIdx.y * blockDim.y;<br />

}<br />

float sum = 0.0f;<br />

for( int k = 0 ; k < N ; k += 16 )<br />

{<br />

__shared__ float tA[ 16 ][ 16 ];<br />

__shared__ float tB[ 16 ][ 16 ];<br />

}<br />

tA[ threadIdx.y ][ threadIdx.x ] = dA[ m + ( k + threadIdx.x ) * N ];<br />

tB[ threadIdx.x ][ threadIdx.y ] = dB[ ( k + threadIdx.y ) + n * N ];<br />

__syncthreads( );<br />

for( int t = 0 ; t < 16 ; t++ )<br />

{<br />

sum += tA[ threadIdx.y ][ t ] * tB[ threadIdx.x ][ t ];<br />

}<br />

__syncthreads( );<br />

dC[ m + n * N ] = sum;<br />

ブロック内のスレッドを同期<br />

ブロック内のスレッドを同期<br />

各スレッドが独立<br />

して行列積を計算<br />

各スレッドが独立して<br />

メモリアクセス<br />

16<br />

16<br />

N<br />

amk<br />

N<br />

bkn<br />

16<br />

cmn<br />

16<br />

66<br />

行列B<br />

行列A 行列C


計算時間の比較(1)<br />

CPU, GPU#1, GPU#2 の計算時間を比較<br />

使用計算機<br />

CPU: Intel Core i7-X980(3.33GHz)<br />

OpenMPを使用して6スレッドで並列計算<br />

GPU: NVIDIA Geforce GTX480<br />

マルチプロセッサ数: 15(SP数 = 15×32 = 480)<br />

Memory:1.5 GB<br />

OS:Windows 7 SP1<br />

67


計算時間の比較(2)<br />

500<br />

450<br />

400<br />

350<br />

300<br />

250<br />

200<br />

150<br />

100<br />

50<br />

0<br />

[msec.]<br />

計算時間(N = 560 の場合)<br />

・CPU#1: 91.7 msec.<br />

・GPU#1: 18.3 msec. (×5.0)<br />

・GPU#2: 06.7 msec. (×13.7)<br />

N = 560<br />

16<br />

32<br />

48<br />

64<br />

80<br />

96<br />

112<br />

128<br />

144<br />

160<br />

176<br />

192<br />

208<br />

224<br />

240<br />

272<br />

288<br />

304<br />

320<br />

336<br />

352<br />

368<br />

400<br />

416<br />

432<br />

448<br />

464<br />

480<br />

496<br />

528<br />

544<br />

560<br />

592<br />

608<br />

624<br />

656<br />

672<br />

688<br />

720<br />

736<br />

752<br />

768<br />

784<br />

800<br />

行列のサイズ (N 次正方行列)<br />

CPU<br />

GPU#1<br />

GPU#2<br />

68


まとめ(行列積)<br />

GPU上での実装方法<br />

1. 行列積の計算式に忠実な実装(GPU#1)<br />

2. 共有メモリを利用した高速化(GPU#2)<br />

大きさ16×16の部分行列積に分割<br />

共有メモリをキャッシュとして利用<br />

CPUとGPUそれぞれでの計算速度を比較<br />

CPU < GPU#1 < GPU#2<br />

×5.0 ×13.7<br />

メモリアクセスの工夫により約34倍の高速化<br />

69


~テンプレートマッチング~<br />

70


テンプレートマッチング(1)<br />

画像処理の分野で広く用いられている手法<br />

基板の品質検査<br />

画像中の特定物体(人物など)の検出<br />

入力画像中からテンプレートに類似する部分を探索<br />

入力画像中に窓を設定<br />

窓を移動させながら<br />

類似度を評価<br />

比較<br />

テンプレート 窓<br />

71


テンプレートマッチング(2)<br />

基本的なテンプレートマッチングの戦略<br />

入力画像中の部分画像とテンプレートの類似度を評価<br />

窓を移動させながら類似度計算<br />

位置(X軸方向, Y軸方向)<br />

拡大/縮小(スケール変化)<br />

回転<br />

類似度の例<br />

SAD(Sum of Absolute Difference)<br />

SSD(Sum of Squared Difference)<br />

NCC(Normalized Cross Correlation)<br />

窓の大きさに比例して計算コストが増加<br />

膨大な回数の類似度評価が必要<br />

計算コスト大<br />

72


デモ(テンプレートマッチング)<br />

73


テンプレートマッチングの実装方法<br />

テンプレートマッチングの特徴<br />

類似度は各位置で独立に計算可能<br />

GPUによる並列計算が可能<br />

頻繁に画像メモリへアクセス<br />

テクスチャメモリのキャッシュ機能を利用<br />

複数スケールでテンプレートと入力画像を比較<br />

正規化テクスチャ座標とハードウェア線形補間の利用<br />

解像度に依存しないメモリアクセス<br />

・・・<br />

スケール<br />

74


処理の流れ<br />

CPU側の処理<br />

入力画像とテンプレートの読み込み<br />

GPUスレッドの同期<br />

GPU側の処理<br />

CPU<br />

類似度計算<br />

画<br />

像<br />

読<br />

み<br />

込<br />

み<br />

GPU<br />

メ<br />

モ<br />

リ<br />

確<br />

保<br />

CPU GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU<br />

スケールの変更<br />

類<br />

似<br />

度<br />

計<br />

算<br />

CPU<br />

ス<br />

レ<br />

ッ<br />

ド<br />

同<br />

期<br />

GPU CPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

75


テクスチャメモリとは?<br />

GPU上の読み取り専用の特殊なメモリ領域<br />

キャッシュを利用した高速なアクセスが可能<br />

2次元アクセスに対して効率的<br />

ハードウェアを利用した高速な線形補間が可能<br />

テクスチャメモリの定義<br />

texture texRef;<br />

DataType: データの型(int,float 等)<br />

Type: テクスチャの種類(1次元テクスチャなど)<br />

cudaTextureType1D,cudaTextureType2D,…<br />

ReadMode: 値の範囲<br />

cudaReadModeElementType: 各データ型の値を使用<br />

cudaReadModeNormalizedFloat: 0 ~ 1に正規化(符号付きは-1~1)<br />

76


メモリ確保と転送(1)<br />

入力画像のメモリをGPU上に確保<br />

テクスチャの定義(画素表現:RGBA)<br />

texture< uchar4, cudaTextureType2D, cudaReadModeElementType > imgTex;<br />

cudaArrayを利用して2次元テクスチャを確保<br />

cudaArray *imgArray;<br />

cudaChannelFormatDesc c1 = cudaCreateChannelDesc< uchar4 >( );<br />

cudaMallocArray( &imgArray, &c1, width, height );<br />

CPUからGPUへメモリ転送<br />

テクスチャメモリへの対応付け<br />

cudaBindTextureToArray( imgTex, imgArray, c1 );<br />

転送データ量<br />

77<br />

画素表現の指定<br />

cudaMemcpyToArray( imgArray, 0, 0, pSrc, nBytes, cudaMemcpyHostToDevice );<br />

転送元ポインタ<br />

CPUからGPUへ転送


メモリ確保と転送(2)<br />

テンプレートのメモリをGPU上に確保<br />

テクスチャの定義<br />

ハードウェア線形補間の有効化<br />

正規化テクスチャ座標の有効化<br />

ハードウェア線形補間の利用(1)<br />

texture< uchar4, cudaTextureType2D, cudaReadModeNormalizedFloat > refTex;<br />

refTex.filterMode = cudaFilterModeLinear;<br />

ハードウェア線形補間の利用(2)<br />

refTex.normalized = 1;<br />

(0, 0)<br />

(W-1, 0)<br />

(0, 0) (1, 0)<br />

(0, 1)<br />

(0, H-1) (W-1, H-1)<br />

(0, 0)<br />

(0, 0) (1, 0)<br />

(0, 1)<br />

78<br />

(1, 0)<br />

(0, 1) (1, 1)


GPU上で類似度計算<br />

__global__ void kernel( float *error, float *scale, int imgW, int imgH,<br />

int areaW, int areaH, int maskW, int maskH, float s )<br />

{<br />

int i = threadIdx.x + blockDim.x * blockIdx.x;<br />

int j = threadIdx.y + blockDim.y * blockIdx.y;<br />

}<br />

float err = 0.0f;<br />

float _1_w = 1.0f / maskW; // テンプレートの幅に対するスケーリング係数<br />

float _1_h = 1.0f / maskH; // テンプレートの高さに対するスケーリング係数<br />

for( int n = 0 ; n < maskH ; n++ )<br />

{<br />

for( int m = 0 ; m < maskW ; m++ )<br />

{<br />

uchar4 p1 = tex2D( imgTex, i + m, j + n );<br />

float4 p2 = tex2D( refTex, _1_w * m, _1_h * n ) * 255.0f;<br />

}<br />

}<br />

err += ( p1.x - p2.x ) * ( p1.x - p2.x );<br />

err += ( p1.y - p2.y ) * ( p1.y - p2.y );<br />

err += ( p1.z - p2.z ) * ( p1.z - p2.z );<br />

err *= _1_w * _1_h;<br />

if( error[ i + j * imgW ] > err )<br />

{<br />

error[ i + j * imgW ] = err;<br />

scale[ i + j * imgW ] = s;<br />

}<br />

テクスチャからデータを読み取り<br />

SSDが最小のも<br />

のを記録<br />

SSDを計算<br />

79


CPUによる類似度計算の制御<br />

// スケール&誤差を保持するGPU側のメモリ領域<br />

thrust::device_vector< float > derror( img.size( ), 1.0e10f );<br />

thrust::device_vector< float > dscale( img.size( ), 0 );<br />

float s = MIN_SCALE;<br />

while( s ( thrust::raw_pointer_cast( &derror[ 0 ] ),<br />

thrust::raw_pointer_cast( &dscale[ 0 ] ),<br />

W, H, W - w, H - h, w, h, s );<br />

cudaDeviceSynchronize( ); // 処理が完了するまで待機する<br />

s *= SCALE_FACTOR; // スケールを変更する<br />

// GPUの処理結果を転送する<br />

thrust::host_vector< float > herror = derror;<br />

thrust::host_vector< float > hscale = dscale;<br />

スケールを変更して再探索<br />

GPUを用いて類似度を計算<br />

80


計算時間の比較(1)<br />

CPUとGPUで計算時間を評価<br />

使用計算機<br />

CPU: Intel Core i7-X980(3.33GHz)<br />

OpenMPを使用して6スレッドで並列計算<br />

GPU: NVIDIA GeForce GTX480<br />

マルチプロセッサ数: 15(SP数= 15×32 = 480)<br />

Memory:1.5 GB<br />

OS:Windows 7 SP1<br />

81


計算時間の比較(2)<br />

実験パラメータ<br />

入力画像 :800×600画素<br />

テンプレート:105×135画素<br />

スケール :0.3~1.9倍(拡大率1.2)<br />

実験結果<br />

CPU: 77.3 sec.<br />

GPU: 2.2 sec.<br />

約 35 倍の高速化<br />

結果画像<br />

テンプレート<br />

82


まとめ(テンプレートマッチング)<br />

テクスチャメモリを利用した高速化<br />

キャッシュを利用した効率的なメモリアクセス<br />

解像度に依存しないメモリアクセス<br />

ハードウェア線形補間<br />

正規化テクスチャ座標<br />

CPUとGPUで計算時間を比較<br />

CPU: 77.3 sec.<br />

GPU: 2.2 sec.<br />

約 35 倍の高速化を実現<br />

83


~テンプレートマッチングを高速化~<br />

84


処理の流れ<br />

類似度計算の打ち切り処理を導入<br />

CPU<br />

各スケールで計算した類似度の最大値をGPUで計算<br />

画<br />

像<br />

読<br />

み<br />

込<br />

み<br />

Parallel Reduction アルゴリズムを利用<br />

GPU<br />

メ<br />

モ<br />

リ<br />

確<br />

保<br />

CPU GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU<br />

類<br />

似<br />

度<br />

計<br />

算<br />

スケールの変更<br />

CPU<br />

ス<br />

レ<br />

ッ<br />

ド<br />

同<br />

期<br />

GPU<br />

最<br />

大<br />

類<br />

似<br />

度<br />

計<br />

算<br />

GPU CPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

85


GPU上で類似度計算<br />

__global__ void kernel( float *error, float *scale, int imgW, int imgH,<br />

int areaW, int areaH, int maskW, int maskH, float s, float maxerr )<br />

{<br />

int i = threadIdx.x + blockDim.x * blockIdx.x;<br />

int j = threadIdx.y + blockDim.y * blockIdx.y;<br />

}<br />

float err = 0.0f;<br />

float _1_w = 1.0f / maskW; // テンプレートの幅に対するスケーリング係数<br />

float _1_h = 1.0f / maskH; // テンプレートの高さに対するスケーリング係数<br />

for( int n = 0 ; n < maskH ; n++ )<br />

{<br />

for( int m = 0 ; m < maskW ; m++ )<br />

{<br />

uchar4 p1 = tex2D( imgTex, i + m, j + n );<br />

float4 p2 = tex2D( refTex, _1_w * m, _1_h * n ) * 255.0f;<br />

}<br />

}<br />

err += ( p1.x - p2.x ) * ( p1.x - p2.x );<br />

err += ( p1.y - p2.y ) * ( p1.y - p2.y );<br />

err += ( p1.z - p2.z ) * ( p1.z - p2.z );<br />

if( maxerr < err *_1_w * _1_h )<br />

{<br />

break;<br />

}<br />

... 以下同じ ...<br />

前スケールの探索時における<br />

SSDが最小値より大きい場合<br />

は計算を打ち切り<br />

86


Parallel Reductionによる最小値探索<br />

GPU上のメモリから最小値を計算<br />

GPUのスレッドが並列に値の比較処理を実行<br />

GPU上のデータ<br />

11 4 27 25 5 13 6 9 20 12 14 7 2 19 3 15<br />

4 25 5 6 12 7 2 3<br />

4 5 7 2<br />

4 2<br />

2<br />

2<br />

同期<br />

同期<br />

同期<br />

87


Parallel Reductionによる最小値探索<br />

GPU上のメモリから最小値を計算<br />

GPUのスレッドが並列に値の比較処理を実行<br />

Thrustライブラリの reduce アルゴリズムを利用<br />

thrust::reduce( 先頭, 末尾, 初期値, thrust::minimum( ) );<br />

先頭を指すイテレータ<br />

末尾を指すイテレータ<br />

GPU上のデータ<br />

11 4 27 25 5 13 6 9 20 12 14 7 2 19 3 15<br />

4 25 5 6 12 7 2 3<br />

4 5 7 2<br />

4 2<br />

2<br />

88<br />

同期<br />

同期<br />

同期<br />

2


CPUによる類似度計算の制御<br />

// スケール&誤差を保持するGPU側のメモリ領域<br />

thrust::device_vector< float > derror( img.size( ), 1.0e10f );<br />

thrust::device_vector< float > dscale( img.size( ), 0 );<br />

float s = MIN_SCALE, maxerr = 1.0e10f;<br />

while( s ( thrust::raw_pointer_cast( &derror[ 0 ] ),<br />

thrust::raw_pointer_cast( &dscale[ 0 ] ),<br />

W, H, W - w, H - h, w, h, s, maxerr );<br />

cudaDeviceSynchronize( ); // 処理が完了するまで待機する<br />

// 誤差の最大値を取得する<br />

maxerr = thrust::reduce( derror.begin( ), derror.end( ), 1.0e10f, thrust::minimum< float >( ) );<br />

s *= SCALE_FACTOR; // スケールを変更する<br />

// GPUの処理結果を転送する<br />

...<br />

Trustライブラリを用いてGPUを<br />

活用して誤差の最小値を計算<br />

89


計算時間の比較(1)<br />

CPUとGPUで計算時間を評価<br />

使用計算機<br />

CPU: Intel Core i7-X980(3.33GHz)<br />

OpenMPを使用して6スレッドで並列計算<br />

GPU: NVIDIA GeForce GTX480<br />

マルチプロセッサ数: 15(SP数= 15×32 = 480)<br />

Memory:1.5 GB<br />

OS:Windows 7 SP1<br />

90


計算時間の比較(2)<br />

実験パラメータ<br />

入力画像 :800×600画素<br />

テンプレート:105×135画素<br />

スケール :0.3~1.9倍(拡大率1.2)<br />

実験結果<br />

CPU: 8.700 sec.<br />

GPU: 0.258 sec.<br />

約 33.7 倍の高速化<br />

結果画像<br />

テンプレート<br />

91


まとめ(テンプレートマッチング)<br />

類似度計算の打ち切り処理により高速化<br />

Parallel Reductionアルゴリズムを利用<br />

CPUとGPUで計算時間を比較<br />

類似度の打ち切り<br />

なし あり<br />

CPU 77.3 秒 8.7 秒<br />

GPU 2.2 秒 0.258 秒<br />

92


~ガウシアンフィルタ~<br />

93


ガウシアンフィルタに挑戦<br />

94


空間フィルタリング(線形フィルタ)<br />

線形フィルタとは?<br />

i, j<br />

hx,<br />

y<br />

f i x j y<br />

画像処理の基本的な処理<br />

ガウシアンフィルタ<br />

LoGフィルタ<br />

ガボールフィルタ<br />

他<br />

h<br />

w<br />

<br />

g ,<br />

出力画像<br />

y<br />

h x<br />

w<br />

フィルタ<br />

入力画像<br />

フィルタh(i,j)<br />

積和<br />

出力g(i,j)<br />

周辺領域<br />

注目画素 f(i,j)<br />

入力画像<br />

95


ガウシアンフィルタ<br />

フィルタ係数に2次元ガウス分布を利用<br />

h<br />

問題点<br />

2<br />

1 x y<br />

2<br />

2<br />

2<br />

<br />

x,<br />

y exp <br />

フィルタ半径が大きくなるにつれ計算コスト大<br />

フィルタを1次元ガウス分布の積に分解<br />

h<br />

2<br />

<br />

<br />

2 2<br />

2<br />

2<br />

1 x y 1 x y<br />

<br />

<br />

2<br />

2<br />

2<br />

2<br />

2<br />

2<br />

2<br />

2<br />

<br />

x,<br />

y exp <br />

exp exp<br />

<br />

h<br />

2 w<br />

2<br />

1 y x <br />

g , <br />

<br />

,<br />

2<br />

2<br />

2<br />

y<br />

h 2<br />

x<br />

w 2<br />

<br />

i j<br />

exp exp f i x j y<br />

Y軸方向への1次元ガウシアンフィルタ<br />

<br />

<br />

96<br />

X軸方向への1次元ガウシアンフィルタ


ガウシアンフィルタの特徴<br />

フィルタ出力は各画素で独立に計算可能<br />

GPUによる並列計算が可能<br />

フィルタ係数の算出<br />

フィルタ適用前に事前計算が可能<br />

フィルタ適用中は常に同じ値を参照<br />

コンスタントメモリを利用<br />

注目画素の周辺領域(1次元)にアクセス<br />

1次元テクスチャのキャッシュ機能を利用<br />

97


コンスタントメモリとは?<br />

GPU上に実装されている特殊なメモリ領域<br />

読み取り専用<br />

マルチプロセッサ1基あたり 64 KB<br />

キャッシュを利用した高速なアクセスが可能<br />

レジスタとほぼ同じ速度でアクセス可能<br />

コンスタントメモリの定義<br />

__constant__ float coeff[ 512 ];<br />

C言語の配列のようにアクセス可能<br />

98


処理の流れ<br />

GPU上に2つの1次元配列を確保<br />

X,Y軸方向の処理時に入力と出力を入れ替え<br />

GPU内でのメモリ転送コストを削減<br />

99<br />

画<br />

像<br />

読<br />

み<br />

込<br />

み<br />

CPU<br />

メ<br />

モ<br />

リ<br />

確<br />

保<br />

GPU<br />

(<br />

X<br />

軸<br />

)<br />

ガ<br />

ウ<br />

シ<br />

ア<br />

ン<br />

フ<br />

ィ<br />

ル<br />

タ<br />

GPU<br />

ス<br />

レ<br />

ッ<br />

ド<br />

同<br />

期<br />

CPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

CPU GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU CPU<br />

(<br />

Y<br />

軸<br />

)<br />

ガ<br />

ウ<br />

シ<br />

ア<br />

ン<br />

フ<br />

ィ<br />

ル<br />

タ<br />

GPU<br />

メモリ1<br />

メモリ2 メモリ2<br />

入力画像


メモリ確保と転送(1)<br />

入力画像のメモリをGPU上に確保<br />

テクスチャの定義<br />

texture< float4, 1 > imgTexX;<br />

texture< float4, 1 > imgTexY;<br />

GPU上に1次元配列を確保<br />

cudaMalloc( ( void ** )&iData, nBytes );<br />

cudaMalloc( ( void ** )&oData, nBytes );<br />

CPUからGPUへメモリ転送<br />

cudaMemcpy( oData, pSrc, nBytes, cudaMemcpyHostToDevice );<br />

1次元テクスチャにマッピング<br />

cudaBindTexture( 0, imgTexX, oData );<br />

cudaBindTexture( 0, imgTexY, iData );<br />

100


メモリ確保と転送(2)<br />

フィルタ係数をコンスタントメモリに確保<br />

コンスタントメモリの定義<br />

__constant__ float coeff[ 512 ];<br />

コンスタントメモリを表す修飾子<br />

CPUからGPUへメモリ転送<br />

サイズ指定が必要<br />

cudaMemcpyToSymbol( coeff, pSrc, 512 * sizeof( float ) );<br />

101


計算時間の比較(1)<br />

CPUとGPUで計算時間を評価<br />

使用計算機<br />

CPU: Intel Core i7-X980(3.33GHz)<br />

OpenMPを使用して6スレッドで並列計算<br />

GPU: NVIDIA Geforce GTX480<br />

マルチプロセッサ数: 15(SP数 = 15×32 = 480)<br />

Memory:1.5 GB<br />

OS:Windows 7 SP1<br />

実験パラメータ<br />

画像サイズ:100 2 ~ 3000 2 画素<br />

σ:5.0,10.0<br />

102


計算時間の比較(2)<br />

400<br />

350<br />

300<br />

250<br />

200<br />

150<br />

100<br />

50<br />

[msec.]<br />

計算時間(N 2 = 2000 2 の場合)<br />

・CPU(σ=5) : 105.2 msec.<br />

・GPU(σ=5) : 20.9 msec. (×5.0)<br />

・CPU(σ=10) : 167.7 msec.<br />

・GPU(σ=10) : 30.7 msec. (×5.5)<br />

0<br />

2002 400 2 600 2 800 2 1000 2 1200 2 1400 2 1600 2 1800 2 2000 2 2200 2 2400 2 2600 2 2800 2 3000 2<br />

画像サイズ N 2<br />

103<br />

CPU(σ=10)<br />

CPU(σ=5)<br />

GPU(σ=10)<br />

GPU(σ=5)


まとめ(ガウシアンフィルタ)<br />

2次元ガウス分布を1次元ガウス分布の積で表現<br />

ガウシアンフィルタを1次元フィルタに分解<br />

コンスタントメモリを利用した高速化<br />

キャッシュを利用したメモリアクセスの高速化<br />

同じ値をスレッド間で共有する場合に有効<br />

CPUとGPUで計算時間を比較<br />

画像サイズが 2000 2 の場合(σ=5)<br />

CPU: 105.2 msec.<br />

GPU: 20.9 msec.<br />

約 5 倍の高速化を実現<br />

104


~SIFT特徴量の計算~<br />

105


SIFT(Scale Invariant Feature Transform)<br />

回転・スケール変化等に頑健な特徴点の検出<br />

画像間のマッチングや物体認識・検出に利用<br />

1. David G. Lowe, “Distinctive image features from scale-invariant keypoints,”<br />

International Journal of Computer Vision, 60, 2, pp. 91-110, 2004.<br />

2. 藤吉弘亘. "Gradientベースの特徴抽出 - SIFTとHOG - ", 情報処理学会 研究報告<br />

CVIM 160, pp. 211-224, 2007.<br />

106<br />

1,2


SIFTのアルゴリズム(1)<br />

DoG画像の作成<br />

(Difference-of-Gaussian)<br />

特徴点の検出<br />

エッジ上の点を削除<br />

サブピクセル位置推定<br />

3<br />

k <br />

2<br />

k<br />

<br />

コントラストの<br />

DoG2<br />

小さい点を削除 DoG1<br />

107<br />

異なるスケールの平滑化画像の差分<br />

(DoG)を計算<br />

k<br />

<br />

周辺26画素に対して極値をとる位置を<br />

特徴点として検出<br />

DoG3<br />

DoG3<br />

DoG2<br />

DoG1


SIFTのアルゴリズム(2)<br />

DoG画像の作成<br />

(Difference-of-Gaussian)<br />

特徴点の検出<br />

エッジ上の点を削除<br />

サブピクセル位置推定<br />

コントラストの<br />

小さい点を削除<br />

108


SIFTのアルゴリズム(3)<br />

オリエンテーションの<br />

算出<br />

特徴ベクトルを算出<br />

周辺領域の勾配方向と強度から<br />

オリエンテーションを算出<br />

109


デモ( SIFT )<br />

110


SIFTの実装方法<br />

さまざまなスケールにおけるDoG計算<br />

高速なガウシアンフィルタを利用<br />

キーポイント検出<br />

各画素で独立に判定可能<br />

GPUによる並列計算が可能<br />

判定に26近傍の画素値が必要<br />

3次元テクスチャを利用できるか?<br />

テクスチャサイズの制限から利用は困難<br />

2次元テクスチャで代用<br />

オリエンテーションの算出<br />

各キーポイントで独立に計算可能<br />

111


処理の流れ<br />

112<br />

画<br />

像<br />

読<br />

み<br />

込<br />

み<br />

CPU<br />

D<br />

o<br />

G<br />

計<br />

算<br />

GPU<br />

ス<br />

レ<br />

ッ<br />

ド<br />

同<br />

期<br />

CPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

CPU GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU CPU<br />

キ<br />

ー<br />

ポ<br />

イ<br />

ン<br />

ト<br />

検<br />

出<br />

GPU<br />

キーポイント<br />

DoG画像 オリエンテーション<br />

入力画像<br />

キ<br />

ー<br />

ポ<br />

イ<br />

ン<br />

ト<br />

リ<br />

ス<br />

ト<br />

作<br />

成<br />

CPU<br />

オ<br />

リ<br />

エ<br />

ン<br />

テ<br />

ー<br />

シ<br />

ョ<br />

ン<br />

算<br />

出<br />

GPU<br />

デ<br />

ー<br />

タ<br />

転<br />

送<br />

GPU CPU


SIFT計算におけるメモリ配置<br />

複数スケールのDoG画像をテクスチャ1枚に配置<br />

ミップマップ ※ を構築<br />

DoG画像の出力先をソフトウェア的に調整<br />

1<br />

1<br />

1<br />

2<br />

1<br />

4<br />

1<br />

8<br />

・・・<br />

・・・<br />

113<br />

※CUDAは未サポート


計算時間の比較(1)<br />

CPUとGPUで計算時間を評価<br />

SIFT特徴量としてオリエンテーションを計算<br />

使用計算機<br />

CPU: Intel Core2 Quad Q9550(2.83 GHz)<br />

4スレッドで並列計算<br />

GPU: NVIDIA GeForce GTX280<br />

マルチプロセッサ数: 30(SP数= 30×8 = 240)<br />

Memory:1.0 GB<br />

OS:Windows Vista SP1<br />

実験パラメータ<br />

画像サイズ:50 2 ~ 1000 2 画素<br />

σ 0:1.6,分割数:3<br />

114


計算時間の比較(2)<br />

2100<br />

2000<br />

1900<br />

1800<br />

1700<br />

1600<br />

1500<br />

1400<br />

1300<br />

1200<br />

1100<br />

1000<br />

900<br />

800<br />

700<br />

600<br />

500<br />

400<br />

300<br />

200<br />

100<br />

[msec.]<br />

計算時間(N 2 = 600 2 の場合)<br />

・CPU : 458.0 msec.<br />

・GPU : 10.2 msec. (×44.9)<br />

0<br />

50 2 100 2 150 2 200 2 250 2 300 2 350 2 400 2 450 2 500 2 550 2 600 2 650 2 700 2 750 2 800 2 850 2 900 2 950 2 10002<br />

画像サイズ N 2<br />

GPU<br />

CPU<br />

115


デモ(SIFT)<br />

116


まとめ(SIFT)<br />

テクスチャメモリとコンスタントメモリの利用<br />

キャッシュを利用したメモリアクセスの高速化<br />

ミップマップを構築<br />

複数スケールのDoG画像を1枚のテクスチャに格納<br />

CPUとGPUで計算時間を比較<br />

N 2 = 600 2 の場合<br />

CPU: 458.0 msec.<br />

GPU: 10.2 msec.<br />

約 44 倍の高速化を実現<br />

117


~GPGPUによる高速画像処理に挑戦して~<br />

118


GPGPUへの挑戦を終えて<br />

CUDAを利用することで容易にGPGPUが可能<br />

スレッドプログラミングの経験があれば非常に簡単<br />

既存プログラムの移植も比較的容易<br />

Thrustライブラリによる簡単なGPGPU<br />

GPUを意識せずにプログラミングが可能<br />

GPUを使うと10倍以上の高速化が可能?<br />

多くの画像処理アルゴリズムは高速化が可能<br />

空間フィルタリング,局所特徴量計算,他<br />

逐次型の画像処理アルゴリズムは高速化が困難<br />

ラベリング,細線化,他<br />

119


GPGPUの問題点と今後の展望<br />

複数GPUの利用<br />

各GPU上で別々に処理を実行<br />

CUDA 4.0 は単一CPUスレッドから複数GPUを利用可能<br />

CPUとの連携<br />

GPUの苦手な処理をCPUで計算<br />

CPUとGPUの役割分担が重要<br />

高性能なGPUが登場<br />

リアルタイム画像処理(大規模計算)への挑戦<br />

GPGPU開発環境の標準化(OpenCL)<br />

120


参考文献<br />

[1] M. J. Harris, G. Coombe, T. Scheuermann, and A. Lastra, “Physically-<br />

Based Visual Simulation on Graphics Hardware,” Proceedings of<br />

SIGGRAPH 2002 / Eurographics Workshop on Graphics Hardware 2002,<br />

pp.1-10, 2002.(GPGPUの起源が書かれている論文)<br />

[2] J. D. Owens, D. Luebke, N. Govindaraju, M. Harris, J. Krüger, A. E.<br />

Lefohn, and T. J. Purcell, “A Survey of General-Purpose Computation on<br />

Graphics Hardware,” Computer Graphics Forum, Vol.26, No.1, pp.80-<br />

113, 2007.(最近のGPGPUが詳しく述べられている論文)<br />

[3] “GPGPU,” http://gpgpu.org/<br />

[4] “CUDA ZONE,” http://www.nvidia.com/object/cuda_home.html<br />

[5] “CUDA Programming Guide,”<br />

http://www.nvidia.com/object/cuda_develop.html<br />

[6] “OpenCL,” http://www.khronos.org/opencl/<br />

121


MIST(Media Integration Standard Toolkit)<br />

複数メディアを扱うためのライブラリ<br />

音声・画像処理のアルゴリズムを多数実装<br />

C/C++を用いた高速な処理を実現<br />

C++のテンプレートを用いた汎用的な実装<br />

複数のプラットフォームで動作<br />

充実した日本語チュートリアルを用意<br />

オープンソースとして公開中<br />

BSDスタイルのライセンス<br />

商用の製品開発でも利用可能<br />

http://mist.murase.m.is.nagoya-u.ac.jp/<br />

122


~Visual Studio 2010 の詳細設定~<br />

※CUDA Toolkit 4.0 以降<br />

123


Visual Studio 2010 の簡易設定(1)<br />

“.cu” ファイルの簡易コンパイル設定<br />

プロジェクトメニューのビルドのカスタマイズを表示<br />

CUDA 4.0を選択<br />

1.これを表示<br />

※ .cu ファイルの追加前に行う<br />

2.これを選択<br />

124


Visual Studio 2010 の簡易設定(2)<br />

“.cu” ファイルのコンパイルオプション<br />

“.cu” ファイルのプロパティを表示<br />

NVCCのコンパイルオプションをGUIで調整可能<br />

これを表示<br />

125


Visual Studio 2010 の簡易設定(3)<br />

「プロジェクト」 「プロパティ」を選択<br />

「構成プロパティ」 「リンカー」を選択<br />

追加の依存ファイル<br />

“cudart.lib” を指定<br />

ここに入力<br />

126


Visual Studio 2010 の簡易設定(3)<br />

プログラムのコンパイルと実行<br />

Visual Studio の「ビルド」を実行<br />

実行ファイルが作成されることを確認<br />

「デバッグなしで開始」<br />

コマンドラインに “Hello World!!” が表示される<br />

Hello World!!<br />

続行するには何かキーを押してください . . .<br />

127<br />

※本講演のプログラムを実行した場合


~補足資料~<br />

128


FLOPS<br />

FLoating point number Operations Per Second<br />

FLOPSやFLOP/s と表記される<br />

1秒あたりに実行可能な浮動小数点演算回数<br />

スーパーコンピュータ等の性能を表す指標<br />

代表的なCPU/GPUのFLOPS<br />

Core i7-965<br />

51.20 GFLOPS<br />

GeforceGTX580<br />

1.58 TFLOPS<br />

RadeonHD5870<br />

2.72 TFLOPS<br />

129<br />

※ GPUは積和演算の性能


CUDAにおけるエラー処理<br />

API関数(メモリ確保,他)の場合<br />

各API関数の戻り値を評価<br />

cudaSuccess → 実行に成功<br />

GPUで実行する関数の場合<br />

cudaThreadSynchronize( )により同期<br />

cudaGetLastError( ) の戻り値を評価<br />

cudaSuccess → 実行に成功<br />

130


~OpenCV + CUDA~<br />

131


デモ(特徴点検出&対応付け)<br />

132


OpenCVをダウンロード<br />

Subversion経由で最新版を入手<br />

https://code.ros.org/svn/opencv/trunk/opencv<br />

Cmakeをダウンロード<br />

http://www.cmake.org/<br />

133


OpenCVライブラリのビルド<br />

CUDAを有効にする<br />

Cmakeの設定で「WITH_CUDA」にチェック<br />

Configureを実行<br />

ビルド設定を反映<br />

Generateを実行<br />

ビルドファイルを生成<br />

3.ビルドファイルの生成<br />

2.設定を反映<br />

1.ここにチェック<br />

Cmakeの設定画面<br />

134


CPU/GPUのコードの違い<br />

SURFを用いた特徴点検出&マッチング(CPU)<br />

#include <br />

...<br />

cv::Mat_< float > desc2;<br />

cv::SurfFeatureDetector detector2( th2 );<br />

// 特徴点を検出<br />

detector2.detect( frame_gray, keys2 );<br />

// 特徴量を計算<br />

cv::SurfDescriptorExtractor extractor;<br />

extractor.compute( frame_gray, keys2, desc2 );<br />

// 特徴点を対応付け<br />

cv::BruteForceMatcher< cv::L2< float > > matcher;<br />

matcher.match( desc1, desc2, matches );<br />

135


CPU/GPUのコードの違い<br />

SURFを用いた特徴点検出&マッチング(GPU)<br />

#include <br />

...<br />

cv::gpu::GpuMat desc_gpu2;<br />

cv::gpu::SURF_GPU detector2;<br />

detector2.hessianThreshold = th2;<br />

// データをGPUへ転送<br />

cv::gpu::GpuMat frame_gpu;<br />

frame_gpu.upload( frame_gray );<br />

// 特徴点検出&特徴量計算<br />

detector2( frame_gpu, cv::gpu::GpuMat(), keys2, desc_gpu2 );<br />

// 特徴点を対応付け<br />

cv::gpu::BruteForceMatcher_GPU< cv::L2< float > > matcher;<br />

matcher.match( desc_gpu1, desc_gpu2, matches );<br />

136


OpenCVでGPUを利用できる機能<br />

行列演算<br />

テンプレートマッチング<br />

歩行者検出(HOG)<br />

特徴点検出&特徴量(SURF)<br />

特徴点対応付け<br />

画像フィルタ<br />

ラプラシアン,ソーベル,ガウシアン,他<br />

カメラキャリブレーション<br />

・・・<br />

137

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!