137
GPGPUによる高速画像処理 ~リアルタイム画像処理への挑戦~ 名古屋大学大学院情報科学研究科 出口 大輔

GPGPUによる高速画像処理 - NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

  • Upload
    lyhanh

  • View
    251

  • Download
    10

Embed Size (px)

Citation preview

Page 1: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUによる高速画像処理 ~リアルタイム画像処理への挑戦~

名古屋大学大学院情報科学研究科

出口 大輔

Page 2: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

リアルタイム画像処理

2

Page 3: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

発表の流れ

GPGPUを始める前に GPGPUの基礎知識

CUDAの使い方 CUDAを使う前に

プログラミングの予備知識

CUDAを使って“Hello World”

GPGPUにチャレンジ 行列積の計算

テンプレートマッチング

ガウシアンフィルタ

SIFT特徴量の計算

3

Page 4: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~GPGPUって何?~

4

Page 5: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUって?

GPGPUは何の略?

General-Purpose computation on GPUs

GPUを汎用計算に利用しようという試み

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

どうしてGPGPUが注目されているのか?

5

Page 6: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

0

200

400

600

800

1000

1200

1400

1600

1900 1900 1900 1900 1900 1900 1900 1900 1900

CPUとGPUの性能比較

6

2003 2004 2005 2006 2008

※NVIDIA CUDA Programming Guide 4.0より引用

[GFLOP/s]

ピーク性能

NVIDIA GPU

Intel

CPU

Geforece GTX280

Geforece 8800GTX

Quad Core

Xeon 3.2GHz Core2 Duo

3.0GHz

2007 2010 2009

Geforece GTX480

Page 7: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUって?

GPGPUは何の略?

General-Purpose computation on GPUs

GPUを汎用計算に利用しようという試み

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

どうしてGPGPUが注目されているのか?

GPUの計算性能が飛躍的に向上

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

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

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

手頃な価格で入手可能

GeForce GTX580 は約5万円で購入可能

7

Page 8: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUの活用例

動画像処理 CyberLink PowerDirector 7 ビデオエフェクトのレンダリングを高速化

TMPGEnc 4.0 XPress フィルター処理・デコード処理の高速化

画像処理 Adobe Photoshop CS4 / CS5,Adobe Premire CS5 各種フィルタ処理の高速化

OpenCV 各種画像処理の高速化

数値計算 MATLAB FFTの高速化

8

Page 9: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUの歴史: 1999年以前

1970年 ~ 1990年 初期開発の時代

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

プログラム可能なGPUに関する初期研究 Ikonas System [1], Pixel Machine [2]

1990年 ~ 1999年 GPU技術の黎明期

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

グラフィックス向けライブラリの開発 OpenGL(1992年~), DirectX(1995年~)

9

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

objects,” Proceedings of SIGGRAPH 78, pp.336-340. 1978

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

Computer,” Proceedings of SIGGRAPH89, pp.69-78, 1989

Page 10: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUの歴史: 1999年~

“GPU”の誕生

NVIDIA社の GeForce 256 の登場

ハードウェア T&L※をサポート

CPU負荷を大幅に削減

グラフィックスパイプライン

ハードウェア固定の処理

自由表現な表現は不可

10

頂点処理

(ハードウェア固定)

ジオメトリ処理

(クリッピング等)

ラスタライズ処理

画面出力 ※Transform & Lighting

ピクセル処理

(ハードウェア固定)

Page 11: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUの歴史: 2003年~

プログラマブルシェーダの登場

Vertex Shader

頂点座標の変換処理

Pixel Shader

画素の輝度計算処理

シェーダ言語の進化

アセンブラから高級言語へ

Cg, HLSL, GLSL

柔軟な映像表現が可能に

グラフィックス以外への応用

GPGPUへの関心が高まる

11

Vertex Shader

(プログラマブル)

ジオメトリ処理

(クリッピング等)

ラスタライズ処理

画面出力

Pixel Shader

(プログラマブル)

Page 12: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUの歴史: 2007年~

GPGPUの開発環境の整備 CUDA (NVIDIA)

ATI Stream (AMD)

OpenCL

DirectCompute

GPGPU時代の到来 物理シミュレーション

数値計算

信号解析

画像処理・認識

・・・

12

Vertex Shader

(プログラマブル)

Geometry Shader

(プログラマブル)

ラスタライズ処理

画面出力

Pixel Shader

(プログラマブル)

統合型シェーダ

Page 13: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUの歴史: 2009年~

Fermiアーキテクチャの登場

汎用計算向けのアーキテクチャ※

L1/L2キャッシュの搭載

複数カーネルの同時実行のサポート

倍精度浮動小数点演算の高速化

ECCメモリのサポート

アトミックなメモリ操作の高速化

C++のフルサポート

GPGPU関連のライブラリ&ツールの充実

CUBLAS, CUFFT, Thrust, NPP

Parallel Nsight, Visual Profiler

13

※ビデオカードによって

サポート状況は異なる

Page 14: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~CUDAを使う前に~

14

Page 15: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAって何?

CUDA(Compute Unified Device Architecture)

発音は「クーダ」もしくは「キューダ」

NVIDIA社が提供するGPUを利用する ための統合開発環境

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

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

CPUでプログラムを実行する感覚

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

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

15

Page 16: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAが利用可能な環境

CUDA対応のグラフィックスカード

OSの対応状況(CUDA 4.0) Windows XP(32bit, 64bit)

Windows Vista(32bit, 64bit)

Windows Server 2008(32bit, 64bit)

Linux(32bit, 64bit)

Mac OS

16

GeForce GTX 580, GTX 480, GTX 260, 8800シリーズ,他

Quadro Plex 2200 D2, FX 5800, FX 5600, 5000, 6000, 他

Tesla S2050, C2070, S1070, C1060, S870, D870, C870

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

Page 17: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使うための準備(Windows編)

「CUDA ZONE」から次をダウンロード

NVIDIA Driver

CUDA対応のビデオドライバー

CUDA Toolkit 4.0 ※

コンパイラ(nvcc)

CUBLASやCUFFT

ライブラリ

ドキュメント

CUDA SDK 4.0

サンプル

※ 2011年5月25日にリリース

17

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

Page 18: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを動かしてみよう!!

18

Volumetric Particle Shadows

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

Image Denoising

Page 19: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~プログラミングの予備知識~

19

Page 20: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDA対応のGPU(G80, GT200)

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

8 個のスカラープロセッサ(SP)

16KBの共有メモリ

スレッド間の同期機構

マルチプロセッサ内でのみ可能

マルチプロセッサ間での同期には CPU処理が必要

GeForce GTX280 の場合

30 基のマルチプロセッサを搭載

1GByte以上のグローバルメモリ

20

SP SP SP SP

SP SP SP SP

マルチプロセッサ #1

SP SP SP SP

SP SP SP SP

マルチプロセッサ #2

Page 21: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDA対応のGPU(Fermi)

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

32 個のスカラープロセッサ(SP)

48KBの共有メモリ

スレッド間の同期機構

マルチプロセッサ内でのみ可能

マルチプロセッサ間での同期には CPU処理が必要

GeForce GTX480 の場合

15 基のマルチプロセッサを搭載

1GByte以上のグローバルメモリ

21

SM #1 SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

SM #2 SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

SP SP SP SP SP SP SP SP

Page 22: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAのプログラミングモデル

GPUの特徴

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

GPU のみでプログラムを実行できない

CPUとの連携が不可欠

CUDAにおける計算の流れ

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

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

階層的にスレッドを管理

スレッドのまとまり = ブロック

ブロックのまとまり = グリッド

問題を分割して計算する際に便利

22

Page 23: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

スレッドを3次元的に配置

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

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

グリッド

ブロック(0,1,1)

ブロック(0,0,1)

スレッド

(0,0,1) スレッド

(0,1,1) スレッド

(0,2,1)

スレッド

(1,0,1) スレッド

(1,1,1) スレッド

(1,2,1)

ブロック(1,0,1)

スレッド

(0,0,1) スレッド

(0,1,1) スレッド

(0,2,1)

スレッド

(1,0,1) スレッド

(1,1,1) スレッド

(1,2,1)

ブロック(1,1,1) ブロック(0,1,0) ブロック(1,1,0)

ブロック(1,0,0)

スレッド

(0,0,0) スレッド

(0,1,0) スレッド

(0,2,0)

スレッド

(1,0,0) スレッド

(1,1,0) スレッド

(1,2,0)

スレッド

(0,0,0) スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

CUDAにおけるスレッド管理

23

ブロック(0,0,0)

スレッド

(0,0,0) スレッド

(0,1,0) スレッド

(0,2,0)

スレッド

(1,0,0) スレッド

(1,1,0) スレッド

(1,2,0)

スレッド

(0,0,0) スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

Page 24: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

階層的スレッドの利用方法

一般的な画像処理で利用する場合

ブロック内のスレッド数を決定

16×16 = 256 スレッド

画像内にブロックを配置

ブロック内のスレッド が各画素を処理

計算範囲の求め方

ブロックID

スレッドID

ブロック内スレッド数

24

T00 T10 TN0

T01

ブロック#1

T0M TNM

T00 T10 TN0

T01

T0M TNM

Page 25: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

スレッド

(0,0,0) スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

ブロック(0,0,0)

CUDAにおける計算の流れ

25

ブロック(1,0,0)

ブロック(0,1,0) ブロック(1,1,0)

処理1

グリッド 1

ブロック (0,0,0) ブロック (1,0,0)

グリッド 2 処理2

スレッド

(0,0,0) スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

Page 26: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAのメモリモデル

26

共有メモリ

スレッド (0,0,0)

ローカルメモリ

レジスタ

スレッド(1,0,0)

ブロック (0,0,0)

共有メモリ

スレッド(0,0,0) スレッド(1,0,0)

ブロック(1,0,0)

コンスタントメモリ

テクスチャメモリ

グローバルメモリ

ローカルメモリ

レジスタ

ローカルメモリ

レジスタ

ローカルメモリ

レジスタ

Page 27: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAで利用可能なメモリ

グローバルメモリ 大量のメモリを利用可能

低速なメモリアクセス(400~600クロック必要) Fermiアーキテクチャではキャッシュ機構(L1/L2)を搭載

共有メモリ レジスタと同じ速度でアクセス可能

マルチプロセッサ1基あたり16KB(Fermiは最大48KB) Fermiの場合は一部をL1キャッシュとして利用可能

テクスチャメモリ キャッシュ機構による高速なアクセスが可能

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

コンスタントメモリ キャッシュ機構による高速なアクセスが可能

マルチプロセッサ1基あたり64KB

27

Page 28: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

各GPUで使用できる機能の違い

使用できる機能を Compute Capability で区別

現在 1.0 ~ 2.1 のGPUが存在

Compute Capability による機能の違い

1.0:初期リリース

GeForce 8800GTX, Quadro FX 5600, 他

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

GeForce 9800 GTX, Quadro FX 3700, 他

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

GeForce GTX 280, Quadro FX 5800, 他

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

GeForce GTX480,Quadro 5000,他

28

Page 29: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Compute Capabilityによる機能の違い

29

Compute Capability

1.0 1.1 1.2 1.3 2.x

3次元グリッド × ○

倍精度浮動小数 × ○

32ビット整数の

アトミック演算 × ○

64ビット整数の

アトミック演算 × ○

単精度浮動小数の

アトミック演算 × ○

Page 30: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAにおける制限事項

30

Compute Capability

1.0 1.1 1.2 1.3 2.x

1ブロックあたりのスレッド数

512 1024

1マルチプロセッサあたりのスレッド数

768 1024 1536

1マルチプロセッサあたりのレジスタ数

8192 16384 32768

コンスタントメモリ 64KB

共有メモリ 16KB 48KB

2Dテクスチャ 216×215 216×216

Page 31: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~ CUDAを使って“Hello World”~

31

Page 32: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使って“Hello World!!”

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

32

__global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { char *dData, hData[ 15 ]; cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( dData ); cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost ); printf( "%s", hData ); cudaFree( dData ); return( 0 ); }

Page 33: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使って“Hello World!!”

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

33

__global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { char *dData, hData[ 15 ]; cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( dData ); cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost ); printf( "%s", hData ); cudaFree( dData ); return( 0 ); }

_ _global_ _, threadIdx

dim3, <<<…>>>

CUDAで拡張された部分

Page 34: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使って“Hello World!!”

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

34

__global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { char *dData, hData[ 15 ]; cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( dData ); cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost ); printf( "%s", hData ); cudaFree( dData ); return( 0 ); }

Page 35: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAの言語拡張(1)

言語拡張により追加された修飾子

関数に対する修飾子

変数に対する修飾子

35

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

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

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

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

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

_ _device_ _ GPU 上のメモリに存在する変数

Page 36: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使って“Hello World!!”

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

36

