突然发现有一篇opencv操作mat数据的基础操作搞忘了,现在发出来,原来有一篇是只有操作结果,而且是用emgu写的,这里用c++实现并给出详细的代码。
数字图像处理其实就是处理二维矩阵数据。利用opencv来学习处理算法是一种比较好的方式。学习opencv,主要就是调用其中的图像处理函数来实现各种操作。如果要得到想要的处理结果,还需要对图像处理算法有一定的了解。同时,可能也有自己想写一些算法的冲动,并急切的想验证自己写的算法是否能够满足处理的要求。可是,真正到自己动手写的时候,总发现无从下手,比如,想得到图像中的ROI区域(感兴趣区域),想对图像做加减运算算,将两幅图像连接成一幅图像等等。如果要想随心所欲的按照自己的想法来操作一幅图像,则需要对opencv的mat数据操作非常熟练,建议初学者将这些基本的操作背下来,然后你会发现,你再进行这样的操作将是得心应手。
在opencv中对图像数据进行操作,我们可以简单的把mat数据看成一个二维数,里面存储的就是图像的数据,所以,对mat数组的操作就是在操作二维图像数据。下面我们来实现常见的Mat数据操作。
首先,在c++中新建一个空的控制台程序,然后,在属性管理器中添加我们之前设置好的opencv4.0配置文件,回到解决方案界面,选择源文件,右键添加新项目,然后得到一个空白的源文件,如图1和图2所示。图1添加配置好的opencv配置文件图2 添加空白的源文件
然后,就可以在这个空白的源文件中写代码了。下面用具体的代码来说明怎么操作mat数据。下面的代码都可以直接复制到自己的工程中直接运行如果涉及到要打开图像文件的,直接替换代码中的文件路径为自己电脑中的图像路劲就可以了。
int main(int argc, char** argv)
{
//构造3X3的Mat矩阵
// CV_8UC1和CV_8UC3是数据类型和通道,指8位无符号单通道和3数据,通道数据
Mat a(Size(3,3),CV_8UC1);
Mat b = Mat(Size(3,3),CV_8UC3);
cout<
cout<
system("pause");//暂停
}图3 输出构造的mat数据
图3是上面代码输出的结果,a是单通道,b是三通道,所以b是3行9列。里面的具体的值是opencv随机自动给的,因为我们还没有对mat赋值,这个不用管。再来看几个操作,
Mat mz = Mat::zeros(Size(5,5),CV_8UC1);//构造5X5的全是0的mat数据
Mat mo = Mat::ones(Size(5,5),CV_8UC1);// 构造5X5的全是1的mat数据
Mat me = Mat::eye(Size(5,5),CV_8UC1); 构造5X5的单位矩阵的mat数据
上面的代码可以直接在上面的main函数里面接着往下写。图4是输出结果
图4 上面三行代码的输出结果
接着往下写。
Mat m1 = Mat::eye(Size(3,3),CV_32F);// CV_32F表示浮点数
Mat m2 = Mat::ones(Size(3,3),CV_32F);
Mat add = m1 + m2;//mat相加
Mat sub = m1 –m2;//mat相减
Mat mul1 = m1 * 2;//mat乘一个常数
Mat mul2 = m1 + 2;//mat加一个常数
Mat m1t = m1.t();//mat矩阵转置
Mat meInv = mul2.inv();//mat矩阵的逆矩阵
int nonZeroNum = countNonZero(m1);//统计mat中非0的个数
下面我们重新写一个main函数,不自己构造mat数据,直接从图像中得到。然后对图像数据的mat进行操作
int main(int argc, char** argv)
{
Mat src1 = imread("E:\\1.bmp", 0);//0表示单通道,1表示3通道
Mat src2 = imread("E:\\2.bmp", 0);
Mat absSub;
absdiff(src1, src2, absSub);//两图相减的绝对值
namedWindow("absSub",0);
imshow("absSub", absSub);
Mat roiMat = src1(Range(0,3),Range(0,4));//取图像中的0到3行和0到4列
cout<< roiMat <
roiMat = roiMat.t();//图像矩阵转置
cout<< roiMat <
Mat addimg;
add(src1, src2, addimg);//两图相加
namedWindow("addimg",0);
imshow("addimg", addimg);
Mat addNum;
add(src1,100, addNum);//图像加一个数字
namedWindow("addNum",0);
imshow("addNum", addNum);
Mat ddWeightedImg;
cv::addWeighted(src1,0.3,src2,0.7,0, ddWeightedImg);//两图带权重相加,可以用于图像融合
namedWindow("ddWeightedImg",0);
imshow("ddWeightedImg", ddWeightedImg);
Mat subImg;
subtract(src1,src2, subImg);//两图相减
namedWindow("subImg",0);
imshow("subImg", subImg);
Mat subNum;
subtract(100,src1, subNum);//一个数字减去图像
namedWindow("subNum",0);
imshow("subNum", subNum);
Mat divideImg;
cv::divide(src1,src2, divideImg,1.0);//两图相除
namedWindow("divideImg",0);
imshow("divideImg", divideImg);//不能直接显示,因为是浮点数,需要转换
Mat mulImg;
cv::multiply(src1,src2, mulImg,1.0);//两图相乘
namedWindow("mulImg",0);
imshow("mulImg", mulImg); //不能直接显示,因为是浮点数,需要转换
Mat transposeImg;
cv::transpose(src1, transposeImg);//图像转置
namedWindow("transposeImg", 0);
imshow("transposeImg", transposeImg);
Mat tImg;
tImg =src1.t();//图像转置,和上面的转置一样
namedWindow("tImg", 0);
imshow("tImg", tImg);
Mat copyImg;
src1.copyTo(copyImg);//图像复制
namedWindow("copyImg",0);
imshow("copyImg", copyImg);
Mat maxImg;
cv::max(src1,src2, maxImg);//两图中的最大值
namedWindow("maxImg",0);
imshow("maxImg", maxImg);
Mat inRangeImg;
cv::inRange(src1,40,100, inRangeImg);//相当于在一定范围内的值二值化
namedWindow("inRangeImg",0);
imshow("inRangeImg", inRangeImg);
int r = src1.rows;
int c = src2.cols;
Mat r1 = src1.row(10);
Scalar meanValue = cv::mean(src1);//图像均值
cout<
Mat colRangeImg = src1.colRange(0,10);//取图像中一定列
Mat rowRangeImg = src1.rowRange(0,10); //取图像中一定行
纵向合并图像
Mat colMergeImg =Mat(src1.rows*2,src1.cols,0);
Mat subM = colMergeImg.rowRange(0,src1.rows);
src1.copyTo(subM);
subM = colMergeImg.rowRange(src1.rows, src1.rows*2);
src2.copyTo(subM);
namedWindow("colMergeImg",0);
imshow("colMergeImg", colMergeImg);
///横向合并矩阵
Mat rowMergeImg =Mat(src1.rows,src1.cols*2,0);
subM = rowMergeImg.colRange(0,src1.cols);
src1.copyTo(subM);
subM = rowMergeImg.colRange(src1.cols, src1.cols*2);
src2.copyTo(subM);
namedWindow("rowMergeImg",0);
imshow("rowMergeImg", rowMergeImg);
第二种在纵向合并两个图像的方法,横向合并类似,可以先对图像做转置,然后合并,最后再转置回来
Mat mergeImg2;
mergeImg2.push_back(src1);
mergeImg2.push_back(src2);
namedWindow("mergeImg2", 0);
imshow("mergeImg2", mergeImg2);
double minValue,maxValue;
Point minLoc,maxLoc;
minMaxLoc(src1,&minValue,&maxValue,&minLoc,&maxLoc);//取图像矩阵中的最大最小值和位置
cout<
cout<
cout<
cout<
Mat mean;
Mat stddev;
meanStdDev(src1, mean, stddev);//求图像矩阵的均值和方差
cout << "mean==" << mean << endl;
cout << "stddev==" << stddev << endl;
waitKey(0);
system("pause");
}图5 输出结果显示
上面包括了mat数据的主要操作命令,如果能够把这些命令记住了,要对图像做一般的操作基本上没有什么问题了。
此外,如果要访问图像中的每一个像素值,可以采用下面指针的方式,速度是最快的。下面的代码只是演示了怎么使用指针访问像素,没有什么特别的目的。
Mat dyImg = Mat(src1.rows, src1.cols,0);
for(int i=0;i
{
uchar* srcdata = src1.ptr(i);
uchar* dydata = dyImg.ptr(i);
for(int j=0;j< src1.cols;j++)
{
double tempValue = double(srcdata [j]) + 20.0;
if(tempValue < 0)
dydata[j] = 0;
else if(tempValue > 255)
dydata[j] = 255;
else
dydata[j] = tempValue;
}
}
namedWindow("dyImg ",0);
imshow("dyImg ", dyImg);