OpenCV学习笔记(四) 数据结构与基础绘图

0x01 基础图像容器 Mat

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

由于矩阵的开销比较大, 不到万不得已, 不应该进行大图像的复制, 为了解决这个问题, OpenCV 使用了引用机制。 即让每个 Mar 对象有自己的信息头, 但共享同一个矩阵。 而 拷贝构造函数 则只复制信息头和矩阵指针, 而不复制矩阵。

Mat A, C;   // 仅创建信息头
A = imread("1.jpg", CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat  B(A);  // 使用拷贝函数
C = A;      // 赋值运算符
  • OpenCV 函数中输出图像的内存分配是自动完成的 (如果不特别指定的话)。
  • 使用 OpenCV 的 C++ 接口时不需要考虑内存释放的问题。
  • 复制运算符和拷贝构造函数 (构造函数) 只复制信息头。
  • 使用函数 clone() 或者 copyTo() 来复制一幅图像的矩阵。

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

1. 使用 Mat() 构造函数

最常用的方法是直接使用 Mat() 构造方法, 简单明了。示例如下 :

Mat M(2, 2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;

2. 在 C\C++ 中同构构造函数来进行初始化

如下 :

IplImage* img = cvIoadImage("1.jpg", 1);
Mat mtx(img);   // 转换 IplImage* -> Mat

3. 为已存在的 Ipllmage 指针创建信息头

如下 :

IplImage* img = cvLoadImage("1.jpg",  1);
Mat mxt(img);   // 转换 IplImage* -> Mat

4. 利用 Create() 函数

方法四是利用 Mat 类中的 Create() 成员函数进行 Mat 类的初始化操作。如下 :

M.Create(4, 4, CV_8UC(2));
cout << "M = " << endl << " " << M << endl << endl;

5. 采用 Matlab 式的初始化方式

采用 Matlab 形式的初始化方式: zeros(), ones(), eye()。 如下 :

Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;

Mat O = Mat::onse(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;

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

6. 对小矩阵使用逗号分隔式初始化函数

如下 :

Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;

7. 为已存在的对象创建新信息头

使用成员函数 clone() 或者 copyTo() 为一个已经存在的 Mat 对象创建一个新的信息头, 如下 :

Mat RowCloone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;

格式化输出方法

  • OpenCV 默认风格。
  • Python 风格。
  • 逗号分隔风格。
  • Numpy 风格。
  • C 语言风格。

0x02 常用数据结构和函数

  • Vec
  • Point
  • Scalar
  • Size
  • Rect
  • RotatedRect

2.1 Vec 类

Vec 是一个模版类, 用于储存数值向量。

2.1.1 可以使用它来定义任意类型的向量

Vec<doeble, 8> myVector;  // 定义一个存放 8 个 double 型变量的向量.

2.1.2 使用 [] 访问 Vec 向量成员

myVector[0] = 0;

2.1.3 可使用以下预定于的类型

typedef Vec<uchar, 2> Vec2b;  
typedef Vec<uchar, 3> Vec3b;  
typedef Vec<uchar, 4> Vec4b;  
typedef Vec<short, 2> Vec2s;  
typedef Vec<short, 3> Vec3s;  
typedef Vec<short, 4> Vec4s;  
typedef Vec<int, 2> Vec2i;  
typedef Vec<int, 3> Vec3i;  
typedef Vec<int, 4> Vec4i;  
typedef Vec<float, 2> Vec2f;  
typedef Vec<float, 3> Vec3f;  
typedef Vec<float, 4> Vec4f;  
typedef Vec<float, 6> Vec6f;  
typedef Vec<double, 2> Vec2d;  
typedef Vec<double, 3> Vec3d;  
typedef Vec<double, 4> Vec4d;  
typedef Vec<double, 6> Vec6d;

2.1.4 Vec 支持的运算如下

v1 = v2 + v3  
v1 = v2 - v3  
v1 = v2 * scale  
v1 = scale * v2  
v1 = -v2  
v1 += v2  
v1 == v2, v1 != v2  
norm(v1) (euclidean norm)  

//-----------------------------------------------------//
// 示例代码如下
Vec<int, 6> v1, v2, v3;
for (int i = 0; i < v2.rows; i++)  // 返回向量 v2 的行数
{
    v2[i] = i;
    v3[i] = i + 1;
}

v1 = v2 + v3;

cout << "v2       = " << v2 << endl;
cout << "v3       = " << v3 << endl;
cout << "v1 = v2 + v3      = " << v1 << endl;
cout << "v1 = v2 * 2       = " << v2*2 << endl;
cout << "v1 = -v2     = " << -v2 << endl;
cout << "v1 == v2     = " << (v1 == v2) << endl;
cout << "v1 != v2     = " << (v1 != v2) << endl;
cout << "norm(v2)     = " << norm(v2) << endl;

system ("color A");
system ("pause");

2.2 颜色的表示 Scalar 类

2.2.1 cv::Scalar 结构

Scalar() 表示具有 4 个元素的数组, 在 OpenCV 中被大量用于传递像素值。

可以使用 [] 访问 Scalar 的值, 或者使用 cv::Scalar(B,G,R) 方式定义 RGB 三个通道的值。例如 :

cv::Scalar myScalar;

myScalar = cv::Scalar(0,255,0);

cout << "myScalar = " << myScalar << endl;

system("color A");
system("pause");

2.2.2 读取彩色图像像素值

彩色图像的每个像素对应三个部分: RGB 三个通道。 因此包含彩色图像的 cv::Mat 类会返回一个向量, 向量中包含三个 8 bit 的数值。 OpenCV 为这样的短向量定义了一种类型, 即 cv::Vec3b 。 这个向量包含三个无符号 (unsigned character) 类型的数据。

储存的顺序确实相反的, 为 BGR。

访问彩色像素中元素的方法如下:

Mat pImg = cv::imread("1.jpg", 1);
if(!pImg.data)
  return 0;

int x = 100, y = 100;
cv::Scalar pixel = pImg.at<Vec3b> (x,y);

cout << " B chanel of pixel is =" << pixel.val[0] << endl;
cout << " G chanel of pixel is =" << pixel.val[1] << endl;
cout << " R chanel of pixel is =" << pixel.val[2] << endl;

system("color A");
system("pause");

2.3 点的表示 Point 类

2.3.1 基本用法

Point 类数据结构表示了二维坐标系下的点, 即由图像坐标 x 和 y 指定的 2D 点。 用法如下:

Point point;
point.x = 10;
point.y = 8;

// 或者
Point point = Point(10,8);

// ------------- 或者使用如下预定义 ----------------- //
typedef Point_<int> Point2i;  
typedef Point2i Point;  
typedef Point_<float> Point2f;  
typedef Point_<double> Point2d;  

2.3.2 基本计算

cv::Point pt1(1,1);  
cv::Point pt2(5,5);  
cv::Point pt3(9,10);  
int x = 2;

pt1 = pt2 + pt3;
cout << "pt1 =  pt2 + pt3 = " << pt1 << endl;
pt1 = pt2 - pt3;
cout << "pt1 =  pt2 - pt3 = " << pt1 << endl;
pt1 = pt2 * x;
cout << "pt1 =  pt2 * 2   = " << pt1 << endl;
pt1 += pt2;
cout << "pt1 += pt2       = " << pt1 << endl;
pt1 -= pt2;
cout << "pt1 -= pt2       = " << pt1 << endl;
pt1 *= x;
cout << "pt1 *= 2         = " << pt1 << endl;
double value = norm(pt1);
cout << "norm(pt1)        = " << value << endl;
pt1 == pt2;
cout << "pt1 == pt2       = " << (pt1 == pt2) << endl;
pt1 != pt2;
cout << "pt1 != pt2       = " << (pt1 != pt2) << endl;

system("color A");
system("pause");

2.4 尺寸的表示 Size 类

使用频率最高的是下面这个构造函数 :

Size(_TP _width, _TP _height);

如下 :

// Size 的用法
Size    size1(6,3);
Size    size2;

size2.width = 4;
size2.height = 2;
Mat mat1(size1, CV_8UC1, cv::Scalar(0));        // 单通道
Mat mat2(size2, CV_8UC3, cv::Scalar(1,2,3));    // 三通道
cout << "mat1 = " << endl << " " << mat1 << endl;
cout << endl << "mat2 = " << endl << " " << mat2 << endl;
system("color A");
system("pause");
````





<div class="se-preview-section-delimiter"></div>

## 2.5 矩形的表示 Rect 类




<div class="se-preview-section-delimiter"></div>

### 2.5.2 基本概念
Rect 的成员变量有 x,y, width, height。Rect 可以用来定义图像的 ROI 区域。
常用的成员函数有 :

* Size() 返回值为 Size
* area() 返回矩形的面积
* contains(Point) 判断点是否在矩形内
* inside(Rect) 判断矩形是否在矩形内
* tl() 返回左上角点的坐标
* br() 返回右下角点的坐标





<div class="se-preview-section-delimiter"></div>

### 2.5.2 使用方法




<div class="se-preview-section-delimiter"></div>

```cpp
Rect rect(x,y,width, height);

示例 :

Mat pImg = imread("Lena.jpg", 1);   // 读取工程目录下的 Lena.jpg 图像 文件为 512x512 的图片
Rect rect(180,200,200,200);         // 定义选中的区域 (x,y) = (180,200) width = 200, height = 200
Mat roi = cv::Mat(pImg, rect);      // 定义 roi 变量储存选中的区域
Mat pImgRect = pImg.clone();        
cv::rectangle(pImgRect, rect, cv::Scalar(0,255,0),2);   // 画制一个矩形轮廓
imshow("original image with rectangle", pImgRect);
imshow("roi", roi);

waitKey(0);

2.6 RotatedRect 类

2.6.1 基本概念

该数据类型是一个特殊的矩形, RotatedRect。 这个类通过中心点、宽度、高度、旋转角度来表示一个旋转矩阵。

2.6.2 使用方法

RotatedRect(const Point2f& center, const Size2f& size, float angle);  

参数:

  • center, 中心点坐标 Point2f 类型。
  • size, 矩形宽度和高度, Size2f 类型。
  • angle, 顺时针方向的旋转角度 (单位 °), float 类型。

2.6.3 示例

Point2f center(100,100);
Size2f size(100, 100);
float angle = 45;   // 可以试以下其他角度 10, 30, 45 ,60

RotatedRect rRect(center, size, angle);
Mat image(200,200,CV_8UC3, Scalar(0));      // 创建一个图片

Point2f vertices[4];
rRect.points(vertices);     // 返回 rRect 矩形的四个顶点
for (int i = 0; i < 4; i++)
{
    // 在 image 中绘制线段
    line(image, vertices[i],vertices[(i+1)%4], Scalar(0,255,0));
}

Rect brect = rRect.boundingRect();      // 返回包含旋转矩阵的矩形
rectangle(image, brect, Scalar(255,0,0));

imshow("rectangles", image);

waitKey(0);

颜色空间转换 cvtColor() 函数

原型如下 :

void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0)

第一个参数为输图像, 第二个参数为输出图像, 第三个参数为颜色空间转换的标识符。随着 OpenCV 版本的升级, 对颜色空间种类的支持也越来越多。

图形绘制函数的写法

编写代码的时候, 为了统一规范, 最好都按照一定的规范进行编码。这里简单提一下几个常用的函数

  • DrawEllipse() 函数的写法
  • DrawFilledCircle() 函数的写法
  • DrawPolygon() 函数的写法
  • DrawLine() 函数的写法

最后需要对这些方法结合起来做一些练习, 才能够熟悉起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值