文章目录
OpenCV数据结构与基本绘图(Opencv3编程入门第4章)
第一节 基础图像容器Mat
-
Mat是一个类,由两部分数据组成:矩阵头(包含矩阵尺寸、储存方法、储存地址等信息)和一个指向储存所有像素值的矩阵的指针。
-
不到万不得已,不应该进行大图像的复制,因为这会降低程序的运行速度。
-
Mat类型对象的拷贝
Mat A, C; //仅创建信息头部分
A = imread("1.jpg"); //为矩阵开辟内存空间
Mat B(A); //使用拷贝构造函数
C= A;
//----------------------------------------------------------
//以上结果得到的A,B,C均指向同一个矩阵,对其中任意一个进行修改,另外两个的矩阵值也会随之变化。
//-----------------------------------------------------------
Mat F = A.clone();
Mat G;
A.copyTo(G);
//----------------------------------------------------------
//使用clone或者cloneTo可以复制矩阵本身,F,G,A分别指向不同的三个矩阵。
//-----------------------------------------------------------
- 小结:
- OpenCV函数中输出图像的内存分配是自动完成的。
- 使用OpenCV的C++接口时不需要考虑内存释放的问题。
- 赋值运算符和拷贝构造函数只复制信息头(只复制指针)。
- 使用函数clone()或者成员方法copyTo()来复制一幅图像的矩阵。
- 四种颜色系统
- RGB
- HSV和HLS把颜色分解为色调、饱和度和亮度/明度。
- YCrCb在JPEG图像格式中广泛使用。
- CIE Lab*是一种在感知上均匀的颜色空间,他适合用来度量两个颜色之间的距离。
- 创建Mat对象的几种方法
- 使用Mat构造函数
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
cout << M;
// 输出:[0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255]
CV_8UC3表示使用8位的unsigned char类型,每个像素有三个元素组成三通道。
Scalar是个short类型的向量
- 创建一个超过两维的矩阵:
int sz[3] = {2,2,2};
Mat L(3, sz, CV_8UC, Scalar::all(0));
第一个参数指定维度,然后传递一个指向数组的指针,包含每个维度的尺寸;后续的两个参数与之前的相同。
- 为已存在的Ipllmage指针创建信息头
IplImage* img = cvLoadImage("1.jpg", 1);
Mat mtx(img);
- 利用create()函数
M.create(4, 4, CV_8UC(2));
其中CV_8UC(2)表示2通道的8位unsigned char。
此创建方法不能为矩阵设置初值,只是在改变尺寸时重新为矩阵数据开辟内存而已。
- 采用Matlab式的初始化方式
Mat E = Mat::eye(4, 4, CV_64F);
Mat O = Mat::ones(2,2,CV_32F);
Mat Z = Mat::zeros(3, 3, CV_8UC1);
- 对小矩阵使用逗号分隔式初始化函数
Mat C = (Mat <double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
- 为已存在的对象创建新信息头
Mat RowClone = C.row(1).clone();
- 先分配内存,在进行初始化赋值
Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255)); //在0 - 255之间取随机数进行赋值
-
opencv的Mat格式化输出有很多样式,个人认为没有太大的作用,只要使用cout就可以了。
-
其他数据结构
- 二维点:
Point2f p(6,2);
- 三维点:
Point3f p3f(8, 2, 0);
- 基于Mat类的std::vector
vector<float> v;
v.push_back(3);
v.push_back(5);
v.push_back(7);
- 定义和输出std::vector点
vector<Point2f> points(20);
for (size_t i = 0; i < points.size(); ++i)
points[i] = Point2f((float)(i * 5), (float)(i % 7));
cout << points;
二、常用数据结构和函数
- Point类
Point point;
point.x = 10;
point.y = 8;
Point point = Point(10, 8);
Point_<int>、Point2i、Point互相等价, Point_<float>、Point2f相互等价
-
颜色的表示:Scalar类
Scalar(a,b,c);三个参数分别为蓝色分量、绿色分量、红色分量。 -
尺寸的表示:Size类
Size_<int>、Size2i、Size这三个类名等价。 -
Rect类
成员变量有x,y,width,height;成员函数有Size()返回Size;area()返回矩形的面积;contains(Point)判断点是否在矩形内;tl()返回左上角坐标;br()返回右下角坐标。
Rect rectShitf = rect + point; //平移矩形
rect rectScale = rect + Size; //缩放矩形
- 颜色空间转换
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0);
src和dst分别表示输入图像和输出图像;code为颜色空间转换的标识符;第四个参数为目标图像的通道数,默认为0表示输出图像取输入图像的通道数。
基本图形的绘制
- 头文件paint.h
#ifndef PAINT_H
#define PAINT_H
#include <opencv2/opencv.hpp>
using namespace cv;
void DrawEllipse(int x, int y, int width, int length, Mat img, float angle);
void DrawFilledCircle(Mat img, Point center, int radium);
#endif // PAINT_H
- 源文件paint.cpp
#include "paint.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
void DrawEllipse(int x, int y, int width, int length, Mat img, float angle) {
/*
x,y:椭圆中心的位置
width,length:椭圆所在的矩形的大小
img:Mat对象
angle:椭圆旋转的角度
*/
int thickness = 2;
int lineType = 8;
ellipse( img, //将输出的结果存放在img
Point(x, y), //椭圆中心的位置
Size(width, length), //椭圆所在的矩形的大小
angle, //椭圆旋转的角度
0, //椭圆扩展的弧度从0-360
360,
Scalar(255, 129, 0),
thickness, //线宽
lineType); //线型
}
void DrawFilledCircle(Mat img, Point center, int radium) {
/*
center:圆心
radium:半径
*/
int thickness = -1;
int lineType = 8;
circle(img,
center,
radium,
Scalar(0, 0, 255),
thickness,
lineType);
}
- 源文件main.cpp
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "paint.h"
using namespace cv;
#define WINDOW_NAME1 "【绘制图1】"
#define WINDOW_NAME2 "【绘制图2】"
#define WINDOW_WIDTH 600
int main() {
// 创建空白的Mat图像
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
Mat rookImage = atomImage.clone();
//线绘制出椭圆
DrawEllipse(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2, WINDOW_WIDTH / 4, WINDOW_WIDTH / 16, atomImage, 90);
DrawEllipse(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2, WINDOW_WIDTH / 4, WINDOW_WIDTH / 16, atomImage, 0);
DrawEllipse(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2, WINDOW_WIDTH / 4, WINDOW_WIDTH / 16, atomImage, -45);
DrawEllipse(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2, WINDOW_WIDTH / 4, WINDOW_WIDTH / 16, atomImage, 45);
//绘制实心圆
DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), WINDOW_WIDTH / 32);
imshow(WINDOW_NAME1, atomImage);
moveWindow(WINDOW_NAME1, 0, 200);
waitKey(0);
return 0;
}
结果如下: