OpenCV OpenMP - eiichiromomma/CVMLAB GitHub Wiki
(OpenCV) OpenMPによるマルチスレッド並列プログラミング
※試行錯誤中なので参考程度
導入についてはVisualC OpenMPを参照。
forループを並列化
#pragma omp parallel for [schedule(スケジューリング法)]
をfor文の直前に入れる。
- 100x100の画像をcvSet2Dで塗り潰すのをx, yについて並列化
- dynamic, static, guidedの3種を順次y, xについて行なう
- スレッド数はプロセッサ数*4
#include <cv.h>
#include <highgui.h>
#include <omp.h>
int main(void){
IplImage *src = cvCreateImage(cvSize(100,100),IPL_DEPTH_8U,3);
cvSetZero(src);
int y,x;
cvNamedWindow("src",1);
int nProc = omp_get_num_procs();
int nTh = nProc * 4;
omp_set_num_threads(nTh);
CvScalar *colors = new CvScalar[nTh];
for (x = 0; x< nTh; x++){
colors[x] = CV_RGB(255,255-x*255/nTh,x*255/nTh);
}
//dynamic で y のparallel
//スレッドでxを共有するので private(x)が必要
#pragma omp parallel for schedule(dynamic) private(x)
for (y = 0; y< src->height; y++){
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_d_y.png",src);
cvWaitKey(0);
cvSetZero(src);
//static で y のparallel
//スレッドでxを共有するので private(x)が必要
#pragma omp parallel for schedule(static) private(x)
for (y = 0; y< src->height; y++){
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_s_y.png",src);
cvWaitKey(0);
cvSetZero(src);
//guided で y のparallel
//スレッドでxを共有するので private(x)が必要
#pragma omp parallel for schedule(guided) private(x)
for (y = 0; y< src->height; y++){
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_g_y.png",src);
cvWaitKey(0);
cvSetZero(src);
//dynamic で x のparallel
for (y = 0; y< src->height; y++){
#pragma omp parallel for schedule(dynamic)
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_d_x.png",src);
cvWaitKey(0);
cvSetZero(src);
//dynamic で x のparallel
for (y = 0; y< src->height; y++){
#pragma omp parallel for schedule(static)
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_s_x.png",src);
cvWaitKey(0);
cvSetZero(src);
//guided で x のparallel
for (y = 0; y< src->height; y++){
#pragma omp parallel for schedule(guided)
for (x = 0; x< src->width; x++){
cvSet2D(src,y,x,colors[omp_get_thread_num()]);
cvShowImage("src",src);
cvWaitKey(10);
}
}
cvShowImage("src",src);
cvSaveImage("OpenMP_g_x.png",src);
cvWaitKey(0);
return 0;
}
最初は規則的だが処理が終わったスレッドに順次割り当てるので下に行く程バラ付く。
等間隔で割り当てるので最後まで綺麗に分かれる。 当たり前だが一つでもモタ付くとそのスレッドが終わるまで待ちになるので、演算量が均等な処理以外は使うべきでは無い?
対数的(?)に割り当てるので面白い。 文献によると効率は最も良いが、割り当ての計算コストがかかるとか。
_makeからビルドしようとするとRelease OpenMPといった構成があるので、どの程度対応しているのかpragma ompでgrepしてみたところ、
- apps/HaarTraining
- cvDistTransform
- cvHarr
- Pyramid関係
- auxのblobtracking
でしか使われていないらしい。
CVS版だと
- cvMatchTemplate
も対応している。
つまり上記に該当する機能を使う場合は、OpenMPの構成を使った方がパフォーマンス向上の可能性がある。
以降の話は上記以外の処理をOpenMPに渡すサンプルなので、OpenCVに限った話ではない。
- Express Editionより上のVisualC++を使う または Windows SDK for Windows Server 2008を入れると使える
- #include <omp.h>を入れる
- 並列化したい所にOpenMPの宣言子を入れる
- プロジェクトの構成プロパティで[C/C++]-[言語]でOpenMPサポートを"はい/openmp"にする
- Intelの資料
- AIX におけるC およびC++アプリケーションの開発および移植 から"OpenMP"を検索すると出てくる。AIX用の資料だがOpenMPについてサンプルソースを基にした解説がある。
細線化で白と黒のマッチング部分を並列化してみる。使用するデータが独立しているので細かいことは考えずに並列化できた。
宣言子は入れ子になっていて、parallelの中でsectionsを使った場合、sections内で宣言されたsectionを並列に処理する。
今のところこれで動いている。大きい画像を処理しようとすると10~20%パフォーマンスが向上した。
#include <cv.h>
#include <highgui.h>
#include <omp.h>
void myThinningInit(CvMat** kpw, CvMat** kpb)
{
for (int i=0; i<8; i++){
*(kpw+i) = cvCreateMat(3, 3, CV_8UC1);
*(kpb+i) = cvCreateMat(3, 3, CV_8UC1);
cvSet(*(kpw+i), cvRealScalar(0), NULL);
cvSet(*(kpb+i), cvRealScalar(0), NULL);
}
//kernel1
cvSet2D(*(kpb+0), 0, 0, cvRealScalar(1));
cvSet2D(*(kpb+0), 0, 1, cvRealScalar(1));
cvSet2D(*(kpb+0), 1, 0, cvRealScalar(1));
cvSet2D(*(kpw+0), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+0), 1, 2, cvRealScalar(1));
cvSet2D(*(kpw+0), 2, 1, cvRealScalar(1));
//kernel2
cvSet2D(*(kpb+1), 0, 0, cvRealScalar(1));
cvSet2D(*(kpb+1), 0, 1, cvRealScalar(1));
cvSet2D(*(kpb+1), 0, 2, cvRealScalar(1));
cvSet2D(*(kpw+1), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+1), 2, 0, cvRealScalar(1));
cvSet2D(*(kpw+1), 2, 1, cvRealScalar(1));
//kernel3
cvSet2D(*(kpb+2), 0, 1, cvRealScalar(1));
cvSet2D(*(kpb+2), 0, 2, cvRealScalar(1));
cvSet2D(*(kpb+2), 1, 2, cvRealScalar(1));
cvSet2D(*(kpw+2), 1, 0, cvRealScalar(1));
cvSet2D(*(kpw+2), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+2), 2, 1, cvRealScalar(1));
//kernel4
cvSet2D(*(kpb+3), 0, 2, cvRealScalar(1));
cvSet2D(*(kpb+3), 1, 2, cvRealScalar(1));
cvSet2D(*(kpb+3), 2, 2, cvRealScalar(1));
cvSet2D(*(kpw+3), 0, 0, cvRealScalar(1));
cvSet2D(*(kpw+3), 1, 0, cvRealScalar(1));
cvSet2D(*(kpw+3), 1, 1, cvRealScalar(1));
//kernel5
cvSet2D(*(kpb+4), 1, 2, cvRealScalar(1));
cvSet2D(*(kpb+4), 2, 2, cvRealScalar(1));
cvSet2D(*(kpb+4), 2, 1, cvRealScalar(1));
cvSet2D(*(kpw+4), 0, 1, cvRealScalar(1));
cvSet2D(*(kpw+4), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+4), 1, 0, cvRealScalar(1));
//kernel6
cvSet2D(*(kpb+5), 2, 0, cvRealScalar(1));
cvSet2D(*(kpb+5), 2, 1, cvRealScalar(1));
cvSet2D(*(kpb+5), 2, 2, cvRealScalar(1));
cvSet2D(*(kpw+5), 0, 2, cvRealScalar(1));
cvSet2D(*(kpw+5), 0, 1, cvRealScalar(1));
cvSet2D(*(kpw+5), 1, 1, cvRealScalar(1));
//kernel7
cvSet2D(*(kpb+6), 1, 0, cvRealScalar(1));
cvSet2D(*(kpb+6), 2, 0, cvRealScalar(1));
cvSet2D(*(kpb+6), 2, 1, cvRealScalar(1));
cvSet2D(*(kpw+6), 0, 1, cvRealScalar(1));
cvSet2D(*(kpw+6), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+6), 1, 2, cvRealScalar(1));
//kernel8
cvSet2D(*(kpb+7), 0, 0, cvRealScalar(1));
cvSet2D(*(kpb+7), 1, 0, cvRealScalar(1));
cvSet2D(*(kpb+7), 2, 0, cvRealScalar(1));
cvSet2D(*(kpw+7), 1, 1, cvRealScalar(1));
cvSet2D(*(kpw+7), 1, 2, cvRealScalar(1));
cvSet2D(*(kpw+7), 2, 2, cvRealScalar(1));
}
void main(void)
{
CvMat** kpb = new CvMat *[8];
CvMat** kpw = new CvMat *[8];
myThinningInit(kpw, kpb);
IplImage* src = cvLoadImage("thinning_testL.jpg",0);
IplImage* dst = cvCloneImage(src);
IplImage* src_w = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1);
IplImage* src_b = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1);
IplImage* src_f = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1);
cvScale(src, src_f, 1/255.0, 0);
cvThreshold(src_f,src_f,0.5,1.0,CV_THRESH_BINARY);
cvThreshold(src_f,src_w,0.5,1.0,CV_THRESH_BINARY);
cvThreshold(src_f,src_b,0.5,1.0,CV_THRESH_BINARY_INV);
//使えるプロセッサ数をスレッド数に(今回はデュアルで足りる)
int nProcs = omp_get_num_procs();
omp_set_num_threads(nProcs);
double sum=1;
CvMat* w;
CvMat* b;
while(sum>0){
sum=0;
for (int i=0; i<8; i++){
w= *(kpw+i);
b= *(kpb+i);
#pragma omp parallel
{
#pragma omp sections
{
#pragma omp section
{
cvFilter2D(src_w, src_w, w);
cvThreshold(src_w,src_w,2.99,1,CV_THRESH_BINARY);
}
#pragma omp section
{
cvFilter2D(src_b, src_b, b);
cvThreshold(src_b,src_b,2.99,1,CV_THRESH_BINARY);
}
}
}
cvAnd(src_w, src_b, src_w);
sum += cvSum(src_w).val[0];
cvXor(src_f, src_w, src_f);
cvCopyImage(src_f, src_w);
cvThreshold(src_f,src_b,0.5,1,CV_THRESH_BINARY_INV);
}
}
cvConvertScaleAbs(src_f, dst, 255, 0);
cvNamedWindow("dst",1);
cvShowImage("dst",dst);
cvWaitKey(0);
}