のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

OpenCVの使い方(2) ピクセルへのアクセス方法(5種・data,point,at,pointer,iterator)+速度比較

Sponsored Links

皆さんこんにちは
お元気ですか?私はプレゼン準備でひぃひぃいってます。

さて、今回はOpenCVについてやります。
だいぶご無沙汰してますね。

ところで、皆さんOpenCVを使っていて、ピクセルにアクセスしたいってことありませんでしたか?私は結構ありました。

カラーヒストグラム作ったりLBP(Local Binary Pattern)を作ったりと何かとピクセルに用事がありました。

ということで今回はピクセルへのアクセス方法を紹介するとともに、速度面について比較をしたいと思っています。

ピクセルへのアクセス方法

ポインターを使う

void PixelAccess::pointer(){
	for(int height = 0; height < image.rows; height++){
		Vec3b *ptr = image.ptr<Vec3b>(height);
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = ptr[width];
		}
	}
}

iteratorを利用する方法

vectoriteratorは脆弱でしたが、今回はどうか?

void PixelAccess::iterator(){
	MatIterator_<Vec3b> itd = image.begin<Vec3b>(),itd_end = image.end<Vec3b>();
	for(itd; itd != itd_end; itd++){
		Vec3b bgr = (*itd);
	}
}

atを使ったアクセス

void PixelAccess::atData(){
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = image.at<Vec3b>(height,width);
		}
	}
}

Pointを利用して…

void PixelAccess::execPoint(){
	Mat3b dotImg = image;
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = dotImg(Point(width,height));
		}
	}
}

dataメソッドを使って

void PixelAccess::readData(){
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			for(int channel = 0; channel < image.channels(); channel++){
				image.data[height * image.step + width * image.elemSize() + channel];
			}
		}
	}
}

以上の5種類あります。高さと横幅については間違えないでください。実装によっては反対になっています。

速度比較実験

実験内容

それぞれのメンバ関数を1000回回し、比較
また、コンパイルオプションでも比較

g++ pixel_access.cpp `pkg-config --cflags opencv` `pkg-config --libs opencv`
g++ -O2 pixel_access.cpp `pkg-config --cflags opencv` `pkg-config --libs opencv`

実験環境

OS:X 10.9.1
CPU:2.8GHz IntelCore i7
メモリ:16GB 1600MHz DDR3

結果

関数 速度[ms](No option) 速度(-O2)
pointer 903266 3
iterator 2387590 134332
at 1130726 14
Point 1528948 28
data 2058010 3

考察

ポインター早い、そしてiterator

  • O2によってデータが早くなるのは予想通りですが、結構なゴボウ抜き

まさか実行がされてないのか…!?

結論

ポインター使うのが一番早いが、オプションつけたらdataが早くなるかも…
どの参照方法を使うか迷った時は検討してみはいかがでしょうか?

ソースコード全文

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/nonfree.hpp>

using namespace std;
using namespace cv;

class PixelAccess{
private:
	Mat image;
	void pointer();
	void iterator();
	void atData();
	void execPoint();
	void readData();

public:
	PixelAccess(string path);
	void executeProgram();
};

PixelAccess::PixelAccess(string path){
	image = cv::imread(path);
}

void PixelAccess::pointer(){
	for(int height = 0; height < image.rows; height++){
		Vec3b *ptr = image.ptr<Vec3b>(height);
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = ptr[width];
		}
	}
}

void PixelAccess::iterator(){
	MatIterator_<Vec3b> itd = image.begin<Vec3b>(),itd_end = image.end<Vec3b>();
	for(itd; itd != itd_end; itd++){
		Vec3b bgr = (*itd);
	}
}

void PixelAccess::atData(){
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = image.at<Vec3b>(height,width);
		}
	}
}

void PixelAccess::execPoint(){
	Mat3b dotImg = image;
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			Vec3b bgr = dotImg(Point(width,height));
		}
	}
}

void PixelAccess::readData(){
	for(int height = 0; height < image.rows; height++){
		for(int width = 0; width < image.cols; width++){
			for(int channel = 0; channel < image.channels(); channel++){
				image.data[height * image.step + width * image.elemSize() + channel];
			}
		}
	}
}

void PixelAccess::executeProgram(){
	void (PixelAccess::*executeCompare[])() = {
		&PixelAccess::pointer,
		&PixelAccess::iterator,
		&PixelAccess::atData,
		&PixelAccess::execPoint,
		&PixelAccess::readData
	};

	for(int i = 0; i < 5; i++){
		clock_t start = clock();
		for(int t = 0; t < 1000; t++){
			(this->*executeCompare[i])();
		}
		clock_t end = clock();
		cout << end - start << "[ms]" << endl;
	}
}

int main(int argc, char const *argv[]){
	PixelAccess pixelaccess(argv[1]);
	pixelaccess.executeProgram();

	return 0;
}