- 问:Mat是什么?
- 答:个人见解-> Mat 是一种“容器”,用来装不同类型的数据。
- 问:可以装哪些类型的数据?
- 答:因为 Mat在手册的 Basic Structure 下,因此这个要看 手册中的解释:
上图中的第一段可以这样解释:
名称 | 数据类型 |
OpenCV中原始(primitive)的数据类型 | unsigned char, bool, signed char, unsigned short, signed short, int, float, double |
统一叫做:元组(tuple) | CV_< bit-depth>{UlSlF}C(< number_of_channels>) |
例子 | uchar ~ CV_8UC1; |
例子 | 有三个浮点元素的元组 ~ CV_32FC3 |
- 因此,Mat中可以装的数据是:元组。
扯了这么多还没看到 Mat 啊。
好了,先看一下手册中Mat的截图:
- 注意:里面有两句话 a lot of methods;// other members …
意思就是,这只是不完全展示。
但是,手册中的这一页的后面全是关于Mat的各种细节。 - 问:那我只是想用一下,不关心更多的细节,需要知道信息什么呢?
- 答:请继续往下看。
- 首先,看如何初始化一个 Mat
手册中给出的初始化方法有: - 用哪一种呢?
根据个人喜好以及实际应用需求。
但是在初始化的时候有各种坑等着大家,这就要自己去一步步填坑了。
下面给出几种本人常用的初始化方法:
// initialization of Mat
int m = 300;
int n = 200;
Mat m1 = Mat::zeros(m,n,CV_8U);
m1 = 255*Mat::eye(Size(m,n),CV_8U);
imshow("m1",m1);
// 利用 函数获取 结构元
Mat ker = getStructuringElement(MORPH_CROSS,Size(3,3),Point(0,0));
cout << endl<<
"to_string(ker.at<uchar>(Point(x,y))) is:" <<endl;
cout << "注意:这里不知道为什么,结构元不能直接显示成数字,"<<endl;
cout <<" 因此,把每个元素转换成 string 然后再显示"<<endl;
for (int y=0; y < ker.rows; y++)
{
for (int x = 0; x < ker.cols; x++)
{
cout << to_string(ker.at<uchar>(Point(x,y))) <<" ";
}
cout <<endl;
}
cout <<endl
<< "利用 Scalar 对Mat 进行赋值"<<endl;
Mat roi(Size(10,10),CV_8UC3);
roi = Scalar(0,255,0);
imshow("with box",roi);
Mat m2(Size(10,10),CV_8U);
m2 = 20;
imshow("m2",m2);
- 问: 代码中
ker.at<uchar>(Point(x,y))
是什么意思? - 答:这就是下面要说的,如何获取元素,以及元素的位置。 下面先看一个图示:
- 将Mat看成矩阵的话,点的位置按照矩阵中的顺序进行遍历
- 将Mat看做图像的话,点的位置按照Point(x,y)的顺序进行遍历(因为OpenCV就是用来处理图像的工具包啊。)
- 下面看具体的例子:
// 对Mat中的分量进行遍历的不同方法
// 主要是 对于位置的理解。
cout <<endl<<"注意:这里初始化的时候使用的是 double"<<endl;
cout<<" 因此,在调用 at 和 ptr 的时候也需要指定 为 double "<<endl;
Mat m2 = (Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9);
cout << "(Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9) is: " <<endl;
cout << "print by: m2.at<double>(Point(x,y)) "<<endl;
for (int y=0; y < m2.rows; y++)
{
for (int x = 0; x < m2.cols; x++)
{
cout << m2.at<double>(Point(x,y)) <<" ";
}
cout <<endl;
}
cout << "print by: m2.at<double>(i,j) "<<endl;
for (int i = 0; i < m2.rows; i++)
{
for (int j = 0; j < m2.cols; j++)
{
cout << m2.at<double>(i,j)<<" ";
}
cout <<endl;
}
cout << "print by: double* p_m2 = m2.ptr<double>(y); "<<endl;
cout << " cout << p_m2[x] "<<endl;
for (int y =0; y < m2.rows; y++)
{
double* p_m2 = m2.ptr<double>(y);
for (int x = 0; x < m2.cols; x++)
{
cout << p_m2[x]<<" ";
}
cout <<endl;
}
- 我想对不同类型的 Mat 进行赋值,如何做呢?
- 下面给出例子,划重点:一定要搞清楚Mat这个瓶子里装的是什么药,不然是会出错了。也就是说,在赋值的时候,一定要与Mat内存放的数据类型相一致。
// 下面展示如何对Mat进行局部赋值
m1 = Mat::zeros(m , n , CV_8UC2);
Vec2b pixel;
pixel[0] = 255;
pixel[1] = 0;
// 这里得到的结果应该是只有右上角为白色,其他地方为黑色
for (int y=0; y < m1.rows; y++)
for (int x = y; x < m1.cols; x++)
m1.at<Vec2b>(Point(x,y)) = pixel;
cout << endl
<<"imshow 只能展示通道为 1,3,4 的图像,这里 m1 只有两个通道"<<endl;
cout << "因此,先利用 split 将 m1 拆开,然后只展示 第一层"<<endl;
Mat mv[2];
split(m1,mv);
imshow("m1",mv[0]);
- 那么,如果对于数据类型使用不当,会出现什么错误呢?
- 看下面的例子:
void test_type2(void){
int m = 3;
int n = 2;
cout <<"m1 初始化的时候虽然用了 CV_8U,但是 前面乘了一个 -1"<<endl;
cout <<"因此 m1 的 type() 仍然是 0 "<<endl;
cout <<"不论在输出的时候使用 uchar,还是 char 都全部为 0 "<<endl;
Mat m1 = -1*Mat::ones(m,n,CV_8U);
cout <<"m1.type() is: "<< m1.type()<<endl;
cout <<endl<<"利用 m1.at<uchar>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<uchar>(i,j))<<" ";
cout <<endl;
}
cout <<endl<<"利用 m1.at<char>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<char>(i,j))<<" ";
cout <<endl;
}
cout <<endl;
cout <<"m1 使用 CV_8S 初始化"<<endl;
cout <<"m1 的 type() 是 1 "<<endl;
cout <<"使用 uchar 输出的结果是 255"<<endl;
cout <<"使用 char 输出的结果是 -1 "<<endl;
m1 = -1*Mat::ones(m,n,CV_8S);
cout <<"m1.type() is: "<< m1.type()<<endl;
cout <<endl<<"利用 m1.at<uchar>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<uchar>(i,j))<<" ";
cout <<endl;
}
cout <<endl<<"利用 m1.at<char>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<char>(i,j))<<" ";
cout <<endl;
}
}
上面的例子就是使用容器装错内容的下场,得到意想不到的结果。
关于 Mat还有很多需要说明的内容,但是,还是在需要的时候查比较靠谱。
放大招:所有的代码如下:
// csdn_code.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
//#define IMG_PATH "..//figures//12.jpg"
#define IMG_PATH "..//figures//lotus.jpg"
void test_type2(void){
int m = 3;
int n = 2;
cout <<"m1 初始化的时候虽然用了 CV_8U,但是 前面乘了一个 -1"<<endl;
cout <<"因此 m1 的 type() 仍然是 0 "<<endl;
cout <<"不论在输出的时候使用 uchar,还是 char 都全部为 0 "<<endl;
Mat m1 = -1*Mat::ones(m,n,CV_8U);
cout <<"m1.type() is: "<< m1.type()<<endl;
cout <<endl<<"利用 m1.at<uchar>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<uchar>(i,j))<<" ";
cout <<endl;
}
cout <<endl<<"利用 m1.at<char>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<char>(i,j))<<" ";
cout <<endl;
}
cout <<endl;
cout <<"m1 使用 CV_8S 初始化"<<endl;
cout <<"m1 的 type() 是 1 "<<endl;
cout <<"使用 uchar 输出的结果是 255"<<endl;
cout <<"使用 char 输出的结果是 -1 "<<endl;
m1 = -1*Mat::ones(m,n,CV_8S);
cout <<"m1.type() is: "<< m1.type()<<endl;
cout <<endl<<"利用 m1.at<uchar>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<uchar>(i,j))<<" ";
cout <<endl;
}
cout <<endl<<"利用 m1.at<char>(i,j) 输出 得到的结果"<<endl;
for (int i = 0; i<m; i++)
{
for (int j = 0; j < n; j++)
cout << to_string(m1.at<char>(i,j))<<" ";
cout <<endl;
}
}
int main()
{
// initialization of Mat
int m = 300;
int n = 200;
Mat m1 = Mat::zeros(m,n,CV_8U);
m1 = 255*Mat::eye(Size(m,n),CV_8U);
imshow("m1",m1);
// 利用 函数获取 结构元
Mat ker = getStructuringElement(MORPH_CROSS,Size(3,3),Point(0,0));
cout << endl<<
"to_string(ker.at<uchar>(Point(x,y))) is:" <<endl;
cout << "注意:这里不知道为什么,结构元不能直接显示成数字,"<<endl;
cout <<" 因此,把每个元素转换成 string 然后再显示"<<endl;
for (int y=0; y < ker.rows; y++)
{
for (int x = 0; x < ker.cols; x++)
{
cout << to_string(ker.at<uchar>(Point(x,y))) <<" ";
}
cout <<endl;
}
cout <<endl
<< "利用 Scalar 对Mat 进行赋值"<<endl;
Mat roi(Size(10,10),CV_8UC3);
roi = Scalar(0,255,0);
imshow("with box",roi);
Mat m22(Size(10,10),CV_8U);
m22 = 20;
imshow("m2",m22);
// 对Mat中的分量进行遍历的不同方法
// 主要是 对于位置的理解。
cout <<endl<<"注意:这里初始化的时候使用的是 double"<<endl;
cout<<" 因此,在调用 at 和 ptr 的时候也需要指定 为 double "<<endl;
Mat m2 = (Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9);
cout << "(Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9) is: " <<endl;
cout << "print by: m2.at<double>(Point(x,y)) "<<endl;
for (int y=0; y < m2.rows; y++)
{
for (int x = 0; x < m2.cols; x++)
{
cout << m2.at<double>(Point(x,y)) <<" ";
}
cout <<endl;
}
cout << "print by: m2.at<double>(i,j) "<<endl;
for (int i = 0; i < m2.rows; i++)
{
for (int j = 0; j < m2.cols; j++)
{
cout << m2.at<double>(i,j)<<" ";
}
cout <<endl;
}
cout << "print by: double* p_m2 = m2.ptr<double>(y); "<<endl;
cout << " cout << p_m2[x] "<<endl;
for (int y =0; y < m2.rows; y++)
{
double* p_m2 = m2.ptr<double>(y);
for (int x = 0; x < m2.cols; x++)
{
cout << p_m2[x]<<" ";
}
cout <<endl;
}
// 下面展示如何对Mat进行局部赋值
m1 = Mat::zeros(m , n , CV_8UC2);
Vec2b pixel;
pixel[0] = 255;
pixel[1] = 0;
// 这里得到的结果应该是只有右上角为白色,其他地方为黑色
for (int y=0; y < m1.rows; y++)
for (int x = y; x < m1.cols; x++)
m1.at<Vec2b>(Point(x,y)) = pixel;
cout << endl
<<"imshow 只能展示通道为 1,3,4 的图像,这里 m1 只有两个通道"<<endl;
cout << "因此,先利用 split 将 m1 拆开,然后只展示 第一层"<<endl;
Mat mv[2];
split(m1,mv);
imshow("m1",mv[0]);
// 对于数据类型的判断
test_type2();
waitKey();
system("pause");
return 0;
}