opencv入门学习(1)——how to scan image

opencv入门学习(1)——how to scan image

 

 

 目标:

通过学习,我们会了解:

  • 如何遍历图像每一个像素点
  • 图像矩阵如何存储
  • 如何去测量算法的效率
  • 什么是lookup table 我们为什么要用这个算法

内容:

我们通常使用uchar(0-255)来存储图像矩阵,每个通道的像素最多可以存储256的不同的值,而对于3通道的图像来说按照这种方法会有很多的颜色。这样会使算法的效果不是很好,通常我们可以使用较少得颜色就可以展现我们想表达得图片。这里就用到了图像压缩方法。

其中一个比较通用得方法是,我们将每一个像素得像素值除以某一个int型得数,再乘上这个数,得到得结果会比原结果小,且在uchar得范围内。公式如下:                                      

                                                               Inew=(\frac{Iold}{10})*10

这个算法很简单,也很好理解,但是我们在使用得时候,如果在遍历每一个像素得时候都是用这个这个操作,会很麻烦也很占用时间。所以我们预先将转化得结果压缩在一个table[256]数组中,等到遍历像素得时候,利用数组随机访问得特性取到压缩后像素值,将会大大提高算法的时间效率。这部分代码如下:

//product a look up table
int dividewith;
stringstream s;
s << argv[2];
s >> dividewith;
uchar table[256];
for (int i = 0; i < 256; i++) {
    table[i] = (uchar)(dividewith* (i / dividewith));
}

遍历图像方法:

遍历图像的方法,官方介绍了三种,但是3种方法各有各的特点。

方法1:the efficient way

第一种方法使用的是c风格和[](指针的方式),这种方法效率快,便利效果好。方法是获得一个指针,从每一行的开头开始遍历,直到结束,如果矩阵存在一个连续的空间内,我们只需要获取一次指针,然后用这种方法便利到头。代码如下:

//effective
Mat& ScanImageAndReduceC(Mat &I, const uchar *table) {
	CV_Assert(I.depth() ==CV_8U);
	int rows = I.rows;
	int cols = I.cols*I.channels();
	//判断是否为矩阵元素连续分配,如果连续分配,可以当作一维数组看待,操作更快
	if (I.isContinuous()) {
		cols *= rows;
		rows = 1;
	}
	uchar *p;
	for (int i = 0; i < rows; i++) {
		p = I.ptr<uchar>(i);
		for (int j = 0; j < cols; ++j) {
			p[j] = table[p[j]];
		}
	}
	return I;
}

方法二:迭代器(安全)的方法

第一种方法,效率很高,但是相对不安全,对于不连续的存储访问有一定的危险性,而使用迭代器的方法我们可以以更安全的方法来遍历图像。这里有一篇blog可以帮助你们了解什么是连续存储(OpenCV isContinuous()连续存储的问题)。我们可以使用*操作来获取指针的值。代码如下:

//reducing with the Iterator Safer
Mat& ScanImageAndReduceIterator(Mat &I, const uchar *table) {
	CV_Assert(I.depth() == CV_8U);
	int channels = I.channels();
	int rows = I.rows;
	int cols = I.cols;
	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;
}

这里我们需要注意的是,如果是彩色图像的话,每一列会有BGR3个不同的值,所以在遍历彩色图像需要使用[]来获取不同通道的值,如果不适用[]方法的话,最终遍历的只有蓝色通道的像素。

方法三:动态地址计算

说实话,这个方法的使用我没有看的很明白,不过官方不推荐使用这个方法,不懂的话也无所谓,2333333333

这个方法主要是用于获取和修改某一个随机的图像像素。代码如下:

//not recommend
Mat &ScanImageAndReduceRandomAccess(Mat &I, const uchar *table) {
	CV_Assert(I.depth() == CV_8U);
	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_ 遍历矩阵更快,操作更方便,代码量也更小
		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]];
			}
		}
	}
	}
	return I;
}

这里使用Mat_的方法,这种方法对比Mat来说在遍历多通道的图像的时候,更快捷,代码量更少。

官方提供的LUT方法

opencv官方库为我们提供接口,我们可以直接调用这个方法就可以了。

void LUT(InputArray src, InputArray lut, OutputArray dst);

src:是指我们需要进行图像压缩的图像。

lut:是我们进行图像压缩的方法,这里用数组形成映射。

dst:是转化的结果,输出的图像。

最后的结果肯定是官方提供的LUT方法效果最好啦,执行速度最快。

源代码

源代码我已经上传到github上啦,大家有兴趣的话可以去我的github上去下载和调试。我使用的opencv3.4版本,大家使用的时候注意自己的版本号,感兴趣的话可以给我的项目一个小小的star,谢谢啦。

网址:how_to_scan_image

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值