【OpenCV学习笔记】之Mat结构与基本数据结构、函数

本文介绍了OpenCV中Mat类的基本概念及其创建方法,并演示了如何使用Mat类进行图像读取和处理。此外,还详细解释了几种基本的图像绘制方法,如画线、画矩形等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Mat

         我们知道相机,摄像机,扫描仪或者磁共振等设备生成的图像都是数字图像,它其实就是一个矩阵,矩阵行列上的值就是其像素值。我们进行图像处理,其实就是对图像矩阵进行不同的操作。那么OpenCV里面是怎么对图像进行存储和处理的呢?

        在老版本的OpenCV1.X里面,通常用的是一个名为IplImage*的指针进行图像的存储。但是这种基于C语言的接口的格式,有一个很麻烦的地方,在每次使用完存储空间后必须进行释放,否则会产生内存泄露((memory leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。)的问题。当程序量小的时候,我们可能还顾及得来,但是一旦程序量变大后,我们可能很难去手动管理内存了。那么怎么办呢?

        OpenCV2.0版本以后,大牛们引入了新的C++接口,使用Mat类数据结构进行图像的存储。自动的管理内存,不必再为其手动开辟空间,也不必在使用完后需立即手动释放内存。那么为什么Mat对象有这么强大的功能呢?

(1)Mat是一个类。由两部分数据组成:矩阵头(包括矩阵尺寸、存储方法、存储地址等信息)和一个指向所有像素值的矩阵(根据所选存储方法不同,矩阵可以是不同的维数)的指针。

(2)Opencv采用了计数机制,让每一个Mat对象有自己的信息头,但共用一个矩阵。这通过让矩阵指针指向同一个地址实现,而拷贝构造函数只复制信息头和矩阵指针,而不复制矩阵。

以下是Mat对象的7种创建方法和常用的输出风格

// 显式创建Mat对象的七种方法

	//一、使用Mat()构造函数
	Mat I(4, 4, CV_8UC3, Scalar(0, 0, 255));//行列数,存储元素的数据类型及通道数
	cout << "I=" <<endl<<""<< I << endl;

	//二、使用Create()函数
	I.create(4, 4, CV_8UC2);
	cout << "I=" << endl << I << endl;

	//三、采用Matlab式的初始化方式
	Mat E = Mat::eye(4, 4, CV_64F);
	cout << "E=" << endl << E << endl;

	Mat O = Mat::ones(4, 4, CV_32F);
	cout << "O=" << endl << O << endl;

	Mat Z = Mat::zeros(4, 4, CV_8UC1);
	cout << "Z=" << endl << Z << endl;

	//四、为已存在的对象创建新信息头
	Mat RowClone = E.row(1).clone();
	cout << "RowClone=" << endl << RowClone << endl;

	//五、在C/C++中通过构造函数进行初始化
	int sz[3] = { 2,2,2 };
	Mat L(3, sz, CV_8UC1, Scalar::all(0));
	cout << "L=" << endl << L << endl;

	//六、为已存在的IplImage指针创建信息头
	IplImage* img = cvLoadImage("test.jpg", 1);
	Mat mtx(img);

	//七、对小矩阵使用逗号分隔式初始化函数
	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C=" << endl << C << endl;

	// Opencv的格式化输出方式(常见的)
	Mat r = Mat(10, 3, CV_8UC3);
	randu(r, Scalar::all(0), Scalar::all(255));//使用randu()函数产生的随机值填充矩阵
	//一、OpenCV默认风格
	cout << "r=" << r << endl;

	//二、Python风格
	cout << "r=" << format(r, "python") << endl;//OpenCV2版本
	cout << "r=" << format(r,Formatter::FMT_PYTHON) << endl;//OpenCV3版本

	//三、Numpy风格
	cout << "r=" << format(r, "numpy") << endl;//OpenCV2版本
    cout << "r=" << format(r,Formatter::FMT_NUMPY) << endl;//OpenCV3版本

示例程序:

//Mat对象的使用
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	Mat src;
	src = imread("C:/Users/59235/Desktop/imag/girl1.jpg");
	if (src.empty())
	{
		cout << "could not load image..." << endl;
		return -1;
	}
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src);
	/*
	得到一个与原图像相同的空白图像
	Mat dst;
	dst = Mat(src.size(),src.type());
	dst = Scalar(255,255,0);//设置空白图像的背景色
	namedWindow("output", CV_WINDOW_AUTOSIZE);
	imshow("output", dst);
	*/
