38
GTX 1080Computer Vision アルゴリズムを色々動かしてみる 関東GPGPU勉強会#4 2016/8/21 @dandelion1124

【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

Embed Size (px)

Citation preview

Page 1: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

関東GPGPU勉強会#42016/8/21

@dandelion1124

Page 2: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

自己紹介(1/2)

Twitter ID:@dandelion1124

• 学生時代はコンピュータビジョン、VRの研究に従事

• 大学院の研究室でOpenCVのTipsサイトを作っていたらOpenCV関連書籍(OpenCVプログラミングブック)を書くことに

• 現在は都内勤務エンジニア

Web: http://atinfinity.github.io

Wiki: https://github.com/atinfinity/lab/wiki

連載記事: http://www.buildinsider.net/small/opencv/

Page 3: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

自己紹介(2/2)

• メインで参加している勉強会

–関東コンピュータビジョン勉強会 #cvsaisentan

http://sites.google.com/site/cvsaisentan/

Page 4: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

本日のアジェンダ

• OpenCVとは

• OpenCVのデータ構造

• GpuMatとCUDAカーネルの連携

• CUDAカーネル実装

– フィルタ処理

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

• 速度計測いろいろ

– OpenCV

Page 5: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

OpenCVとは

Intelが開発したOpen SourceのComputer Vision

ライブラリ。現在はItseezIntelが開発を行っている。

http://opencv.org/

• 公式サポートOS

– Windows/Linux/Mac OS/Android/iOS

• 公式サポート言語

– C/C++/Python/Java

※有志による非公式ラッパーの一覧を以下のサイトにまとめています。https://github.com/atinfinity/lab/wiki/OpenCV-binding%E3%81%BE%E3%81%A8%E3%82%81

先日ItseezがIntelに買収されるという発表がありました

Page 6: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

OpenCVのデータ構造

OpenCVで画像を格納するために使うデータ構造は

おおまかに以下の4つ。

• Mat 画像データの入れ物(CPU版)

• UMat 画像データの入れ物(CPU/OpenCL版)

• GpuMat 画像データの入れ物(CUDA版)

UMatについては前回勉強会の発表資料を参照ください。

http://www.slideshare.net/YasuhiroYoshimura/gpgpu-dandelion1124-201301130

※上記資料は2013年11月時点のものなので最新版ではAPI仕様が少し変わっています。

Page 7: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

GpuMatとCUDAカーネルの連携

GpuMatとCUDAカーネルを連携する場合は、

cudevモジュールのGlobPtrSz、PtrStepSzを活用すると

便利です。

• GlobPtrSz:cols(幅)、rows (高さ) 、step等が参照可

• PtrStepSz:cols (幅)、rows (高さ)、step、ptr等が参照可

– Ptrメソッドを使うとポインタのアドレス計算を簡略化できる

– 以降のコードではこちらを使います

参考:http://proc-cpuinfo.fixstars.com/2016/08/gpumat.html

Page 8: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

サンプルコード(PtrStepSz)

// PtrStepSz型の変数を生成

cv::cudev::PtrStepSz<uchar> pSrc =

cv::cudev::PtrStepSz<uchar>(src.rows, src.cols * src.channels(), src.ptr<uchar>(), src.step);

const dim3 block(64, 2);

const dim3 grid(cv::cudev::divUp(dst.cols, block.x), cv::cudev::divUp(dst.rows, block.y));

// CUDAカーネルを呼び出す

kernel<<<grid, block>>>(pSrc); //カーネル引数としてPtrStepSz型の変数を渡す

CV_CUDEV_SAFE_CALL(cudaGetLastError());

CV_CUDEV_SAFE_CALL(cudaDeviceSynchronize());

カーネル呼び出し部

Page 9: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

サンプルコード(PtrStepSz)

// カーネル引数としてPtrStepSz型の変数を受け取る

__global__ void kernel(cv::cudev::PtrStepSz<uchar> src)

{

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

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

if(x < src.cols && y < src.rows) {

uchar val = src.ptr(y)[x]; // 座標(x, y)の画素値を参照する

}

}

CUDAカーネル

Page 10: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

【追記】GpuMatとCUDAカーネルの連携

GpuMatとCUDAカーネルを連携する場合は、

cudevモジュールのGlobPtrSz、PtrStepSzを活用すると

便利です。

発表時に「CUDAカーネルの引数としてGpuMatクラスの

インスタンスを直接渡せる」というご指摘を受けたため、

確認したところ、正しく動作することがわかりました。

実装する際にはGpuMatクラスのインスタンスを渡した方が

わかりやすそうです。

Page 11: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

【追記】サンプルコード(GpuMat)

cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_GRAYSCALE);

cv::cuda::GpuMat d_src(src);

const dim3 block(64, 2);

const dim3 grid(cv::cudev::divUp(dst.cols, block.x), cv::cudev::divUp(dst.rows, block.y));

// CUDAカーネルを呼び出す

kernel<<<grid, block>>>(d_src); //カーネル引数としてGpuMatクラスのインスタンスを渡す

CV_CUDEV_SAFE_CALL(cudaGetLastError());

CV_CUDEV_SAFE_CALL(cudaDeviceSynchronize());

カーネル呼び出し部

Page 12: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

【追記】サンプルコード(GpuMat)

// カーネル引数としてGpuMatクラスのインスタンスを受け取る

__global__ void kernel(cv::cuda::GpuMat src)

{

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

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

if(x < src.cols && y < src.rows) {

uchar val = src.ptr(y)[x]; // 座標(x, y)の画素値を参照する

}

}

CUDAカーネル

Page 13: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

よーし、CUDAカーネル書くぞー!

→まずはCUDA8のドキュメントを読もう

→Pascal Tuning Guideはまだない?

※CUDA 8 RCのドキュメント

Page 14: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

ご清聴ありがとうございました

Page 15: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

よーし、CUDAカーネル書くぞー!

→まずはCUDA8のドキュメントを読もう

→Pascal Tuning Guideはまだない?

→今回は手探りでやってみましょう

※CUDA 8 RCのドキュメント

Page 16: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

• 計測環境– OS:Ubuntu 16.04 LTS(64bit)

– CUDA:CUDA Toolkit v8.0

– GCC:5.4.0

– CPU:Intel Xeon CPU E5-2623 v3 @ 3.00GHz

– メモリ:128GB

– GPU:GeForce GTX 1080• Pascalアーキテクチャ

Page 17: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

• 計測環境(比較用)– OS:Ubuntu 16.04 LTS(64bit)

– CUDA:CUDA Toolkit v8.0

– GCC:5.4.0

– CPU:Intel Xeon CPU E5-2623 v3 @ 3.00GHz

– メモリ:128GB

– GPU:GeForce GTX TITAN X• Maxwellアーキテクチャ

Page 18: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

• 計測環境(比較用)– OS:Windows 10 Pro(64bit)

– CUDA:CUDA Toolkit v7.5

– Visual Studio:Visual Studio 2013 Update5

– CPU:Intel Core i7-3930K @ 3.20GHz

– メモリ:32GB

– GPU:GeForce GTX 680• Keplerアーキテクチャ

Page 19: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

CUDAカーネル実装

GeForce GTX 1080

GeForce GTX Titan X

GeForce GTX 680

アーキテクチャ Pascal Maxwell Kepler

CUDAコア数 2560基 3072基 1536基

定格クロック 1607MHz 1000MHz 1006MHz

ブーストクロック 1733MHz 1075MHz 1058MHz

メモリバス帯域幅 320GB/s 336.5GB/s 192.2GB/s

メモリ容量 8GB 12GB 2GB

動作クロックが大きく向上 メモリバス帯域幅はGeForce

Titan Xとほぼ同等

CUDAコア、メモリ容量がGTX 1080より多い

Page 20: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理

• 着目座標および周辺の画素値にカーネル係数で重み付けした値を計算する処理

• 以下の例だと・・・(1/9)*11 + (1/9)*14 + (1/9)*11 +

(1/9)*13 + (1/9)*10 + (1/9)*13 +

(1/9)*11 + (1/9)*14 + (1/9)*11 = 12

カーネル(3x3)

11

13

11

14

13

14 11

10

11

入力画像

1/9

1/9 1/9

1/9 1/9

1/9

1/91/91/9

注目座標

12

出力画像

積和演算が多い!!この例だと積:9回、和:9回

Page 21: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理(CPU実装)

ソースコード:https://github.com/atinfinity/imageFilteringGpu

for(int y = border_size; y < (dst.rows - border_size); y++){

uchar* pdst = dst.ptr<uchar>(y);

for(int x = border_size; x < (dst.cols - border_size); x++){

double sum = 0.0;

for(int yy = 0; yy < kernel.rows; yy++){

for(int xx = 0; xx < kernel.cols; xx++){

sum +=

(kernel.ptr<float>(yy)[xx] * src.ptr<uchar>(y+yy-border_size)[x+xx-border_size]);

}

}

pdst[x] = sum;

}

}

計算結果をストア

カーネル係数で重み付けして加算

Page 22: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理

• Naive実装

– CPU実装の類似度計算部分をそのままCUDAに持ってくる

• __ldg関数利用

– __ldg関数を使ってRead-Onlyキャッシュ経由でデータ読み込み

• テクスチャメモリ利用

– 画像データをテクスチャメモリに格納し、カーネルで参照

ソースコード:https://github.com/atinfinity/imageFilteringGpu

Page 23: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理(Naive実装)

ソースコード:https://github.com/atinfinity/imageFilteringGpu

if((y >= border_size) && y < (dst.rows-border_size)){

if((x >= border_size) && (x < (dst.cols-border_size))){

double sum = 0.0;

for(int yy = 0; yy < kernel.rows; yy++){

for(int xx = 0; xx < kernel.cols; xx++){

sum += (kernel.ptr(yy)[xx] * src.ptr(y+yy-border_size)[x+xx-border_size]);

}

}

dst.ptr(y)[x] = sum;

}

}

範囲チェック

カーネル係数で重み付けして加算

計算結果をストア

Page 24: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理(__ldg関数利用)

ソースコード:https://github.com/atinfinity/imageFilteringGpu

if((y >= border_size) && y < (dst.rows-border_size)){

if((x >= border_size) && (x < (dst.cols-border_size))){

double sum = 0.0;

for(int yy = 0; yy < kernel.rows; yy++){

const uchar* psrc = src.ptr(y+yy-border_size) + (x-border_size);

const float* pkernel = kernel.ptr(yy);

for(int xx = 0; xx < kernel.cols; xx++){

sum += (__ldg(&pkernel[xx]) * __ldg(&psrc[xx]));

}

}

dst.ptr(y)[x] = sum;

}

}

__ldg関数を利用してRead-Onlyキャッシュ経由で読み込み

Page 25: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理(テクスチャメモリ利用)

ソースコード:https://github.com/atinfinity/imageFilteringGpu

// bind texture

cv::cuda::device::bindTexture<uchar>(&srcTex, pSrc);

const dim3 block(64, 2);

const dim3 grid(cv::cudev::divUp(dst.cols, block.x), cv::cudev::divUp(dst.rows, block.y));

imageFilteringGpu_tex<<<grid, block>>>(pSrc, pDst, pKernel, border_size);

CV_CUDEV_SAFE_CALL(cudaGetLastError());

CV_CUDEV_SAFE_CALL(cudaDeviceSynchronize());

// unbind texture

CV_CUDEV_SAFE_CALL(cudaUnbindTexture(srcTex));

カーネル呼び出し部

OpenCVに便利関数があるので活用!

なぜかunbind用の便利関数が見当たらなかったので直接呼ぶ・・・

Page 26: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理(テクスチャメモリ利用)

ソースコード:https://github.com/atinfinity/imageFilteringGpu

if((y >= border_size) && (y < (dst.rows-border_size))){

if((x >= border_size) && (x < (dst.cols-border_size))){

double sum = 0.0;

for(int yy = 0; yy < kernel.rows; yy++){

for(int xx = 0; xx < kernel.cols; xx++){

sum += (kernel.ptr(yy)[xx] * tex2D(srcTex, x + xx - border_size, y + yy - border_size));

}

}

dst.ptr(y)[x] = sum;

}

}

Tex2D関数を使ってテクスチャメモリを参照

CUDAカーネル

Page 27: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

フィルタ処理

入力画像はw=800、h=640のグレースケール画像 単位はms 5回計測して平均をとる

GTX 1080(Pascal)

GTX TITAN X(Maxwell)

GTX680(Kepler)

CPU実装(Naive) 57.521 44.158 39.255

OpenCV(Mat) 4.956 4.980 5.212

CUDA実装(Naive) 0.426 0.606 1.138

CUDA実装(__ldg関数使用)

0.417 0.560 -

CUDA実装(テクスチャメモリ使用)

0.531 0.618 1.168

CC3.0では__ldgが使えない・・・

Page 28: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

テンプレートマッチング

• 画像中から特定のパターンを見付ける処理

• 類似度の指標はいくつかある

– SSD:差の2乗の総和

– SAD:差の絶対値の総和

etc…

• 今回はSSDという指標について実装します

参考:http://imagingsolution.blog107.fc2.com/blog-entry-186.html

Page 29: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

テンプレートマッチング(CPU実装)

ソースコード:https://github.com/atinfinity/matchTemplateGpu

for(int y = 0; y < result.rows; y++){

float* presult = result.ptr<float>(y);

for(int x = 0; x < result.cols; x++){

long sum = 0;

for(int yy = 0; yy < templ.rows; yy++){

const uchar* pimg = img.ptr<uchar>(y + yy);

const uchar* ptempl = templ.ptr<uchar>(yy);

for(int xx = 0; xx < templ.cols; xx++){

int diff = pimg[x + xx] - ptempl[xx];

sum += (diff*diff);

}

}

presult[x] = sum;

}

}

画素値の差を計算

差の二乗和を加算

Page 30: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

テンプレートマッチング

1. Naive実装 CPU実装の類似度計算部分をそのままCUDAに持ってくる

2. shared memory利用 テンプレート画像は頻繁に参照するため、shared memoryに格納

テンプレートサイズは任意なので動的にshared memoryを確保

3. 2. + __ldg利用 __ldg関数を使ってRead-Onlyキャッシュ経由でデータ読み込み

ソースコード:https://github.com/atinfinity/matchTemplateGpu

Page 31: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

テンプレートマッチング(Naive実装)

ソースコード:https://github.com/atinfinity/matchTemplateGpu

if((x < result.cols) && (y < result.rows)){

long sum = 0;

for(int yy = 0; yy < templ.rows; yy++){

for(int xx = 0; xx < templ.cols; xx++){

int diff = (img.ptr((y+yy))[x+xx] - templ.ptr(yy)[xx]);

sum += (diff*diff);

}

}

result.ptr(y)[x] = sum;

}

差の二乗和を加算

画素値の差を計算

Page 32: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

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

入力画像はw=1920、h=1080のグレースケール画像 単位はms 5回計測して平均をとる

GTX 1080(Pascal)

GTX TITAN X(Maxwell)

GTX680(Kepler)

CPU実装(Naive) 1696.070 1728.700 1107.840

OpenCV(Mat) 36.876 36.782 38.723

CUDA実装(Naive) 13.845 19.483 46.181

CUDA実装(shared memory)

7.851 11.834 49.417

CUDA実装(shared memory + __ldg)

6.931 9.641 -

CC3.0では__ldgが使えない・・・

Page 33: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

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

入力画像はw=1920、h=1080のグレースケール画像 単位はms 5回計測して平均をとる

GTX 1080(Pascal)

GTX TITAN X(Maxwell)

GTX680(Kepler)

CPU実装(Naive) 5527.08 5519.98

OpenCV(Mat) 65.84 65.32

CUDA実装(Naive) 36.64 46.87

CUDA実装(shared memory)

27.50 38.87

CUDA実装(shared memory + __ldg)

25.15 33.02 -

Page 34: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

速度計測いろいろ(OpenCV)

• cudafilters– GaussianBlur

– BoxFilter

• cudafeatures2d– ORB

– FAST

• cudaobjdetect– HOG

Page 35: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

速度計測いろいろ(OpenCV)

入力画像はw=800、h=640のグレースケール画像 単位はms 5回計測して平均をとる

GTX 1080(Pascal)

GTX TITAN X(Maxwell)

GTX680(Kepler)

GaussianBlur 0.065 0.237 0.757

BoxFilter 0.265 0.564 1.671

ORB 4.295 6.688 11.665

FAST 0.443 0.474 1.252

HOG 9.884 13.099 40.300

Page 36: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

やり残したこと

• テンプレートマッチング(CUDA版)のチューニング– 演算部分の最適化– 【追記】long型を使用している箇所を見直す– アルゴリズムそのものの見直し

• CUDA8から追加された機能を試す– cudaMemPrefetchAsyncなるAPIが増えているのに気付いたけど使い方がよくわからず・・・• 【追記】Unified Memoryのprefetchに関するAPIのようです。

• OpenCVベンチマーク– 詳細なプロファイリングおよび分析

• NPPベンチマーク– CUDA8のNPPはPascal世代にも最適化されている?

Page 37: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

まとめ

• GpuMatとCUDAカーネルの連携は簡単– cudevモジュールのGlobPtrSz、PtrStepSzが便利

– 【追記】CUDAカーネルにGpuMatクラスのインスタンスを直接渡せる

• GTX 1080では動作クロックが大きく向上– 演算ネックとなる問題に対しては恩恵が得られている

• OpenCVのcudaモジュールも(少なくとも今回検証した関数については)GTX 1080上で高速に動作– ただし、OpenCVのcudaモジュールが最新アーキテクチャに対して十分な最適化がなされているかは要検証

• Pascal Tuning Guide欲しい!!

Page 38: 【関東GPGPU勉強会#4】GTX 1080でComputer Visionアルゴリズムを色々動かしてみる

おわり