Mat对象
Mat对象与IplImage对象
Mat对象OpenCV2.0之后引进的图像数据结构、自动分配内存、不存在内存泄漏的问题,是面向对象的数据结构,分了两部分,头部和数据部分。
IplImage是从2001年OpenCV发布之后就一直存在,是C语言风格的数据结构,需要开发者自己分配与管理内存,对大的程序使用它容易导致内存泄漏问题。
//Mat对象构造函数与常用方法
// Mat()
// Mat(int rows,int cols,int type)
// Mat(Size size,int type)
// Mat(int rows,int cols,int type,const Scalar &s)
// Mat(Size size,int type,const Scalar &s)
// Mat(int ndims,const int *sizes,int type)
// Mat(int ndims,const int *sizes,int type,const Scalar &s)
//
// 常用方法:
// void copyTo(Mat mat)
// void convertTo(Mat dst,int type)
// Mat clone()
// int channels()
// int depth()
// bool empty()
// uchar* ptr(i=0)
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int main(int argc,char** argv){
Mat src;
src = imread("");
if(src.empty()){
cout<<"could not load image..."<<endl;
return -1;
}
namedWindow("input",CV_WINDOW_AUTOSIZE);
imshow("input",src);
/*
Mat dst;
dst = Mat(src.size(),src.type());
dst = Scalar(127,0,255);//给图像赋值
namedWindow("output",CV_WINDOW_AUTOSIZE);
imshow("output",dst);
*/
Mat dst = src.clone();//克隆
src.copyTo(dst);//复制
cvtColor(src,dst,CV_GRAY2GRAY);
src.channels();//多少通道,函数返回是一个值
const uchar* firstRow = dst.ptr<uchar>(0);
cout<<"first pixel value:"<<*firstRow<<endl;
int cols = dst.cols;
int rows = dst.rows;
namedWindow("output",CV_WINDOW_AUTOSIZE);
imshow("output",dst);
//Mat(int rows,int cols,int type,const Scalar &s)的用法
Mat M(3,3,CV_8UC3,Scalar(0,0,255));
//cout<<"M="<<endl<<M<<endl;//注释掉,不然很费性能
imshow("output",M);
waitKey(0);
return 0;
}
Mat对象使用
部分复制:一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分
Mat A = imread(imgFilePath);
Mat B(A);//只复制
完全复制:如果想把Mat对象的头和数据部分一起复制,可以通过如下两个API实现
Mat F = A.clone();或Mat G; A.copyTo(G);
四个要点:
输出图像的内存是自动分配的
使用OpenCV的C++接口,不需要考虑内存分配问题
复制操作和拷贝构造函数只会复制头部分
使用clone与copyTo两个函数实现数据完全复制
Mat对象创建:
cv::Mat::Mat构造函数
Mat M(2,2,CV_8UC3,Scalar(0,0,255));
其中前两个参数分别表示行(row)跟列(column)、第三个参数CV_8UC3中的8表示每个通道占8位,UC表示unchar无符号的char类型,3表示3个通道。第四个参数是向量表示初始化每个像素值是多少,向量长度对应通道数目一致
创建多维数组cv::Mat::create
int sz[3] = {2,2,2};
Mat L(3,sz,CV_8UC1,Scalar::all(0));
代码:
Mat m1;
m1.create(src.size(),src.type());
m1=Scalar(0,0,255);
Mat M;
M.create(4,3,CV_8UC2);
M = Scalar(127,127);
cout<<"M="<<endl<<" "<<M<<endl<<endl;
uchar* firstRow = M.ptr<uchar>(0);
printf("%d",*firstRow);
Mat定义数组
Mat C = (Mat_<float>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
filter2D(src,dst,src.depth(),kernel);
小技巧:输出成纯黑色的图片
Mat m2 = Mat::zeros(src.size(),src.type());
调整大小
Mat m = Mat::zeros(2,2,CV_8UC1);
输出对角线为1的对称矩阵
Mat m1 = Mat::eye(2,2,CV_8UC1)
图像操作
读写图像
imread可以指定加载为灰度或者RGB图像
imwrite保存图像文件,类型由扩展名决定
读写像素
读一个GRAY像素点的像素值(CV_8UC1)
Scalar intensity = img.at<uchar>(y,x) 或者 Scalar intensity = img.at<uchar>(Point(x,y))
读一个RGB像素点的像素值
Vec3f intensity = img.at<Vec3f>(y,x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];
namedWindow("input",CV_WINDOW_AUTOSIZE);
imshow("input",src);
//单通道
cvtColor(src,gray_src,CV_BGR2GRAY);
namedWindow("output",CV_WINDOW_AUTOSIZE);
imshow("output",gray_src);
int height = gray_src.rows;
int width = gray_src.cols;
for(int row = 0; row < height; row++){
for(int col = 0; col < width; col++){
int gray = gray_src.at<uchar>(row,col);
gray_src.at<uchar>(row,col) = 255 - gray;//反插
}
}
//三通道
Mat dst;
dst.create(src.size(),src.type());
height = src.rows;
width = src.cols;
int nc = src.channels();//获取通道数
for(int row = 0; row < height; row++){
for(int col = 0; col < width; col++){
if(nc == 1){
int gray = gray_src.at<uchar>(row,col);
gray_src.at<uchar>(row,col) = 255 - gray;//反插
}else if(nc == 3){
int b = dst.at<Vec3b>(row,col)[0];
int g = dst.at<Vec3b>(row,col)[1];
int r = dst.at<Vec3b>(row,col)[2];
dst.at<Vec3b>(row,col)[0] = 255 - b;
dst.at<Vec3b>(row,col)[1] = 255 - g;
dst.at<Vec3b>(row,col)[2] = 255 - r;
gray_src.at<uchar>(row,col) = max(r,max(b,g));//将gray_src的灰度取值最大
gray_src.at<uchar>(row,col) = min(r,min(b,g));//将gray_src的灰度取值最小
}
}
}
注意:
Vec3b byte类型
Vec3f float类型
Vec3b与Vec3f
Vec3b对应三通道顺序是blue,green,red的uchar类型数据
Vec3f对应三通道的float类型数据
把CV_8UC1转换到CV32F1实现如下:
src.convertTo(dst,CV_32F);
//获取像素,反插函数
bitwise_not(src.dst);
修改像素值
图像混合
理论-线性混合操作
g(x)=(1-a)f0(x)+af1(x) 其中a的取值范围0~1之间
相关API(addWeighted)
void cv::addWeighted(InputArray src1, 参数1:输入图像Mat-src1
double alpha, 参数2:输入图像src1的alpha值
InputArray src2, 参数3:输入图像Mat-src2
double beta, 参数4:输入图像src2的alpha值
double gamma, 参数5:gamma值
OutputArray dst, 参数6:输出混合图像
int dtype=-1)
dst(I) = saturate(src1(I)*alpha + src2(I)*beta + gamma)
注意:两张图像的大小和类型必须一致才可以
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc,char** argv){
Mat src1,src2,dst;
double alpha = 0.5;
src1 = imread("");
src2 = imread("");
if(!src1.data){
cout<<"could not load image..."<<endl;
return -1;
}
if(!src2.data){
cout<<"could not load image..."<<endl;
return -1;
}
if(src1.rows==src2.rows && src1.cols==src2.cols && src1.type() == src2.type()){
addWeighted(src1,alpha,src2,(1.0-alpha),0,0,dst);
//直接相加
//add(src1,src2,dst,Mat());
//相乘
//multiply(src1,src2,dst,1.0)
namedWindow("src1",CV_WINDOW_AUTOSIZE);
imshow("src1",src1);
namedWindow("src2",CV_WINDOW_AUTOSIZE);
imshow("src2",src2)
namedWindow("blend demo",CV_WINDOW_AUTOSIZE);
imshow("blend demo",dst);
}else{
printf("could not blend images, the size is not same...\n");
return -1;
}
waitKey(0);
return 0;
}