OpenCV里常用的数据结构(Mat,Mask,Scalar,Range,InputArray,OutputArray等)

来源:互联网 发布:nginx lua 模块变量 编辑:程序博客网 时间:2024/05/20 15:59

一、读取,显示和保存图像

在这里使用Mat做些简单的图像操作:读取、显示和保存。需要引入两个头文件:定义了图像数据结构的核心库core.hpp和包含了所有图形接口函数的highgui头文件。
#include"opencv2/core/core.hpp"#include"opencv2/highgui/highgui.hpp"#include<iostream>int main(){//创建空图像,并读取输入图像cv::Mat image;image = cv::imread("morph01.jpg");if (image.empty()) {  //错误处理std::cout << "图像打开发生错误"<<std::endl;return -1;}//显示图像cv::namedWindow("Image");cv::imshow("Image",image);cv::waitKey();//保存图像cv::imwrite("output.bmp",image);return 0;}

二、图像和矩阵数据结构Mat

cv::Mat有两个必不可少的组成部分:一个头部和一个数据块。头部包含了矩阵的所有相关信息(大小、通道数量、数据类型等)。
访问头部的某些属性,可通过使用size、cols、rows、channels、或depth。size是代表图像大小的数据结构,保存图像宽高信息;cols和rows代表图像列数与行数;channels代表图像通道数,灰度图为1,彩色图为3;depth代表像素的存储类型,比如CV_8U,CV_8S,CV_32F,CV64F等。数据块包含了图像中所有像素的值,头部有一个指向数据块的指针,即data属性。
Mat采用了很巧妙的内存管理机制,因此支持高效的内存使用。一个重要的属性即是,除非有明确要求,内存块才会被复制,实际上,大多数操作仅仅复制了cv::Mat的头部,因此多个对象会同时指向同一个数据块。
新创建一个Mat对象,默认大小为0:
cv::Mat image;
也可以指定大小:
cv::Mat image(240,320,CV_8U,100);
对于彩色图像,因为每个元素包含多个值,因此OpenCV引入了一个简单的数据结构cv::Scalar,用于在调用函数时传递像素值。该结构通常包含一个或三个值:
cv::Mat image(240,320,CV_8UC3,cv::Scalar(0,0,255));
也可以用Size结构来创建新图像:
cv::Mat image(cv::Size(320,240),CV_8UC3);
也可以调用create方法来创建新图像,准确的说是分配图像的数据块,因为调用它的对像已经存在。如果新图像的大小和类型与原来的相同,就不会重新分配内存,提高了性能。
//重新分配一个新图像//(仅在大小或类型不同时)image.create(200,200,CV_8U);
Mat这种数据结构通过实现计数引用和浅复制,避免了C++动态内存分配中经常发生的内存泄漏问题。计数引用的例子可参考C++11中的智能指针的原理。浅复制即两个图像之间赋值时,图像数据(即像素)并不会被复制,此时两个图像都指向同一个内存块。
下面的操作都不会复制图像数据,而指向同一块图像数据:
cv::Mat image2(image1);//或者image2=image1;
至于明确要求要对图像内容做深复制,即创建另一个副本。可以调用copyTo或clone方法:
image1.copyto(image2);//或者cv::Mat image2=image1.clone();
如果拷贝后的图像数据类型发生了改变,可使用convertTo方法,注意这两个图像通道数必须相同:
//转换成浮点型图像[0,1]image1.convertTo(image2,CV_32F,1/255.0,0.0);
在OpenCV参考文档中,有很多函数使用cv::InputArray类型作为输入参数。cv::InputArray类型是一个简单的代理类,用来概括OpenCV中数组的概念,避免同一个函数因为使用了不同类型的输入参数而出现了多个不同的版本。也就是说你可以在参数中使用Mat对象或其他类型比如std::vector,cv::Scalar和cv::Vec,InputArray只是一个接口,因此不能在代码中显式地定义它。此外相对应地,cv::OutputArray用来指定某些函数的返回数组。
OpenCV2中已使用Mat替代了早期的IplImage结构(现在仍可使用),将IplImage结构转换成cv::Mat对象非常容易:
IplImage* iplImage=cvLoadImage("hello.bmp");cv::Mat image(iplImage,false);
第二个参数默认值为false,代表不复制数据,若需复制,可把它设置为true。
不建议使用这个过时的数据结构,如果使用,也有一个更安全的做法来避免产生悬挂指针:
cv::Ptr<IplImage> iplImage=cvLoadImage("hello.bmp");

三、感兴趣区域ROI

ROI实际上就是一个cv::Mat对象,它与它的父图像指向同一个数据缓冲区,并且有一个头部信息表示ROI的坐标。定义ROI的一个方法是使用cv::Rect,正如名称所示,它描述了一个矩形区域。ROI还可通过cv::Range结构来描述,它可以表示行和列的值域。
cv::Mat imageROI(image, cv::Rect(image.cols-image2.cols, 0, image2.cols, image2.rows));
或者:
cv::Mat imageROI = image(cv::Range(0, image2.rows), cv::Range(image.cols - image2.cols, image.cols));

在这里将image2嵌入到image的某个位置:

image2.copyTo(imageROI);

结果如下:


四、图像掩码Mask

OpenCV中有些函数参数包含掩码,函数通常对图像中所有的像素进行操作,通过定义掩码可以限制这些函数的作用范围,或者调制两个图像同位置像素对目标点像素的贡献权值。所以,掩码是一个8位图像,它是二值的或者是灰度图。如果掩码中某个位置的值不为0,在这个位置上的操作就会起作用;如果掩码中某些像素位置的值为0,那么对图像中相应位置的操作将不起作用。如上例,copyTo函数就有一个可选的输入参数Mask。
不加Mask:

加Mask,增加及更改代码如下:
cv::Mat mask;mask = cv::imread("333.jpg",CV_LOAD_IMAGE_GRAYSCALE);image2.copyTo(imageROI,mask);
mask图像:


加Mask,copyto结果如下:


0 0
原创粉丝点击