【opencv/core module】(二)How to scan images, lookup tables and time measurement with OpenCV

说在前面

Color Space Reduction

  • 对于c/c++,使用unsigned char(8 bits)来存储的话,每个像素的单个通道就有256个值,这给算法性能带来了沉重的负担;但是,大部分时候并不需要使用这全部的256个值。

  • 所以我们引入了 color space reduction 这个东东:
    I n e w = ( I o l d 10 ) ∗ 10 I_{new} = ({I_{old}\over 10 }) * 10 Inew=(10Iold)10
    我们将每个像素的每个通道的值均使用上述公式处理一遍,这样就将256个值缩减至26个值。
    例如:5 → 0 255 → 25

0-910-1920-2930-3940-4950-59
01020304050
  • 但是上述的方法使用了除法和乘法,并不适合在遍历像素的时候对每个像素进行这种操作(乘除计算比较慢,相对于加、减、移位)

  • 所以我们又引入了另外一种东东,lookup table ;这是个一维或者多维的数组,数组的值是事先已经计算好的。如下表:

012345678910111213141516
00000000001111111
  • 使用lookup table,我们就不需要进行计算了,只需取出table中的值即可,例如254,使用table[254]即可。

cv::getTickCount() & cv::getTickFrequency()

  • 用于计时
double t = (double)getTickCount();//当前时钟周期数

// do something ...

//getTickFrequency() 获取每秒时钟周期数
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;

遍历像素

  • The efficient way

    • 使用指针进行访问(operator [])
      在这里插入图片描述
    • 如上图,Mat类的方法 ptr(i) 返回第 i 行的首地址,对于三通道的Mat,每一行的数据是连续的,即可以使用循环自增的方式访问每一个数据(channel*cols, 每个数据8bit)
    • 这里还是存在一个问题,那就是 行与行之间的地址可能不是连续的 ,即第 i 行尾地址为 n ,第 i+1 行首地址可能不是 n+1,这是由于mat类在创建的时候可以使用 Mat::col(取列) 以及Mat::diag(取对角线);下面是col()与diag()函数的测试demo。
//code of "col() & diag() demo"

    Mat A = (Mat_<double>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
	Mat c1 = A.col(0);
	Mat c2 = A.diag(0);

	cout << "A      continuous? " << A.isContinuous() << endl;
	cout << "A \n" << A << endl;
	cout << "col()  continuous? " << c1.isContinuous() << endl;
	cout << "col(0) \n" << c1 << endl;
	cout << "diag() continuous? " << c2.isContinuous() << endl;
	cout << "diag(0) \n" << c2 << endl;

result of the demo

// Code of "The efficient way"

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	CV_Assert(I.depth() == CV_8U);

	int channels = I.channels();

	int nRows = I.rows;//行数
	int nCols = I.cols * channels;//列数

	if (I.isContinuous())
	{
		nCols *= nRows;
		nRows = 1;
	}

	int i, j;
	uchar* p;
	for (i = 0; i < nRows; ++i)
	{
		p = I.ptr<uchar>(i);
		for (j = 0; j < nCols; ++j)
		{
			p[j] = table[p[j]];
		}
	}
	return I;
}
  • The iterator (safe) method

    • 使用迭代器,确定start & end,有效防止数组越界,并且不需要考虑是否连续
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	CV_Assert(I.depth() == CV_8U);

	const int channels = I.channels();
	switch (channels)
	{
	case 1:
	{
		MatIterator_<uchar> it, end;
		for (it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
			*it = table[*it];
		break;
	}
	case 3:
	{
		MatIterator_<Vec3b> it, end;
		for (it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
		{
			(*it)[0] = table[(*it)[0]];
			(*it)[1] = table[(*it)[1]];
			(*it)[2] = table[(*it)[2]];
		}
	}
	}

	return I;
}
  • On-the-fly address calculation with reference returning

    • 不推荐用来遍历像素
    • 一般是用来访问指定位置的像素的
    • 基本流程:从第一行开始,遍历第一行的所有像素;然后第二行、第三行
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	CV_Assert(I.depth() == CV_8U);

	const int channels = I.channels();
	switch (channels)
	{
	case 1:
	{
		for (int i = 0; i < I.rows; ++i)
			for (int j = 0; j < I.cols; ++j)
				I.at<uchar>(i, j) = table[I.at<uchar>(i, j)];
		break;
	}
	case 3:
	{
		Mat_<Vec3b> _I = I;

		for (int i = 0; i < I.rows; ++i)
			for (int j = 0; j < I.cols; ++j)
			{
				_I(i, j)[0] = table[_I(i, j)[0]];
				_I(i, j)[1] = table[_I(i, j)[1]];
				_I(i, j)[2] = table[_I(i, j)[2]];
			}
		I = _I;
		break;
	}
	}

	return I;
}
  • The Core Function

    • 使用了多线程,所以快。
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
    p[i] = table[i];
//......
LUT(I, lookUpTable, J);
  • 测试结果对比(release)

在这里插入图片描述

  • 总结

    • 根据上面的测试结果,LUT方法最快,并且官方也推荐使用;
    • 如果你更喜欢自己写的话,推荐使用迭代器(安全,但效率较低)
  • 其他

(测试的时候遇到个奇怪的问题)

    Mat Mat_inA = imread("test.jpg");//上面测试结果对应这种
	Mat Mat_inB = imread("test.jpg");
	Mat Mat_inC = imread("test.jpg");
	Mat I = imread("test.jpg");
    Mat Mat_inA = imread("test.jpg");//使用这种测试结果不一样
	Mat Mat_inB = Mat_inA.clone();
	Mat Mat_inC = Mat_inA.clone();
	Mat I = Mat_inA.clone();
J = ScanImageAndReduceC(Mat_inA, table);
//......
J = ScanImageAndReduceIterator(Mat_inB, table);
//......
J = ScanImageAndReduceRandomAccess(Mat_inC, table);
//......
LUT(I, lookUpTable, J);

上面两种方式得到的测试结果不一样,不知道为啥


END-2019.6.26

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值