OpenCV C++ API 矩阵常用运算

最近和Samuel成功地搭建了基于编码结构光的三维重建系统,这项技术应该说已经是很成熟的了,代码我们也从网上download下来学习,当然自己也重写了一遍。

除了系统校准,实际操作时整个流程分为图像解码和基于三角学计算三维坐标两大块,在不同地方加入不同的filter以及一些recover的过程。之前的代码沿用了OpenCV C的API。

为了配合部门其他组员,同时本着与时俱进的精神,这两天主要就是将之前的代码换成C++的API,同时借这个小项目来熟悉一下OOP。(尽管这个很没有必要)

 

对于C++的API,最常用的一个变量类型就是Mat,命名空间cv,用来存储矩阵数据。

声明一个矩阵头(习惯上我看成指针)

cv::Mat a;

 声明并初始化

int row = 3;
int col = 4;
cv::Mat a(row, col, CV_8UC1, cv::Scalar(0));

输出整个矩阵可以直接用cout。如果是3通道的矩阵,可以用CV_8UC3,赋值中Scalar设置相应的初始化值,这时候输出的矩阵一共还是3行,但是有12(=4*3)列,但是矩阵本身的数据成员cols依然是4。

遍历元素

cv::Mat a(5, 4, CV_8UC1, cv::Scalar(0));

for (int r = 0; r < a.rows; r++)
{
    for (int c = 0; c < a.cols; c++)
    {
        std::cout<<(int)a.ptr<uchar>(r)[c]<<std::endl;
    }
}

(int)a.ptr<uchar>(r)[c],这句代码调用了a的数据成员ptr(指针),指明类型是uchar(与CV_8U)对应,a.ptr<uchar>(r)指向a的第r+1行(下标从零开始),a.ptr<uchar>(r)[c]指向a的第r+1行第c+1列元素并取值,因为指针是指向uchar类型,所以显示时先将其转换为int。

常用的一些对应关系:CV_8U --- uchar, CV_16U --- ushort, CV_32F --- float, CV_64F --- double

如果是3通道,例如CV_32FC3,就在a.ptr<float>(r)的基础上偏移c*3+i,其中i = 0 ,1,2,就是说矩阵有c列,每一列包含3小列,每个元素是一个包含了3个float数据的东西,但是指针是指向float类型的,所以发生偏移时按照float的长度来计算,因此横跨c列实际上要偏移c*3个float占据的长度,再加上i,就可以访问某一个元素中第一、第二和第三个通道中的数据。

cv::Mat x(5, 4, CV_32FC3, cv::Scalar(0.0, 1.1, 2.2));

for (int r = 0; r < x.rows; r++)
{
    for (int c = 0; c < x.cols; c++)
    {
    for (int i = 0; i < 3; i++)
    {
      std::cout<<
a.ptr<float>(r)[c*3+i]<<std::endl;
    } } }

当然更一般的写法是通过x.channel()的方法获取当前矩阵的通道数。

 

常用的矩阵声明/初始化

cv::Mat a;
a.create(5,4,CV_32FC3);  //生成5行4列,float类型3通道矩阵,这个方法不能赋值
a.setTo(cv::Scalar(0.0,1.0,2.0)); //可以配合这个函数来使用

注意当赋值的通道数少于目标通道数时,默认补零,譬如上述代码中如果cv::Scalar(0.0,1.0,2.0)变为cv::Scalar(0.0,1.0),则矩阵所有元素的第三通道的值都是0.0。

OpenCV关于矩阵的操作大多数都允许带掩模进行。譬如

 

cv::Mat a;
a.create(5,4,CV_8UC1);
a.setTo(1, mask);

 

其中mask是一个和a同size的矩阵,mask中非零的位置,a的对应位置的元素会被set为1,其他部分不变。mask默认值是全非零,也就是说默认对a的所有元素都执行操作。

 

矩阵的display,可以将8U的矩阵当作图像显示出来(实际上16U也可以)。直接使用imshow()即可,类似C API中的cvImageShow().

cv::Mat k(500,500,CV_8UC1,128);
cv::imshow("k display",k);
cv::waitKey(0);
cv::destroyWindow("k display");

waitKey用于等待用户按键,destroyWindow用于销毁指定窗口。

 

矩阵元素级的常用操作

cv::Mat a(3,2,CV_8UC1,cv::Scalar(127));
cv::Mat b(3,2,CV_8UC1,cv::Scalar(128));
cv::Mat mask(3,2,CV_8UC1,255);//默认全体操作
...//修改mask获得指定掩模
double alpha;
double beta;
cv::Mat c;
cv::add(a,b,c,mask); // a+b 元素级相加,结果放到c,下面的类似
cv::scaleAdd(a, alpha, b, c, mask); // a*alpha+b, 元素级相加,其中一个矩阵带缩放
cv::addWeighted(a, alpha, b beta, c, mask); // a*alpha+b*beta, 元素级相加,两个矩阵分别带缩放系数 (还可以带偏移gamma)
cv::subtract(a,b,c,mask); // 元素级想减
cv::abs(a,c); // 取绝对值
cv::absdiff(a,b,c); // 元素级相减取绝对值 (个人很好奇为何不是absDiff而是全小写)
cv::multiply(a,b,c,mask); // 元素级乘法
cv::divide(a,b,c,mask); // 元素级除法
//类似的操作还有log,sqrt,exp,pow,min,max

cv::compare(a,b,c, cv::CMP_GT); // 元素级比较大小,结果存放在c中,条件满足则为255,否则为0,就是说c是一个元素类型CV_8UC1的矩阵
//条件关系 CMP_GT 大于 --- CMP_GE 大于等于 --- CMP_LT 小于 --- CMP_LE 小于等于 --- CMP_EQ 等于 --- CMP_NE 不等于

//元素级逻辑运算
cv::bitwise_or(a,b,c,mask);
//类似的还有bitwise_and等,注意这个不仅仅是元素级,还是位运算,例如a某个元素是128,b对应位置的元素是0,则它们进行bitwise_or的结果,对应位置是128
//如果b对应位置的元素是1,那么结果对应位置就是129(因为128最后一位是0,和1的最后一位(也就是1)相或后得到1,128的前边的位数都保留了,得到129)
//同理如果a的元素是129,b对应元素是1,那么结果该位置还是129

 

posted @ 2014-12-10 17:49  Cyrus Ho  阅读(3809)  评论(0编辑  收藏  举报