Opencv系列1.4--图像和大型数据类型
//用cv::Mat直接实例化,此时数组无大小无数据类型
cv::Mat m;
//可用create()分配数据,行数3,列数10,数据类型三通道32位单精度,表示二维对象
m.create(3,10,CV_8UC1);
//数组类型决定其元素类型,有效的数组类型需确定元素种类和通道数
//所有类型已通过头文件预定义CV_{8U,16S,16U,32F,32S,64F}C{1,2,3}
//例如CV_32FC3,表示32为单精度三通道数组;
//设定第一通道的值1.0,第二通道0.0,第三通道1.0
m.setTo(cv::Scalar(1.0f,0.0f,1.0f));
//等同于cv::Mat m(3,10,CV_32FC3,cv::Scalar(1.0f,0.0f,1.0f));
通过从另一个cv::Mat复制数据构造,通过行列感兴趣范围构建,或通过cv::Rect指定感兴趣范围构建;
//cv::Mat(const Mat& mat)
//复制构造数组
const cv::Mat img=cv::imread("a.jpg");
cv::imshow("Original Img",img);
cv::waitKey(0);
cv::Mat img_copy(img); //复制构造
cv::imshow("copy img",img_copy);
cv::waitKey(0);
//cv::Mat(const Mat& mat, const cv::Range rows, cv::Range cols);
//获取数组的子数组
const cv::Mat img=cv::imread("a.jpg");
cv::imshow("Original Img",img);
cv::waitKey(0);
//利用cv::Range构造数组
int height=img.cols;
int width = img.rows;
std::cout<<"cols"<<height<<"rows"<<width;
const cv::Range rows=cv::Range(100,800);
const cv::Range cols=cv::Range(100,800);
cv::Mat img_sub(img, cols, rows);
cv::imshow("Sub Img",img_sub);
cv::waitKey(0);
//利用Rect构造数组
cv::Rect rec1=cv::Rect(400,400,600,600);
cv::Mat img_rect(img,rec1);;
cv::imshow("Rect Img",img_rect);
cv::waitKey(0);
cv::Mat m(cv::Mat::zeros(5,5,CV_8UC1));
std::cout<<"Matrix zeros:"<<std::endl<<m<<std::endl;
cv::Mat m1(cv::Mat::ones(4,6,CV_8UC1));
std::cout<<"Matrix ones:"<<std::endl<<m1<<std::endl;
cv::Mat m2(cv::Mat::eye(4,6,CV_8UC1));
std::cout<<"Matrix eye:"<<std::endl<<m2<<std::endl;
访问单个元素的两个主要方法是通过位置或遍历;
直接访问的方法是根据成员函数,at<数据类型>(位置);
cv::Mat m2(cv::Mat::eye(4,6,CV_32FC1));
std::cout<<"Matrix eye:"<<std::endl<<m2<<std::endl;
std::cout<<m2.at<float>(3,3);
//at<数据类型>(位置)
对于多通道数组,方法是类似的:多通道时建议使用cv::Vec<>
cv::Mat img=cv::imread("a.jpg",1);
std::cout<<"img channels: "<<img.channels()<<std::endl;
std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[0];
std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[1];
std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[2];
通过指针函数ptr<>()进行访问,速度最快:
为访问二维数组,我们可以提取一个指向数组具体行的指针,ptr<>()就是cv::Mat中的模板函数。ptr<>()用一个类型名进行实例化,整数参数表示你想访问并且指针指向的行数,函数返回一个指针,指向所建数组中元素类型,也就是说,如果数组类型是CV_32FC3,那么返回值是类型float*。因此,若给定一个三通道矩阵mtx,元素类型float,构造mtx.ptr<Vec3f>(3)返回一个指针,指向矩阵mtx第三行中第一个元素的第一个通道,这是访问数组最快的方式。
int sz[3]={4,4,4};
cv::Mat m(3,sz,CV_32FC3);
cv::randu(m,-1.0f,1.0f);
float max=0.0f;
cv::MatConstIterator<cv::Vec3f> it=m.begin();
while (it!=m.end()){
len2=(*it)[0]*(*it)[0]+(*it)[1]*(*it)[1]+(*it)[2]*(*it)[2];
if(len2>max)max=len2;
it++;
}
需要注意的是,当我调用这些函数时,数组中的数据并未复制到新数组。
Opencv中,进行操作时可能出现溢出等情况,尤其是无符号类型数据进行减法时,因此构建cv::saturate_cast<>()。例如:
uchar& Vxy = m0.at<uchar>(y,x);
Vxy = cv::saturate_cast<uchar>((Vxy-128)*2+128);
在该例子中,我们首先分配一个引用Vxy直向一个8位数组m0中的元素。当我们从这个数组减去128,然后乘以2,在增加128时,C算法规则会对Vxy-128分配32位有符号整数,然后进行乘法和加法。如果Vxy的原始值是10,那么最后结果是-108,将不满足Vxy 8位无符号数据类型,因此必须使用cv::saturate_casts<uchar>(),当低于无符号数据下限时,变为0。
通常用于0项远多于非零项的数组,一般出现在线性代数的稀疏矩阵或者直方图。稀疏数组仅存储那些存在的数据,所以节省空间,实际中稀疏对象太大很难以稠密数组表示;稀疏表示方法的缺点是,计算太慢。
cv::SparseMat类似于cv::Mat,它采用哈希表方式存储数据。
稀疏数组和稠密数组最大的区别在于,如何访问元素。
稀疏数组提供四种访问方法:cv::SparseMat::ptr(),cv::SparseMat::ref(),cv::SparseMat::value(),cv::SparseMat::find();
cv::SparseMat_<>和cv::Mat_<>,通过模板类则不需要使用他们成员的模板;
例如,定义矩阵cv::Mat m(10,10,CV_32FC2);则访问每个元素都需指定矩阵的类型,m.at<Vec2f>(i0,i1)=cv::Vec2f(x,y);
如果用模板类定义m,则不需要具体指明类型使用at(),
cv::Mat_<Vec2f> m(10,10);
m.at(i0,i1)=cv::Vec2f(x,y);
这种模板结构简化了代码;
以上两种方法,效率上相同,但是更建议第二种方法;