OpenCV Mat 常用的基础知识


Mat 基础

图片在计算机中的本质就是一个数组。其中 Mat 就是在 OpenCV 中图像的表示形式,因此简单介绍 Mat 中一些常用的基础知识。

其中 Mat 类中有一些基本属性:

cols :矩阵列数

rows:矩阵行数

channels:通道数

type:数据类型

total:矩阵总元素数

data:指向矩阵数据块的指针

其中 Mat 排列方式如下:
Mat排列方式
Mat BGR
通道顺序为 BGR

参考:https://blog.csdn.net/x199699/article/details/82424596

1、Mat 类型

在访问图片像素点时,了解 Mat 的类型至关重要。我们可以通过 Mat.type() 函数返回类型,通过下表的数值对应可知预定义名,如

​ “22" 表示 CV_64FC3,Mat 有三个通道,每个像素点是一个 64位double 类型的数值。

命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:

C1C2C3C4
CV_8U081624
CV_8S191725
CV_16U2101826
CV_16S3111927
CV_32S4122028
CV_32F5132129
CV_64F6142230
用户定义7
图像深度枚举数值空间大小范围等同C++变量
CV_8U08bits0~255unsigned char
CV_8S18bits-128~127char
CV_16U216bits0~65535ushort,unsigned short int,unsigned short
CV_16S316bits-32768~32767short,short int
CV32S432bits-2147483648~2147483647int,long
CV32F532bits1.18e-38~3.40e38float
CV_64F664bits2.23e-308~1.79e308double
CV_USRTYPE17-

参考:<https://segmentfault.com/a/1190000015653101>

2、Mat 元素访问

先行后列
图片的 高(height)==> Mat 的 行(row)==> 坐标系的 y
图片的 宽(width) ==> Mat 的 列(col)==> 坐标系的 x
图片坐标系

2.1 at访问

img.at<type>(row, col)[channel];
// 三通道
for(int h = 0 ; h < image.rows ; ++ h)
{
    for(int w = 0 ; w < image.cols ; ++ w)
    {
        std::cout << image.at<Vec3b>(h , w)[0] << std::endl;
        std::cout << image.at<Vec3b>(h , w)[1] << std::endl;
        std::cout << image.at<Vec3b>(h , w)[2] << std::endl;
    }
}
// 单通道
std::cout << image.at<uchar>(h , w) << std::endl;

2.2 ptr访问

// 三通道
for(int h = 0 ; h < image.rows ; ++ h)
{
    for(int w = 0 ; w < image.cols ; ++ w)
    {
    	// 输出 [1.1, 1.2, 1.3] 的形式,输出都一样
        Vec3b*ptr = image.ptr<Vec3b>(h , w) ;
        std::cout << ptr[0] << std::endl;
        std::cout << ptr[1] << std::endl;
        std::cout << ptr[2] << std::endl;
        
        // 输出 每个通道的值,将上边形式的数值分开输出
        //Vec3b *ptr = image.ptr<Vec3b>(h , w) ;
        //std::cout << ptr->val[0] << std::endl;
        //std::cout << ptr->val[1] << std::endl;
        //std::cout << ptr->val[2] << std::endl;
    }
}
// 单通道
double* ptr = image.ptr<double>(h , w) ;
std::cout << *ptr << std::endl;

2.3 迭代器(安全)

// 三通道
Mat_<Vec3b>::iterator it = image.begin<Vec3b>() ;
Mat_<Vec3b>::iterator itend = image.end<Vec3b>() ;
for(;it != itend ; ++ it)
{
	std::cout << (*it)[0] << std::endl;
	std::cout << (*it)[1] << std::endl;
	std::cout << (*it)[2] << std::endl;
}
// 单通道
std::cout << (*it1) << std::endl;

2.4 data 访问

uchar *data = image.data ;
// 三通道
for(int h = 0 ; h < image.rows ; ++ h)
{
    for(int w = 0 ; w < image.cols ; ++ w)
    {
        std::cout << *data ++ << std::endl;
        std::cout << *data ++ << std::endl;
        std::cout << *data ++ << std::endl;
    }
}
// 单通道
for(int h = 0 ; h < image.rows ; ++ h)
	for(int w = 0 ; w < image.cols; ++ w)
    	std::cout << *data ++ << std::endl ;

2.5 continuous+channels 访问(高效)

isContinuous() 判断 image 存储是否为连续,连续为 true,不连续为 false。

可以将简单元素操作的性能提高10-20%,特别是如果图像相当小并且操作非常简单。

一般读取图片都为连续,但是裁剪过后的图片返回为不连续

int nRows = image.rows ;
int nCols = image.cols * image.channels() ;

if(image.isContinuous())
{
    nCols = nRows * nCols ;
    nRows = 1 ;
}

for(int h = 0 ; h < nRows ; ++ h)
{
    uchar *ptr = image.ptr<double>(h) ;
    for(int w = 0 ; w < nCols ; ++ w)
        std::cout << *ptr++ << std::endl;
}

2.6 step 访问

addr step
Mat step
改变 “0”,“1”,“2” 为 “i”,“j”,“k” 即可以访问任意位置像素值(不要越界)
在这里插入图片描述