__global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { char *dData, hData[ 15 ]; cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( dData ); cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost ); printf( "%s", hData ); cudaFree( dData ); return( 0 ); }

Page 37: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAの言語拡張(2)

CUDAで利用可能な組み込み型

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

37

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

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

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

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

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

gridDim グリッドの次数

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

blockDim スレッドが属するブロックの次数

threadIdx ブロック内のスレッドのインデックス

Page 38: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

gridDim

= 2×2×2

スレッドを識別する組み込み変数

38

グリッド

ブロック(0,1,1)

ブロック(1,0,1)

スレッド

(0,0,1) スレッド

(0,1,1) スレッド

(0,2,1)

スレッド

(1,0,1) スレッド

(1,1,1) スレッド

(1,2,1)

ブロック(1,1,1) ブロック(0,1,0) ブロック(1,1,0)

ブロック(1,0,0)

スレッド

(0,0,0) スレッド

(0,1,0) スレッド

(0,2,0)

スレッド

(1,0,0) スレッド

(1,1,0) スレッド

(1,2,0)

スレッド

(0,0,0) スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

ブロック(0,0,1)

スレッド

(0,0,1) スレッド

(0,1,1) スレッド

(0,2,1)

スレッド

(1,0,1) スレッド

(1,1,1) スレッド

(1,2,1)

ブロック(0,0,0)

blockIdx

blockDim

= 3×2×2 スレッド

(0,0,0) スレッド

(0,1,0) スレッド

(0,2,0)

スレッド

(1,0,0) スレッド

(1,1,0) スレッド

(1,2,0)

スレッド

(1,0,0) スレッド

(2,0,0)

スレッド

(0,1,0) スレッド

(1,1,0) スレッド

(2,1,0)

スレッド

(0,0,0)

threadIdx

Page 39: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

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

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

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

GPUで実行する場合は “__global__” を付与

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

CPUとGPU間でメモリ転送が必要

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

関数名<<< ブロック数, スレッド数 >>>( … );

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

問題サイズに合わせて調整可能

39

Page 40: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAを使って“Hello World!!”

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

40

