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