上一篇介绍了Mat的数据结构和存储方式,这里跟随官网学习Mat的遍历操作,因为openCV即是处理像素,所以遍历方法是我们必须要掌握的,这部分内容官网介绍的很详细也易懂,建议大家还是读透这些基础并上手测试加深理解,提前告诉大家,后面的内容比较晦涩,官网的内容很精简不易理解,所以请打好基础。
上篇介绍了Mat数据结构和存储方式:http://blog.csdn.net/jbl20078/article/details/78842477
本章内容官网地址:https://docs.opencv.org/master/db/da5/tutorial_how_to_scan_images.html
openCV如何扫描图像(遍历),利用查找表修改像素
先解释下什么是查找表:table
就是一个uchar类型的数组,用来保存不超多256个值,用来定义每个像素点或者像素中某个通道的值
比如:table[255] = 0 代表RGBA为255的值改为0,一个像素点(255,0,0) 就变成了(0,0,0)从红色变为黑色!
查找表有什么用?
查找表通过给Mat重新赋值,让它变为我们想要的样子,比如我想把一个图片深色部分变为255,浅色部分改为0
那我定义一个table如下:
uchar table[256];
for(int i = 0; i < sizeof(table)/sizeof(table[0]); i++){
if(i <= 100) {
table[i] = 0;
}
if(i > 100){
table[i] = 255;
}
}
那
我将这个table应用到图片中,图片就会有很强的对比(看下面对比图,上面的是应用的table)
认识了查找表,我们看怎么遍历Mat,并且去用查找表修改每一个Mat数据。
官网提供了四种遍历图像数据(Mat)的方法
1、通过指针直接遍历(最快的方式)
Mat& traverseMethod1(Mat& mat,const uchar* const table){
//直接通过指针访问速度是最快的
//只能处理char 类型的matrices
CV_Assert(mat.depth() != sizeof(uchar));
//获取它有多少个通道
int channels = mat.channels();
//有多少行 多少列
int rows = mat.rows*channels;
int cols = mat.cols;
//是否连续存储的,如果连续存储直接一个for循环就是了
if(mat.isContinuous()){
cols *= rows;
rows = 1;
}
int i,j;
uchar* p;
for(i = 0; i < rows; ++i){
p = mat.ptr<uchar>(i);
for(j = 0; j < cols; j++){
p[j] = table[p[j]];
}
}
return mat;
}
2、通过迭代器遍历(最安全的方式,速度适中,不需要我们知道Mat尺寸,所以安全)
Mat& traverseMethod2(Mat& mat,const uchar* const table){
//安全的遍历方法,通过迭代器去遍历
CV_Assert(mat.depth() != sizeof(uchar));
const int channels = mat.channels();
switch(channels){
case 1:{
//单通道
MatIterator_<uchar> it = mat.begin<uchar>();
for(;it != mat.end<uchar>(); ++it){
*it = table[*it];
}
break;
}
case 4:{
//因为我这个图片是4通道的
MatIterator_<Vec4b> it1 = mat.begin<Vec4b>();
for(;it1 != mat.end<Vec4b>(); ++it1){
(*it1)[0] = table[(*it1)[0]];
(*it1)[1] = table[(*it1)[1]];
(*it1)[2] = table[(*it1)[2]];
(*it1)[3] = table[(*it1)[3]];
}
break;
}
}
return mat;
}
3、通过at()函数访问地址(这个明显应用于修改特定位置的元素,用它遍历肯定慢)
代码照抄官网,我没自己写
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
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;
}
4、核心函数LUT
这个是openCV提供的批量处理图像的方法,速度最快,也是我们以后最常用的
Mat& traverseMethod3(Mat& mat,const uchar* const table,Mat& outputMat){
//其实还有一个效率更低的方法 这边不介绍了 官方不推荐使用 是通过mat.at的方法 当确定行数和列数的时候再用吧
//最快的方法是openCV提供的 LUT函数 第一个参数接受一个mat输入 最后一个参数接受一个输出 中间一个参数是table 类型是mat
//我们自定义一个table
Mat lookUpTable(1,256,CV_8U);
uchar* p = lookUpTable.ptr();
for(int i =0 ; i < 256; ++i){
p[i] = table[i];
}
// Mat outputMat;
LUT(mat, lookUpTable, outputMat);
return outputMat;
}
官网也写了一些计算代码执行时间的方法,这里不介绍了,不是重点,我们知道应该用哪些接口最高效就可以了,上面代码执行的效果如上面效果图所示,大家也可以定义自己的查找表修改一些图试一试,后面我们继续学习滤波图像方面的内容。
下一篇:学习openCV滤波功能