__global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { char *dData, hData[ 15 ]; cudaMalloc( ( void ** )&dData, sizeof( char ) * 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( dData ); cudaMemcpy( hData, dData, sizeof( char ) * 15, cudaMemcpyDeviceToHost ); printf( "%s", hData ); cudaFree( dData ); return( 0 ); }

GPU上で実行される関数

GPU上のメモリを解放

CPUからGPUへメモリ転送

GPU上にメモリを確保

ブロック内に配置する スレッド数 15×1 = 15

総ブロック数 1×1 = 1

Page 41: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

スレッドのレジスタ数を調査

Visual Studio 2008 コマンドプロンプトを起動

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

合計レジスタ数をチェック

41

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

main.cu

ptxas info : Compiling entry function '_Z5helloPc' for 'sm_10'

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

レジスタ数

2 × 15 = 30

レジスタ数 生成スレッド数 合計

Page 42: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~Thrustライブラリの使い方~

42

Page 43: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Thrustライブラリって?

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

CUDAとOpenMPに対応したC++ STLの並列版

URL: http://thrust.googlecode.com/

Thrustライブラリの特徴

コンテナ

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

アルゴリズム

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

直感的なインターフェース

CUDAの複雑なAPIに関する知識は不要

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

43

Page 44: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Thrustライブラリの使い方

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

44

#include <thrust/host_vector.h>

#include <thrust/device_vector.h>

#include <thrust/sequence.h>

#include <thrust/reduce.h>

int main( int argc, char *argv[] )

{

// CPU上のメモリを確保

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

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

thrust::sequence( hvec.begin( ), hvec.end( ), 1 );

// GPU上のメモリを確保

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

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

dvec = hvec;

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

printf( "%d¥n", val );

return( 0 );

}

Page 45: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

コンテナの使い方(1)

メモリの確保

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

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

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

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

メモリの解放

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

CPUの場合:free( )

GPUの場合:cudaFree( )

明示的なメモリ解放:dvec.clear( )

45

Page 46: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

コンテナの使い方(2)

メモリの転送

代入操作でメモリ転送が可能

コンテナの各要素へのアクセス

配列と同様にアクセス可能

46

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

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

dvec = hvec;

hvec[ 5 ] = 100.0f;

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

Page 47: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

イテレータ(1)

コンテナの要素を指すポインタ

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

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

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

47

0 1 2 3

dvec.begin( ) dvec.end( )

dvec.begin( ) + 3

dvec

Page 48: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

イテレータ(2)

コンテナの各要素へのアクセス

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

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

*ite = 10;

++ite;

*ite = 25;

48

dvec[ 0 ] = 10 と等価

dvec[ 1 ] = 25 と等価

Page 49: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

アルゴリズムと使用方法

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

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

アルゴリズムの使用例

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

thrust::fill( dvec.begin( ), dvec.end( ), 1 );

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

thrust::sequence( dvec.begin( ), dvec.end( ) );

49

1 1 1 1 1 1 1 1

1 2 3 4 5 6 7 N

Page 50: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Parallel Reduction(1)

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

GPUのスレッドが並列に処理を実行

例:総和計算の場合

50

36 1 2 3 4 5 6 7 8

GPU上のデータ

3 7 11 15

10 26

36

CUDAスレッドの同期

CUDAスレッドの同期

Page 51: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Parallel Reduction(2)

Thrustライブラリを用いた集約処理

総和の計算

int val = thrust::reduce( dvec.begin( ), dvec.end( ) );

最大値の計算

int val = thrust::reduce( dvec.begin( ), dvec.end( ),

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

51

1 2 3 4 5 6 7 10 55

1 10 3 8 5 9 7 6 10 2

Page 52: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAとThrustを組み合わせる

Hello World!! を表示するサンプル

52

#include <thrust/host_vector.h> #include <thrust/device_vector.h> __global__ void hello( char *data ) { char *text = "Hello World!!¥n"; data[ threadIdx.x ] = text[ threadIdx.x ]; } int main( int argc, char *argv[] ) { thrust::host_vector< char > hvec( 15 ); thrust::device_vector< char > dvec( 15 ); dim3 nThreads( 15, 1 ); dim3 nBlocks( 1, 1 ); hello<<< nBlocks, nThreads >>>( thrust::raw_pointer_cast( &dvec[ 0 ] ) ); hvec = dvec; printf( "%s", &hvec[ 0 ] ); return( 0 ); }

CPUからGPUへメモリ転送

GPU上のメモリを指すポインタを取得

Page 53: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

53

Page 54: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUにチャレンジ

行列積の計算 CUDAにおける基本的な実装方法

共有メモリ

テンプレートマッチング 2次元テクスチャメモリ

ハードウェア線形補間

正規化テクスチャ座標

Parallel Reduction アルゴリズム

ガウシアンフィルタ 1次元テクスチャメモリ

コンスタントメモリ

SIFT特徴量の計算 1・2次元テクスチャメモリ

コンスタントメモリ

54

Page 55: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~行列積の計算~

55

Page 56: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPUによる行列積の計算

行列積計算の特徴

各要素はすべて独立に計算可能

GPUによる並列計算が可能

同じメモリ領域へ頻繁にアクセス

メモリアクセス速度がボトルネック

GPU上での実装方法

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

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

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

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

56

Page 57: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

行列積C=A×Bの流れ

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

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

3. 行列Cの各要素cmnを計算

N 次正方行列を仮定

ただし,N は16の倍数

4. 計算結果をCPUに転送

57

N

k

knmkmn bac1

行列A 行列C

行列B

mnc

mka

knb

m行

n列

N

N

Page 58: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

処理の流れ

CPU側の処理

行列の初期化

GPU側の処理

行列Cの各要素を計算

58

行列の初期化

CPU

メモリ確保

GPU

行列積の計算

GPU

データ転送

CPU GPU

データ転送

GPU CPU

Page 59: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

int main( int argc, char *argv[] )

{

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

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

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

/* CPU側のメモリを確保*/

/* GPU側のメモリを確保*/

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

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

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

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

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

return( 0 );

}

行列積計算のCPU処理(ソース)

59

Page 60: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

行列積計算のCPU処理(1)

行列積の計算で使用する変数

CPUとGPUでメモリを確保

CPU:malloc, new, cudaMallocHost でメモリを確保

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

60

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

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

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

/* CPU側のメモリを確保 */

hA = ( float * )malloc( N * N * sizeof( float ) );

hB = ( float * )malloc( N * N * sizeof( float ) );

hC = ( float * )malloc( N * N * sizeof( float ) );

/* GPU側のメモリを確保 */

cudaMalloc( ( void ** )&dA, N * N * sizeof( float ) );

cudaMalloc( ( void ** )&dB, N * N * sizeof( float ) );

cudaMalloc( ( void ** )&dC, N * N * sizeof( float ) );

Page 61: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

行列積計算のCPU処理(2)

CPUとGPU間のメモリ転送

CPU→GPU

cudaMemcpy( dst, src, size, cudaMemcpyHostToDevice );

GPU→CPU

cudaMemcpy( dst, src, size, cudaMemcpyDeviceToHost );

CPUとGPUで確保したメモリの解放

CPU:free, delete, cudaFreeHost でメモリを解放

GPU:cudaFree でグローバルメモリを解放

61

転送先 転送バイト数

転送元 転送方向

この部分を変更

Page 62: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

行列積計算のCPU処理(3)

GPU上で処理を実行

計算範囲の設定

N 次正方行列を仮定

ただし N は16の倍数

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

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

合計M×M 個のブロックを配置

各スレッドが cmn を計算

n = threadIdx.x + blockIdx.x × blockDim.x;

m = threadIdx.y + blockIdx.y × blockDim.y;

GPU上で関数を実行

multiply<<< ブロック数, スレッド数 >>>( … );

62

行列C

16

16

(threadIdx.y, threadIdx.x)

(blockIdx.y, blockIdx.x)

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

Page 63: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

int main( int argc, char *argv[] )

{

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

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

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

/* CPU側のメモリを確保*/

/* GPU側のメモリを確保*/

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

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

dim3 nThreads( 16, 16 );

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

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

multiply<<< nBlocks, nThreads >>>( dA, dB, dC, N );

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

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

return( 0 );

}

行列積計算のCPU処理(まとめ)

63

GPU上で処理を実行

GPU上のメモリを指定

Page 64: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

実装方法(GPU#1)

行列積C=A×Bの各要素cmnを計算

要素cmnをGPU上で並列計算

64

__global__ void multiply1( float *dA, float *dB, float *dC, int N )

{

int n = threadIdx.x + blockIdx.x * blockDim.x;

int m = threadIdx.y + blockIdx.y * blockDim.y;

float sum = 0.0f;

for( int k = 0 ; k < N ; k++ )

{

sum += dA[ m + k * N ] * dB[ k + n * N ];

}

dC[ m + n * N ] = sum;

}

N

k

knmkmn bac1

行列A 行列C

行列B

mnc

mka

knb

m行

n列

N

N

Page 65: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

各要素cmnを部分行列(ブロック)の積で計算

ブロックサイズが16×16の場合

ブロック内で同じメモリを参照

高速な共有メモリを利用

グローバルメモリへの アクセスを削減

2×N×162 回

2×N×16 回

実装方法(GPU#2)

65

16

1

)1(16

16

N

t

t

tk

knmkmn bac

行列A 行列C

行列B

mnc

mka

knbN

N

16

16

16

16

16分の1

Page 66: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

実装方法(GPU#2)

66

__global__ void multiply2( float *dA, float *dB, float *dC, int N )

{

int n = threadIdx.x + blockIdx.x * blockDim.x;

int m = threadIdx.y + blockIdx.y * blockDim.y;

float sum = 0.0f;

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

{

__shared__ float tA[ 16 ][ 16 ];

__shared__ float tB[ 16 ][ 16 ];

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

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

__syncthreads( );

for( int t = 0 ; t < 16 ; t++ )

{

sum += tA[ threadIdx.y ][ t ] * tB[ threadIdx.x ][ t ];

}

__syncthreads( );

}

dC[ m + n * N ] = sum;

}

共有メモリの宣言

ブロック内のスレッドを同期

ブロック内のスレッドを同期

各スレッドが独立して行列積を計算

各スレッドが独立してメモリアクセス

行列A 行列C

行列B

mnc

mka

knbN

N

16

16

16

16

Page 67: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(1)

CPU, GPU#1, GPU#2 の計算時間を比較

使用計算機

CPU: Intel Core i7-X980(3.33GHz)

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

GPU: NVIDIA Geforce GTX480

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

Memory:1.5 GB

OS:Windows 7 SP1

67

Page 68: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

0

50

100

150

200

250

300

350

400

450

500

16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

272

288

304

320

336

352

368

400

416

432

448

464

480

496

528

544

560

592

608

624

656

672

688

720

736

752

768

784

800

計算時間の比較(2)

68

行列のサイズ (N 次正方行列)

[msec.]

CPU

GPU#1

GPU#2

N = 560

計算時間(N = 560 の場合)

・CPU#1: 91.7 msec.

・GPU#1: 18.3 msec. (×5.0)

・GPU#2: 06.7 msec. (×13.7)

Page 69: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

まとめ(行列積)

GPU上での実装方法

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

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

大きさ16×16の部分行列積に分割

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

CPUとGPUそれぞれでの計算速度を比較

CPU < GPU#1 < GPU#2

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

69

×5.0 ×13.7

Page 70: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~テンプレートマッチング~

70

Page 71: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

テンプレートマッチング(1)

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

基板の品質検査

画像中の特定物体(人物など)の検出

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

入力画像中に窓を設定

窓を移動させながら 類似度を評価

71

テンプレート 窓

比較

Page 72: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

テンプレートマッチング(2)

基本的なテンプレートマッチングの戦略 入力画像中の部分画像とテンプレートの類似度を評価

窓を移動させながら類似度計算 位置(X軸方向, Y軸方向)

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

回転

類似度の例

SAD(Sum of Absolute Difference)

SSD(Sum of Squared Difference)

NCC(Normalized Cross Correlation)

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

膨大な回数の類似度評価が必要

計算コスト大

72

Page 73: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

デモ(テンプレートマッチング)

73

Page 74: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

テンプレートマッチングの実装方法

テンプレートマッチングの特徴

類似度は各位置で独立に計算可能

GPUによる並列計算が可能

頻繁に画像メモリへアクセス

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

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

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

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

74

・・・

スケール

Page 75: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

処理の流れ

CPU側の処理

入力画像とテンプレートの読み込み

GPUスレッドの同期

GPU側の処理

類似度計算

75

スケールの変更

画像読み込み

CPU

メモリ確保

GPU

類似度計算

GPU

スレッド同期

CPU

データ転送

CPU GPU

データ転送

GPU CPU

Page 76: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

テクスチャメモリとは?

GPU上の読み取り専用の特殊なメモリ領域 キャッシュを利用した高速なアクセスが可能

2次元アクセスに対して効率的

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

テクスチャメモリの定義 texture<DataType, Type, ReadMode> texRef;

DataType: データの型(int,float 等)

Type: テクスチャの種類(1次元テクスチャなど) cudaTextureType1D,cudaTextureType2D,…

ReadMode: 値の範囲 cudaReadModeElementType: 各データ型の値を使用

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

76

Page 77: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

メモリ確保と転送(1)

入力画像のメモリをGPU上に確保

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

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

CPUからGPUへメモリ転送

テクスチャメモリへの対応付け

77

cudaArray *imgArray;

cudaChannelFormatDesc c1 = cudaCreateChannelDesc< uchar4 >( );

cudaMallocArray( &imgArray, &c1, width, height );

cudaBindTextureToArray( imgTex, imgArray, c1 );

画素表現の指定

cudaMemcpyToArray( imgArray, 0, 0, pSrc, nBytes, cudaMemcpyHostToDevice );

転送データ量

転送元ポインタ CPUからGPUへ転送

texture< uchar4, cudaTextureType2D, cudaReadModeElementType > imgTex;

Page 78: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

メモリ確保と転送(2)

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

テクスチャの定義

ハードウェア線形補間の有効化

正規化テクスチャ座標の有効化

78

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

texture< uchar4, cudaTextureType2D, cudaReadModeNormalizedFloat > refTex;

refTex.normalized = 1;

refTex.filterMode = cudaFilterModeLinear;

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

(0, 0) (W-1, 0)

(0, H-1) (W-1, H-1)

(0, 0) (1, 0)

(0, 1) (1, 1)

(0, 0) (1, 0)

(0, 1)

(0, 0) (1, 0)

(0, 1)

Page 79: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPU上で類似度計算

79

__global__ void kernel( float *error, float *scale, int imgW, int imgH,

int areaW, int areaH, int maskW, int maskH, float s )

{

int i = threadIdx.x + blockDim.x * blockIdx.x;

int j = threadIdx.y + blockDim.y * blockIdx.y;

float err = 0.0f;

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

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

for( int n = 0 ; n < maskH ; n++ )

{

for( int m = 0 ; m < maskW ; m++ )

{

uchar4 p1 = tex2D( imgTex, i + m, j + n );

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

err += ( p1.x - p2.x ) * ( p1.x - p2.x );

err += ( p1.y - p2.y ) * ( p1.y - p2.y );

err += ( p1.z - p2.z ) * ( p1.z - p2.z );

}

}

err *= _1_w * _1_h;

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

{

error[ i + j * imgW ] = err;

scale[ i + j * imgW ] = s;

}

}

テクスチャからデータを読み取り

SSDを計算

SSDが最小のものを記録

Page 80: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CPUによる類似度計算の制御

80

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

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

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

float s = MIN_SCALE;

while( s <= MSCALE )

{

// 実行するGPUのスレッド数を指定

int W = ( int )img.width( ), H = ( int )img.height( );

int w = ( int )( ref.width( ) * s ), h = ( int )( ref.height( ) * s );

int threadNumX = 16, threadNumY = 16;

int blockNumX = ( W - w ) / threadNumX + ( ( ( W - w ) % threadNumX ) == 0 ? 0 : 1 );

int blockNumY = ( H - h ) / threadNumY + ( ( ( H - h ) % threadNumY ) == 0 ? 0 : 1 );

dim3 nThreads( threadNumX, threadNumY, 1 );

dim3 nBlocks( blockNumX, blockNumY, 1 );

// GPU側で類似度を計算する

kernel<<< nBlocks, nThreads >>>( thrust::raw_pointer_cast( &derror[ 0 ] ),

thrust::raw_pointer_cast( &dscale[ 0 ] ),

W, H, W - w, H - h, w, h, s );

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

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

}

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

thrust::host_vector< float > herror = derror;

thrust::host_vector< float > hscale = dscale;

GPUを用いて類似度を計算

スケールを変更して再探索

Page 81: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(1)

CPUとGPUで計算時間を評価

使用計算機

CPU: Intel Core i7-X980(3.33GHz)

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

GPU: NVIDIA GeForce GTX480

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

Memory:1.5 GB

OS:Windows 7 SP1

81

Page 82: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(2)

実験パラメータ

入力画像 :800×600画素

テンプレート:105×135画素

スケール :0.3~1.9倍(拡大率1.2)

実験結果

CPU: 77.3 sec.

GPU: 2.2 sec.

約 35 倍の高速化

82

テンプレート

結果画像

Page 83: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

まとめ(テンプレートマッチング)

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

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

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

ハードウェア線形補間

正規化テクスチャ座標

CPUとGPUで計算時間を比較

CPU: 77.3 sec.

GPU: 2.2 sec.

83

約 35 倍の高速化を実現

Page 84: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~テンプレートマッチングを高速化~

84

Page 85: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

類似度計算の打ち切り処理を導入

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

Parallel Reduction アルゴリズムを利用

スケールの変更

画像読み込み

CPU

メモリ確保

GPU

類似度計算

GPU

スレッド同期

CPU

データ転送

CPU GPU

データ転送

GPU CPU

最大類似度計算

GPU

処理の流れ

85

Page 86: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPU上で類似度計算

86

__global__ void kernel( float *error, float *scale, int imgW, int imgH,

int areaW, int areaH, int maskW, int maskH, float s, float maxerr )

{

int i = threadIdx.x + blockDim.x * blockIdx.x;

int j = threadIdx.y + blockDim.y * blockIdx.y;

float err = 0.0f;

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

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

for( int n = 0 ; n < maskH ; n++ )

{

for( int m = 0 ; m < maskW ; m++ )

{

uchar4 p1 = tex2D( imgTex, i + m, j + n );

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

err += ( p1.x - p2.x ) * ( p1.x - p2.x );

err += ( p1.y - p2.y ) * ( p1.y - p2.y );

err += ( p1.z - p2.z ) * ( p1.z - p2.z );

}

if( maxerr < err *_1_w * _1_h )

{

break;

}

}

... 以下同じ ...

}

前スケールの探索時におけるSSDが最小値より大きい場合は計算を打ち切り

Page 87: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Parallel Reductionによる最小値探索

GPU上のメモリから最小値を計算

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

87

2

GPU上のデータ

同期

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

4 25 5 6 12 7 2 3

4 5 7 2

4 2

2

同期

同期

Page 88: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Thrustライブラリの reduce アルゴリズムを利用

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

Parallel Reductionによる最小値探索

GPU上のメモリから最小値を計算

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

88

2

GPU上のデータ

同期

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

4 25 5 6 12 7 2 3

4 5 7 2

4 2

2

同期

同期

先頭を指すイテレータ

末尾を指すイテレータ

Page 89: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CPUによる類似度計算の制御

89

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

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

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

float s = MIN_SCALE, maxerr = 1.0e10f;

while( s <= MSCALE )

{

// 実行するGPUのスレッド数を指定

int W = ( int )img.width( ), H = ( int )img.height( );

int w = ( int )( ref.width( ) * s ), h = ( int )( ref.height( ) * s );

int threadNumX = 16, threadNumY = 16;

int blockNumX = ( W - w ) / threadNumX + ( ( ( W - w ) % threadNumX ) == 0 ? 0 : 1 );

int blockNumY = ( H - h ) / threadNumY + ( ( ( H - h ) % threadNumY ) == 0 ? 0 : 1 );

dim3 nThreads( threadNumX, threadNumY, 1 );

dim3 nBlocks( blockNumX, blockNumY, 1 );

// GPU側で類似度を計算する

kernel<<< nBlocks, nThreads >>>( thrust::raw_pointer_cast( &derror[ 0 ] ),

thrust::raw_pointer_cast( &dscale[ 0 ] ),

W, H, W - w, H - h, w, h, s, maxerr );

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

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

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

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

}

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

...

Trustライブラリを用いてGPUを活用して誤差の最小値を計算

Page 90: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(1)

CPUとGPUで計算時間を評価

使用計算機

CPU: Intel Core i7-X980(3.33GHz)

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

GPU: NVIDIA GeForce GTX480

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

Memory:1.5 GB

OS:Windows 7 SP1

90

Page 91: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(2)

実験パラメータ

入力画像 :800×600画素

テンプレート:105×135画素

スケール :0.3~1.9倍(拡大率1.2)

実験結果

CPU: 8.700 sec.

GPU: 0.258 sec.

約 33.7 倍の高速化

91

テンプレート

結果画像

Page 92: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

まとめ(テンプレートマッチング)

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

Parallel Reductionアルゴリズムを利用

CPUとGPUで計算時間を比較

92

類似度の打ち切り

なし あり

CPU 77.3 秒 8.7 秒

GPU 2.2 秒 0.258 秒

Page 93: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~ガウシアンフィルタ~

93

Page 94: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

ガウシアンフィルタに挑戦

94

Page 95: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

線形フィルタとは?

画像処理の基本的な処理

ガウシアンフィルタ

LoGフィルタ

ガボールフィルタ

空間フィルタリング(線形フィルタ)

95

h

hy

w

wx

yjxifyxhjig ,,,

入力画像

フィルタ 出力画像 注目画素 f(i,j)

フィルタh(i,j)

積和

周辺領域

入力画像 出力g(i,j)

Page 96: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

ガウシアンフィルタ

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

問題点

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

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

96

2

22

2exp

2

1,

yxyxh

2

2

2

2

2

22

2exp

2exp

2

1

2exp

2

1,

yxyxyxh

h

hy

w

wx

yjxifxy

jig ,2

exp2

exp2

1,

2

2

2

2

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

Page 97: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

ガウシアンフィルタの特徴

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

GPUによる並列計算が可能

フィルタ係数の算出

フィルタ適用前に事前計算が可能

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

コンスタントメモリを利用

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

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

97

Page 98: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

コンスタントメモリとは?

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

読み取り専用

マルチプロセッサ1基あたり 64 KB

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

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

コンスタントメモリの定義

__constant__ float coeff[ 512 ];

C言語の配列のようにアクセス可能

98

Page 99: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

処理の流れ

GPU上に2つの1次元配列を確保

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

GPU内でのメモリ転送コストを削減

99

画像読み込み

CPU

メモリ確保

GPU

(X軸)

ガウシアンフィルタ

GPU

スレッド同期

CPU

データ転送

CPU GPU

データ転送

GPU CPU

(Y軸)

ガウシアンフィルタ

GPU

メモリ1 メモリ2 メモリ2 入力画像

Page 100: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

メモリ確保と転送(1)

入力画像のメモリをGPU上に確保

テクスチャの定義

GPU上に1次元配列を確保

CPUからGPUへメモリ転送

1次元テクスチャにマッピング

100

cudaMalloc( ( void ** )&iData, nBytes );

cudaMalloc( ( void ** )&oData, nBytes );

cudaBindTexture( 0, imgTexX, oData );

cudaBindTexture( 0, imgTexY, iData );

cudaMemcpy( oData, pSrc, nBytes, cudaMemcpyHostToDevice );

texture< float4, 1 > imgTexX;

texture< float4, 1 > imgTexY;

Page 101: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

メモリ確保と転送(2)

フィルタ係数をコンスタントメモリに確保

コンスタントメモリの定義

CPUからGPUへメモリ転送

101

コンスタントメモリを表す修飾子

__constant__ float coeff[ 512 ];

cudaMemcpyToSymbol( coeff, pSrc, 512 * sizeof( float ) );

サイズ指定が必要

Page 102: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(1)

CPUとGPUで計算時間を評価

使用計算機 CPU: Intel Core i7-X980(3.33GHz)

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

GPU: NVIDIA Geforce GTX480

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

Memory:1.5 GB

OS:Windows 7 SP1

実験パラメータ 画像サイズ:1002 ~ 30002 画素

σ:5.0,10.0

102

Page 103: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

0

50

100

150

200

250

300

350

400

200 400 600 800 1000 1200 1400 1600 1800 2000 2200 2400 2600 2800 3000

計算時間の比較(2)

103

画像サイズ N

2

[msec.]

CPU(σ=5)

GPU(σ=10)

GPU(σ=5)

計算時間(N 2= 20002 の場合)

・CPU(σ=5) : 105.2 msec.

・GPU(σ=5) : 20.9 msec. (×5.0)

・CPU(σ=10) : 167.7 msec.

・GPU(σ=10) : 30.7 msec. (×5.5)

CPU(σ=10)

2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

Page 104: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

まとめ(ガウシアンフィルタ)

2次元ガウス分布を1次元ガウス分布の積で表現

ガウシアンフィルタを1次元フィルタに分解

コンスタントメモリを利用した高速化

キャッシュを利用したメモリアクセスの高速化

同じ値をスレッド間で共有する場合に有効

CPUとGPUで計算時間を比較

画像サイズが 20002 の場合(σ=5)

CPU: 105.2 msec.

GPU: 20.9 msec.

104

約 5 倍の高速化を実現

Page 105: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~SIFT特徴量の計算~

105

Page 106: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFT(Scale Invariant Feature Transform)

回転・スケール変化等に頑健な特徴点の検出

画像間のマッチングや物体認識・検出に利用

106

1. David G. Lowe, “Distinctive image features from scale-invariant keypoints,”

International Journal of Computer Vision, 60, 2, pp. 91-110, 2004.

2. 藤吉弘亘. "Gradientベースの特徴抽出 - SIFTとHOG - ", 情報処理学会 研究報告

CVIM 160, pp. 211-224, 2007.

1,2

Page 107: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFTのアルゴリズム(1)

107

DoG画像の作成 (Difference-of-Gaussian)

特徴点の検出

エッジ上の点を削除

サブピクセル位置推定

異なるスケールの平滑化画像の差分(DoG)を計算

周辺26画素に対して極値をとる位置を

特徴点として検出

コントラストの

小さい点を削除 DoG1

DoG2

DoG3

DoG1

DoG2

DoG3

3k

k

2k

Page 108: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFTのアルゴリズム(2)

108

DoG画像の作成 (Difference-of-Gaussian)

特徴点の検出

エッジ上の点を削除

サブピクセル位置推定

コントラストの

小さい点を削除

Page 109: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFTのアルゴリズム(3)

109

オリエンテーションの算出

特徴ベクトルを算出

周辺領域の勾配方向と強度から

オリエンテーションを算出

Page 110: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

デモ( SIFT )

110

Page 111: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFTの実装方法

さまざまなスケールにおけるDoG計算 高速なガウシアンフィルタを利用

キーポイント検出 各画素で独立に判定可能

GPUによる並列計算が可能

判定に26近傍の画素値が必要

3次元テクスチャを利用できるか? テクスチャサイズの制限から利用は困難

2次元テクスチャで代用

オリエンテーションの算出 各キーポイントで独立に計算可能

111

Page 112: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

処理の流れ

112

画像読み込み

CPU

DoG計算

GPU

スレッド同期

CPU

データ転送

CPU GPU

データ転送

GPU CPU

キーポイント検出

GPU

キーポイント DoG画像 オリエンテーション 入力画像

キーポイントリスト作成

CPU

オリエンテーション算出

GPU

データ転送

GPU CPU

Page 113: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

SIFT計算におけるメモリ配置

複数スケールのDoG画像をテクスチャ1枚に配置

ミップマップ※を構築

DoG画像の出力先をソフトウェア的に調整

113

11

21

41

81

・・・

・・・

※CUDAは未サポート

Page 114: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

計算時間の比較(1)

CPUとGPUで計算時間を評価 SIFT特徴量としてオリエンテーションを計算

使用計算機 CPU: Intel Core2 Quad Q9550(2.83 GHz)

4スレッドで並列計算

GPU: NVIDIA GeForce GTX280

マルチプロセッサ数: 30(SP数= 30×8 = 240)

Memory:1.0 GB

OS:Windows Vista SP1

実験パラメータ 画像サイズ:502 ~ 10002 画素

σ0:1.6,分割数:3

114

Page 115: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

0

100

200

300

400

500

600

700

800

900

1000

1100

1200

1300

1400

1500

1600

1700

1800

1900

2000

2100

50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 1000

計算時間の比較(2)

115

画像サイズ N

2

[msec.]

計算時間(N 2 = 6002 の場合)

・CPU : 458.0 msec.

・GPU : 10.2 msec. (×44.9)

2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

CPU

GPU

Page 116: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

デモ(SIFT)

116

Page 117: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

まとめ(SIFT)

テクスチャメモリとコンスタントメモリの利用

キャッシュを利用したメモリアクセスの高速化

ミップマップを構築

複数スケールのDoG画像を1枚のテクスチャに格納

CPUとGPUで計算時間を比較

N 2 = 6002 の場合

CPU: 458.0 msec.

GPU: 10.2 msec.

117

約 44 倍の高速化を実現

Page 118: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~GPGPUによる高速画像処理に挑戦して~

118

Page 119: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUへの挑戦を終えて

CUDAを利用することで容易にGPGPUが可能

スレッドプログラミングの経験があれば非常に簡単

既存プログラムの移植も比較的容易

Thrustライブラリによる簡単なGPGPU

GPUを意識せずにプログラミングが可能

GPUを使うと10倍以上の高速化が可能?

多くの画像処理アルゴリズムは高速化が可能

空間フィルタリング,局所特徴量計算,他

逐次型の画像処理アルゴリズムは高速化が困難

ラベリング,細線化,他

119

Page 120: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

GPGPUの問題点と今後の展望

複数GPUの利用

各GPU上で別々に処理を実行

CUDA 4.0 は単一CPUスレッドから複数GPUを利用可能

CPUとの連携

GPUの苦手な処理をCPUで計算

CPUとGPUの役割分担が重要

高性能なGPUが登場

リアルタイム画像処理(大規模計算)への挑戦

GPGPU開発環境の標準化(OpenCL)

120

Page 121: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

参考文献

121

[1] M. J. Harris, G. Coombe, T. Scheuermann, and A. Lastra, “Physically-

Based Visual Simulation on Graphics Hardware,” Proceedings of

SIGGRAPH 2002 / Eurographics Workshop on Graphics Hardware 2002,

pp.1-10, 2002.(GPGPUの起源が書かれている論文)

[2] J. D. Owens, D. Luebke, N. Govindaraju, M. Harris, J. Krüger, A. E.

Lefohn, and T. J. Purcell, “A Survey of General-Purpose Computation on

Graphics Hardware,” Computer Graphics Forum, Vol.26, No.1, pp.80-

113, 2007.(最近のGPGPUが詳しく述べられている論文)

[3] “GPGPU,” http://gpgpu.org/

[4] “CUDA ZONE,” http://www.nvidia.com/object/cuda_home.html

[5] “CUDA Programming Guide,”

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

[6] “OpenCL,” http://www.khronos.org/opencl/

Page 122: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

MIST(Media Integration Standard Toolkit)

複数メディアを扱うためのライブラリ

音声・画像処理のアルゴリズムを多数実装

C/C++を用いた高速な処理を実現

C++のテンプレートを用いた汎用的な実装

複数のプラットフォームで動作

充実した日本語チュートリアルを用意

オープンソースとして公開中

BSDスタイルのライセンス

商用の製品開発でも利用可能

122

http://mist.murase.m.is.nagoya-u.ac.jp/

Page 123: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~Visual Studio 2010 の詳細設定~

※CUDA Toolkit 4.0 以降

123

Page 124: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Visual Studio 2010 の簡易設定(1)

“.cu” ファイルの簡易コンパイル設定

プロジェクトメニューのビルドのカスタマイズを表示

CUDA 4.0を選択

124

2.これを選択

1.これを表示

※ .cu ファイルの追加前に行う

Page 125: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Visual Studio 2010 の簡易設定(2)

“.cu” ファイルのコンパイルオプション

“.cu” ファイルのプロパティを表示

NVCCのコンパイルオプションをGUIで調整可能

125

これを表示

Page 126: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Visual Studio 2010 の簡易設定(3)

「プロジェクト」 「プロパティ」を選択

「構成プロパティ」 「リンカー」を選択

追加の依存ファイル

“cudart.lib” を指定

126

ここに入力

Page 127: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

Visual Studio 2010 の簡易設定(3)

プログラムのコンパイルと実行

Visual Studio の「ビルド」を実行

実行ファイルが作成されることを確認

「デバッグなしで開始」

コマンドラインに “Hello World!!” が表示される

127

Hello World!!

続行するには何かキーを押してください . . .

※本講演のプログラムを実行した場合

Page 128: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~補足資料~

128

Page 129: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

FLOPS

FLoating point number Operations Per Second

FLOPSやFLOP/s と表記される

1秒あたりに実行可能な浮動小数点演算回数

スーパーコンピュータ等の性能を表す指標

代表的なCPU/GPUのFLOPS

Core i7-965

51.20 GFLOPS

GeforceGTX580

1.58 TFLOPS

RadeonHD5870

2.72 TFLOPS

129

※ GPUは積和演算の性能

Page 130: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CUDAにおけるエラー処理

API関数(メモリ確保,他)の場合

各API関数の戻り値を評価

cudaSuccess → 実行に成功

GPUで実行する関数の場合

cudaThreadSynchronize( )により同期

cudaGetLastError( ) の戻り値を評価

cudaSuccess → 実行に成功

130

Page 131: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

~OpenCV + CUDA~

131

Page 132: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

デモ(特徴点検出&対応付け)

132

Page 133: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

OpenCVをダウンロード

Subversion経由で最新版を入手

https://code.ros.org/svn/opencv/trunk/opencv

Cmakeをダウンロード

http://www.cmake.org/

133

Page 134: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

OpenCVライブラリのビルド

CUDAを有効にする

Cmakeの設定で「WITH_CUDA」にチェック

Configureを実行

ビルド設定を反映

Generateを実行

ビルドファイルを生成

134

Cmakeの設定画面

1.ここにチェック

2.設定を反映

3.ビルドファイルの生成

Page 135: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CPU/GPUのコードの違い

SURFを用いた特徴点検出&マッチング(CPU)

135

#include <opencv2/opencv.hpp>

...

cv::Mat_< float > desc2;

cv::SurfFeatureDetector detector2( th2 );

// 特徴点を検出

detector2.detect( frame_gray, keys2 );

// 特徴量を計算

cv::SurfDescriptorExtractor extractor;

extractor.compute( frame_gray, keys2, desc2 );

// 特徴点を対応付け

cv::BruteForceMatcher< cv::L2< float > > matcher;

matcher.match( desc1, desc2, matches );

Page 136: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

CPU/GPUのコードの違い

SURFを用いた特徴点検出&マッチング(GPU)

136

#include <opencv2/gpu/gpu.hpp>

...

cv::gpu::GpuMat desc_gpu2;

cv::gpu::SURF_GPU detector2;

detector2.hessianThreshold = th2;

// データをGPUへ転送

cv::gpu::GpuMat frame_gpu;

frame_gpu.upload( frame_gray );

// 特徴点検出&特徴量計算

detector2( frame_gpu, cv::gpu::GpuMat(), keys2, desc_gpu2 );

// 特徴点を対応付け

cv::gpu::BruteForceMatcher_GPU< cv::L2< float > > matcher;

matcher.match( desc_gpu1, desc_gpu2, matches );

Page 137: GPGPUによる高速画像処理 -    NVIDIA CUDA Programming Guide 4.0より引用 ... ATI Stream (AMD) OpenCL ... ※「CUDA SDK」内のサンプルの実行結果

OpenCVでGPUを利用できる機能

行列演算

テンプレートマッチング

歩行者検出(HOG)

特徴点検出&特徴量(SURF)

特徴点対応付け

画像フィルタ

ラプラシアン,ソーベル,ガウシアン,他

カメラキャリブレーション

・・・

137