for (int row = 0; row < img.rows; row++)
{
     for (int col = 0; col < img.cols; col++)
     {
            //[row,col]像素的第1通道地址解析为Blue通道
            *(img.data + img.step[0] * row + img.step[1] * col) += 15;

            //[row,col]像素的第2通道地址解析为Green通道
            *(img.data + img.step[0] * row + img.step[1] * col + img.elemSize1()) += 16;

            //[row,col]像素的第3通道地址解析为Red通道
            *(img.data + img.step[0] * row + img.step[1] * col + img.elemSize1() * 2) += 17;
     }
 }

参考:https://blog.csdn.net/BoaHock/article/details/80790323

2.7 单个像素点类型

种类C1C2C3C4C6
uchar8Uucharcv::Vec2bcv::Vec3bcv::Vec4b
char8S
ushort16U
short16Sshortcv::Vec2scv::Vec3scv::Vec4s
int32Sintcv::Vec2icv::Vec3icv::Vec4i
float32Ffloatcv::Vec2fcv::Vec3fcv::Vec4fcv::Vec6f
double64Fdoublecv::Vec2dcv::Vec3dcv::Vec4dcv::Vec6d

当需要访问 Mat 类型为 CV_64FC3 的图片像素时

cv::Vec3d vec3d      = img.at<cv::Vec3d>(0,0);
uchar     vec3d0     = img.at<cv::Vec3d>(0,0)[0];
uchar     vec3d1     = img.at<cv::Vec3d>(0,0)[1];
uchar     vec3d2     = img.at<cv::Vec3d>(0,0)[2];
std::cout<<"vec3d = "<<vec3d<<std::endl;
std::cout<<"vec3d0 = "<<(double)vec3d0<<std::endl;
std::cout<<"vec3d1 = "<<(double)vec3d1<<std::endl;
std::cout<<"vec3d2 = "<<(double)vec3d2<<std::endl;

2.5 convertTo()

当我们了解 Mat 数据类型时,我们可以方便的进行 像素运算:

1、将整型类型的 Mat 转化为 double 类型:利用 convertTo() 函数

src.convertTo(dst, CV_64FC3);

2、将 Mat 中元素进行统一运算,如 matlab 中点除

比如:dst = a* src + b

src.convertTo(dst, CV_32F, a, b);	//CV_32F为 B 中 Mat 类型

当然,也可以利用遍历进行运算

3、Mat 初始化

(1) Mat::Mat()
(2) Mat::Mat(int rows, int cols, int type)
(3) Mat::Mat(Size size, int type)
(4) Mat::Mat(int rows, int cols, int type, const Scalar& s)
(5) Mat::Mat(Size size, int type, const Scalar& s)
(6) Mat::Mat(const Mat& m)
(7) Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
(8) Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
(9) Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
(10) Mat::Mat(const Mat& m, const Rect& roi)
(11) Mat::Mat(const CvMat* m, bool copyData=false)
(12) Mat::Mat(const IplImage* img, bool copyData=false)
(13) template<typename T, int n> explicit Mat::Mat(const Vec<T, n>& vec, bool copyData=true)
(14) template<typename T, int m, int n> explicit Mat::Mat(const Matx<T, m, n>& vec, bool copyData=true)
(15) template explicit Mat::Mat(const vector& vec, bool copyData=false)
(16) Mat::Mat(const MatExpr& expr)
(17) Mat::Mat(int ndims, const int* sizes, int type)
(18) Mat::Mat(int ndims, const int* sizes, int type, const Scalar& s)
(19) Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)

其中 clone() 也可以实现初始化

Mat img(640, 480, CV_8UC3);
Mat roi(img, Rect(10,10,100,100));

*深拷贝浅拷贝

OpenCV 中 ”=“ 是浅拷贝,只赋值图片的指针头,所以在修改 Mat 元素时,会影响另一个。

如果想实现图片复制为两个图片,应当选择 clone()copyto()

dst = src.clone();
src.copyto(dst);

*特殊矩阵初始化

Mat eye   = Mat::eye(4,4,CV_8U);
Mat ones  = Mat::ones(4,4,CV_8U);
Mat zeros = Mat::zeros(4,4,CV_8U);
Mat img = (Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);

// 随机初始化像素值
Mat r = Mat(3,3,CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255));

4、显示图片

#include <opencv2/opencv.hpp>//opencv的头文件
 
using namespace cv;
 
int main()
{
	Mat img = imread("C:/daima practice/opencv/mat3/mat3/image4.jpg");
	imshow("显示灰度图",img);
	waitKey(0);

	return 0;
}

5、播放视频

#include <opencv2/opencv.hpp>
 
using namespace cv;
 
int main()
{
	// VideoCapture capture(0);		摄像头
	VideoCapture cap("test.avi");
	if (!cap_PD_depth.isOpened())
        return -1;
	
	double rate = capture.get(CV_CAP_PROP_FPS);//获取视频文件的帧率
	int delay = cvRound(1000.000 / rate);	   // 设置帧率
	
	Mat Frame;
	while(1)
	{
        cap >> Frame;
        if (frame.empty()) break;
        imshow("视频", frame);
		waitKey(delay);
	}

	return 0;
}

6、waitKey

#include <iostream>
#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;
int main()
{
	Mat Frame;
	VideoCapture cap;

	cap.open(0);

	if (!cap.isOpened())
		return 0;

	while (1)
	{
		cap >> Frame;
		if (Frame.empty())
			return 0;
		imshow("camera", Frame);
		if (waitKey(10) == 'b')				// 设置为按下 b 键退出程序
		{
			break;
		}
	}
	return 0;
}
  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值