/*
	//Mat dst = src.clone();//复制一张原图像
	Mat dst;
	//src.copyTo(dst);
	namedWindow("output", CV_WINDOW_AUTOSIZE);
	cvtColor(src, dst, CV_BGR2GRAY);
	printf("input images channels :%d\n", src.channels());
	printf("output images channels:%d\n", dst.channels());

	int cols = dst.cols;
	int rows = dst.rows;
	printf("rows :%d\n cols :%d\n", cols, rows);
	const uchar*firstRow = dst.ptr<uchar>(0);
	printf("first pixel value:%d\n", *firstRow);

	//Mat M(3,3,CV_8UC3,Scalar(0,1,2));
	//cout << "M=" << endl<< M << endl;
	//int sz[3] = {2,2,2};
	//Mat L(3, sz, CV_8UC1, Scalar::all(0));
	/*
	Mat M;
	M.create(100,100,CV_8UC3);
	M = Scalar(255,255,255);
	*/
/*
	Mat cont;
	Mat kernel= (Mat_<float>(3, 3) << 0, -1, 0,
		-1, 6, -1,
		0, -1, 0);
	filter2D(src, cont, -1,kernel,Point(-1,-1));//卷积滤波
// src: 输入图像 dst: 输出图像,和输入图像具有相同的尺寸和通道数量  int ddepth: 目标图像深度,如果没写将生成与原图像深度相同的图像。(当ddepth输入值为-1时,目标图像和原图像深度保持一致。)图像深度是指存储每个像素所用的位数
// kernel: 卷积核(或者是相关核),一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,可以先使用split()函数将图像通道事先分开。
// Point anchor: 内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。
//double delta: 在储存目标图像前可选的添加到像素的值,默认值为0
//int borderType: 像素向外逼近的方法,默认值是BORDER_DEFAULT,即对全部边界进行计算。

	imshow("output", cont);
	waitKey(0);
	return 0;
}
*/

二、基本数据结构

1、点的表示:Point类

Point类数据结构表示二维坐标系下的点,Point类是一个包含两个整形数据成员x和y的以及一些简单成员方法的类类型,和它有关的好几个Point点类的变种如下所示:

【1】Point2f----二维单精度浮点型点类

【2】Point2d----二维双精度浮点型点类

【3】Point3i----三维整型点类

其源代码如下所示:

  1. typedef Point_<int> Point2i;

  2. typedef Point2i Point;

  3. typedef Point_<float> Point2f;

  4. typedef Point_<double> Point2d;

  5. typedef Point3_<int> Point3i;

  6. typedef Point3_<float> Point3f;

  7. typedef Point3_<double> Point3d;

2、颜色的表示:Scalar类

Scalar()这是一个使用4个元素指定的特殊的Vec4X向量类模板的类模板。Scalar类的源头是Scaler_,我们常用的Scalar也就是Scalar_<double>。虽然我们常常只用到三个元素来表示颜色,如:

Scalar(a,b,c);
//a,b,c分别表示蓝色分量,绿色分量,红色分量

3、尺寸的表示:Size类

        OpenCV中尺寸Size类与点Point类的表示十分类似,最主要的区别是,Size(尺寸)类的数据成员是width和height,而Point类的数据成员是坐标点。

其源代码如下所示:

typedef Size_<int> Size2i; //【1】二维空间中,尺寸Size类的表示

typedef Size2i Size;//【2】用于描述一个二维矩阵的大小,比如: 1--图像的大小;2--矩阵的大小

typedef Size_<float> Size2f;//【3】适用的尺寸Size类----是---cv::Size类


OpenCV源码:
template<typename _Tp> class Size_
{
public:
    typedef _Tp value_type;
 
    //! various constructors
	//【1】-------------------------Size类(尺寸类)的各种构造函数-----------------------------
	//【1】默认构造函数
    Size_();
	//【2】指定宽和高的Size类的构造函数
    Size_(_Tp _width, _Tp _height);
    Size_(const Size_& sz);
    Size_(const CvSize& sz);
    Size_(const CvSize2D32f& sz);
    Size_(const Point_<_Tp>& pt);
    //【2】------------------------------------成员函数--------------------------------------
	//【1】重载=号运算符,判断两个Size是否相等
    Size_& operator = (const Size_& sz);
    //! the area (width*height)
	//【2】计算Size类所描述的那个区域的--面积
    _Tp area() const;
 
    //! conversion of another data type.
    template<typename _Tp2> operator Size_<_Tp2>() const;
 
    //! conversion to the old-style OpenCV types
    operator CvSize() const;
    operator CvSize2D32f() const;
	//【3】------------------------------------类的数据成员----------------------------------
    //【1】Size模板类中,最重要的两个数据成员----宽和高
    _Tp width, height; // the width and the height
};

4、矩形的表示:Rect类 

      Rect矩形类,它有四个很重要的数据成员x,y,width,height,分别代表这个矩形左上角的坐标点和矩形的宽度和高度。常用的数据成员函数:Size()返回值是Size;area()返回矩形的面积;constains(Point)判断点是否在矩形内;inside(Rect)判断矩形是否在该矩形内;tl()返回左上角点的坐标;br()返回右下角点的坐标等等

三、基本常用函数

1、cvtcolor():颜色空间转换函数

2、line():画直线

3、rectangle():画矩形

4、ellipse();画椭圆

5、circle():画圆

6、putText():输入文字

示例程序:

//在图像上绘制形状与文字
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
Mat bgImage;
const char*original_image = "draw shapes and text demo";
void MyLines();//函数的声明
void MyRectangle();
void MyEllipse();
void MyCircle();
void MyPloygon();
void RandomLineDemo();
int main(int argc, char**argv)
{
	bgImage = imread("C:/Users/59235/Desktop/image/test.jpg");
	if (bgImage.empty())
	{
		cout << "could not load image...\n" << endl;
		return -1;
	}

	MyLines();//函数的调用
	MyRectangle();
	MyEllipse();
	MyCircle();
	MyPloygon();

	putText(bgImage, "Hello Opencv", Point(30, 50), CV_FONT_BLACK, 2.0, Scalar(120, 110, 119), 2, 8);
	//依次含义:原图,输入字的内容,起始位置,字体,字的大小,颜色,线条大小粗细,连接域
	namedWindow(original_image,CV_WINDOW_AUTOSIZE);
	imshow(original_image, bgImage);

	RandomLineDemo();

	waitKey(0);
	return 0;
}
//生成一条直线
void MyLines()//函数的实现
{
	Point p1 = Point(20, 30);
	//Point p2=Point(300,30);
	Point p2;
	p2.x = 300;
	p2.y = 30;
	Scalar color = Scalar(255, 0, 0);
	line(bgImage, p1, p2, color, 2, 8);
	//括号内字母依次含义:原图,直线起点,直线终点,直线颜色,直线占用像素值(调粗细),8 (or 0) - 8-connected line(8邻接)连接 线4 - 4 - connected line(4邻接)连接线。CV_AA - antialiased(抗锯齿)线条。
}
//生成一个矩形框
void MyRectangle()
{
	Rect rect = Rect(150, 80, 150, 250);
	//这里跟matlab相似第一二个数表示起始点坐标,后面依次表示的宽和高
	Scalar color = Scalar(0, 255, 0);
	rectangle(bgImage, rect, color, 2, 8);
	//依次含义:原图,矩形框的信息,颜色,线条粗细,同上
}
//生成一个椭圆
void MyEllipse()
{
	Scalar color = Scalar(0, 0, 255);
	ellipse(bgImage, Point(bgImage.cols / 2, bgImage.rows / 2), Size(bgImage.cols / 4, bgImage.rows / 8), 90, 0, 360, color, 2, 8);
	//依次含义:原图,椭圆圆心坐标,椭圆的长轴和短轴,椭圆倾斜程度,0到360表示椭圆的角度(0到180就只画一半椭圆),后面同上
}
//生成一个圆
void MyCircle()
{
	Point center = Point(200, 250);//圆的圆心
	Scalar color = Scalar(255, 255, 0);
	circle(bgImage, center, 150, color, 2, 8);
	//依次含义:原图,圆心,圆的半径,颜色,线条粗细,8邻域
}
//生成一个多边形并填充
void MyPloygon()
{
	Point pts[1][5];//定义一个1*5的数组
	pts[0][0] = Point(100, 100);//给数组赋值坐标
	pts[0][1] = Point(100, 200);
	pts[0][2] = Point(200, 200);
	pts[0][3] = Point(200, 100);
	pts[0][4] = Point(100, 100);
	const Point*ppts[] = { pts[0] };//指向多边形的数组指针
	int npt[] = { 5 };//多边形的顶点个数的数组
	Scalar color = Scalar(0, 255, 255);
	fillPoly(bgImage, ppts, npt, 1, color,8);
	//依次含义:原图,指向多边形的数组指针,多边形的顶点个数的数组,组成填充区域的线段的数量,颜色,组成多边形的线条的类型
}
//生成一张随机的线条图
void RandomLineDemo()
{
	Mat bg = Mat::zeros(bgImage.size(), bgImage.type());
	namedWindow("randow line image", CV_WINDOW_AUTOSIZE);
	RNG rng(12345);//rng(随机数产生器)
	Point pt1;
	Point pt2;
	for (int i = 0; i < 100000; i++)
	{
		pt1.x = rng.uniform(0, bgImage.cols);//生成正态分布随机数uniform (int a, int b)
											 //rng,gaussian(bgImage.cols);//生成高斯随机数gaussian (double sigma)
		pt1.y = rng.uniform(0, bgImage.rows);//随机生成0到原图高之间大小的点
		pt2.x = rng.uniform(0, bgImage.cols);
		pt2.y = rng.uniform(0, bgImage.rows);
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		if (waitKey(50) > 0)
		{
			break;//waitkey到50时跳出程序
		}
		imshow("randow line image", bg);
		line(bg, pt1, pt2, color, 2, 8);
	}
}

效果图:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值