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

  • 问: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值