OpenCV中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage。这三种类型都可以代表和显示图像,区别是:Mat类型侧重于计算,数学性较高,OpenCV对Mat类型的计算进行了优化;CvMat和IplImage类型更侧重于“图像”,opencv对其中的图像操作(缩放、单通道提取、图像阈值操作等)进行了优化。在opencv2.0之前,opencv是完全用C实现的,但是,IplImage类型与CvMat类型的关系类似于面向对象中的继承关系。实际上,CvMat之上还有一个更抽象的基类----CvArr,这在源代码中会常见。
1. opencv文档中明确声明,CvMat已经过时了(CvMat is now obsolete, consider using Mat instead)不建议用;
2. 派生关系:CvArr -> CvMat -> IplImage
3. Mat用的一套东西是imread,imshow等,有别于CvArr及其子类的cvLoadImage(),cvShowImage()...
Mat类型:矩阵类型,Matrix。
在openCV中,Mat是一个多维的密集数据数组。可以用来处理向量和矩阵、图像、直方图等等常见的多维数据。
Mat有3个重要的方法:
1、Mat mat = imread(const String* filename); 读取图像
2、imshow(const string frameName, InputArray mat); 显示图像
3、imwrite (const string& filename, InputArray img); 储存图像
Mat类型较CvMat与IplImage类型来说,有更强的矩阵运算能力,支持常见的矩阵运算。在计算密集型的应用当中,将CvMat与IplImage类型转化为Mat类型将大大减少计算时间花费。
1.像素值的读取可使用at()函数:
uchar value = grayim.at<uchar>(i,j); //读出第i行第j列像素值
graym.at<uchar>(i,j) = 128; //将第i行第j列像素值设置为128
2.使用Mat的成员函数ptr<>()
uchar* data = image.ptr<uchar>(j);
3.使用迭代器遍历图像
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
for (; it != itend; ++it)
{
(*it)[0] = (*it)[0] / div*div + div / 2;
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
}
4.图像的载入、显示和输出到文件。 imread、imshow、imwrite
5.滑动条的创建和使用。
int createTrackbar(const string& trackbarname,const string& winname,int* value,int count,TrackbarCallback onChange=0,void* userdata=0);
第一个参数::轨迹名称
第二个参数:窗口名字
第三个参数:一个指向整型的指针,表示滑块位置。
第四个参数:int类型的count表示滑块可以达到的最大位置值。
第五个参数:指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调,默认值是0。
第六个参数:用户传给回调函数的数据,用来处理轨迹条事件。如果第三个参数value实参是全局变量的话,完全可以不用去管userdata参数。
1、Mat类常用的构造方法
Mat();//无参数构造
Mat(int rows, int cols, int type);//创建行数为rows,列数为cols,类型为type的图像
Mat(Size size, int type);//创建大小为size,类型为type的图像
Mat(int rows, int cols, int type, const Scalar& s);//创建创建行数为rows,列数为cols,类型为type的图像,且全部元素(像素)初始化为 s
Mat(Size size, int type, const Scalar& s);//创建大小为size,类型为type的图像,且全部元素(像素)初始化为 s
Mat(const Mat& m);//将m赋值给新创建的对象,是浅拷贝
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
//创建空矩阵
Mat img1;
//创建6x6的8位单通道矩阵(图像)
Mat img2(6, 6, CV_8UC1);
//创建7x7的8位三通道矩阵(图像)
Mat img3(cv::Size(7, 7), CV_8UC3);
//创建8x8的32位三通道矩阵(图像),并且用cv::Scalar(0,255)填充
Mat img4(8, 8, CV_8UC3,cv::Scalar(0,255));
//创建7x7的8位三通道矩阵(图像),并且用填充cv::Scalar(1, 2, 3)填充
Mat img5(cv::Size(7, 7), CV_8UC3, cv::Scalar(1,2,3));
Mat img6(img2);
cout << img1 << endl;
cout << img2 << endl;
cout << img3 << endl;
cout << img4 << endl;
cout << img5 << endl;
cout << img6 << endl;
return 0;
}
6.常用数据结构和函数
1)Point类
Point point;
point.x = 10;
point.y = 8;
Point_<int>、Point2i、Point互相等价;Point_<float>、Point2f互相等价。
2)颜色表示 Scalar类
Scalar(b,g,r);
3)尺寸:Size类
typedef Size_<int> Size2i;
typedef Size2i Size;
Size_<int>、Size2i、Size三个类等价
4)矩形:Rect类
Rect类的成员变量 x,y,width,height,成员函数Size()返回尺寸,area()返回面积,contains(Ponit)点是否在矩形中,inside(Rect)矩形是否在该矩形内,tl()返回左上角点坐标,br()返回右下角点坐标;
Rect rect = rect1 & rect2;求交集
Rect rect = rect1 | rect2;求并集
Rect rectShift = rect + point;
Rect rectScale = rect + size;
5)颜色空间转换:cvtColor()函数
可以实现RGB颜色向HSV、HSI等颜色空间的转换,也可以转换成灰度颜色。
cvtColor(srcImage,dstImage,COLOR_GRAY2BGR);
问题:
如果Mat 中想存小数,那么声明是就要用CV_32FC1等浮点数的类型,并且在访问像素的时候,指向每一行(i行)的指针:
不再是: uchar *data = src.ptr<uchar>(i); 了 (uchar 是0~255的无符号整数)
而是用:float *data = src.ptr<float>(i)。(以前不懂也没注意这个,程序一直出错)。
PS:因为再写颜色相关图的程序,需要保存一个掩码mark 矩阵,其中每个元素存一个【0~1】的权重,用以前的遍历图片像素的代码改的,结果总不对,后来才发现是新创建的矩阵的元素用的 uchar ,是无符号整型,不能存小数的,才恍然大悟,也才有了这篇blog,内容比较简单,仅供自己学习,也供有需求的人参考。
TYPE表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值
CV_8UC1 CV_8UC2 CV_8UC3 CV_8UC4
CV_8SC1 CV_8SC2 CV_8SC3 CV_8SC4
CV_16UC1 CV_16UC2 CV_16UC3 CV_16UC4
CV_16SC1 CV_16SC2 CV_16SC3 CV_16SC4
CV_32SC1 CV_32SC2 CV_32SC3 CV_32SC4
CV_32FC1 CV_32FC2 CV_32FC3 CV_32FC4
CV_64FC1 CV_64FC2 CV_64FC3 CV_64FC4
这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。
例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2.
C1,C2,C3,C4则表示通道是1,2,3,4
type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth
depth
矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
将type的预定义值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
elemSize
矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes
elemSize1
矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels
Mat的常见属性
data uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。
dims 矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.
rows 矩阵的行数
cols 矩阵的列数
size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
关于将mat数据存放到xml中
void CapVideoFace()
{
int indexNum = 0; //跳帧数.
VideoCapture capture;
cv::Mat frame;
capture.open("resource/999.mp4");//读取视频文件
//capture.open("rtsp://admin:admin123@10.129.74.198:554/cam/realmonitor?channel=1&subtype=1"); //读取rtsp流
if (!capture.isOpened())
{
LoggerN("capture open failed.");
return;
}
while (capture.read(frame)) {
//cv::Mat frame = imread("resource/2.jpg",1);
if (frame.data != NULL && indexNum++%3 ==0) {
FileStorage fs("xuchao.xml", FileStorage::WRITE);
fs << "vocabulary" << frame;
fs.release();
FileStorage fsRead("xuchao.xml", FileStorage::READ);
Mat mat_vocabulary;
fsRead["vocabulary"] >> mat_vocabulary;
fsRead.release();
indexNum == 0;
std::string strPic = Mat2Base64(frame, "jpg");
char* pBuffer = new char[3*1024*1024];
memcpy(pBuffer,strPic.c_str(),strPic.length()); detector[detectorIndex++].Put(pBuffer);
if(detectorIndex == THREADNUM)
detectorIndex = 0;
}
}
}