您的位置:首页 > 运维架构

OpenCV2.4.13 中Mat的理解及常用方法

2017-12-18 16:20 302 查看
问: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: