【OpenCV Tutorials】Mat - The Basic Image Container


注:本文仅作个人翻译学习记录,具体内容请以官方手册为准!

OpenCV (Open Source Computer Vision Library: http://opencv.org)是一个开放源代码库,其中包含数百种计算机视觉算法。
各平台安装方法:
https://docs.opencv.org/master/df/d65/tutorial_table_of_content_introduction.html

Mat - The Basic Image Container

我们有多种从现实世界中获取数字图像的方法:数码相机,扫描仪,计算机断层扫描和磁共振成像等等。在每种情况下,我们(人类)看到的都是图像。
但是,将其转换为数字设备时,我们记录的是图像每个点的数值。

在计算机世界里图像都被简化成数值矩阵。OpenCV主要任务就是处理和操作这些信息。

OpenCV从2001年就有了。在那些日子里,这个库是围绕一个C接口构建的,为了将图像存储在内存中,他们使用了一个名为IplImage的C结构。这是你会在大多数旧的教程和教育材料中看到的。问题是它把C语言的所有缺点都带到了桌面上。最大的问题是手动内存管理。它建立在用户负责内存分配和释放的假设之上。虽然这对于较小的程序来说不是问题,但是一旦你的代码库增长了,处理所有这些将会比专注于解决你的开发目标更加困难。

幸运的是,C++出现了,并引入了类的概念,通过自动内存管理(或多或少)使用户更容易。好消息是,C++与C完全兼容,因此进行更改不会产生兼容性问题。因此,OpenCV 2.0引入了一个新的C++接口,它提供了一种新的做事方式,这意味着您不需要摆弄内存管理,使您的代码简洁(写得更少,实现得更多)。C++接口的主要缺点是,目前许多嵌入式开发系统只支持C。因此,除非你针对嵌入式平台,否则使用旧方法是没有意义的(除非你是一个受虐狂程序员,你在自找麻烦)。

Mat已经不需要手动释放空间。
Mat基本上是一个包含两个数据部分的类:矩阵头(包含矩阵大小存储方法存储矩阵的地址等信息)和包含像素值的矩阵指针(根据存储方法选择任意维数)。矩阵头的大小是恒定的,但是矩阵本身的大小可能因图像而异,并且通常大几个数量级。

penCV是一个图像处理库。它包含大量图像处理功能。为了解决一个计算上的挑战,大多数时候你会使用库的多个函数。因此,将图像传递给函数是一种常见的做法。我们不应该忘记,我们正在谈论的图像处理算法,往往是相当繁重的计算。我们最不想做的事情是通过复制潜在的大图像来进一步降低程序的速度。

为了解决这个问题,OpenCV使用了一个参考计数系统。其思想是每个Mat对象都有自己的头,但是矩阵可以在两个Mat对象之间共享,方法是让它们的矩阵指针指向同一个地址。此外,复制操作符将只复制头和指向大矩阵的指针,而不是数据本身。

Mat A, C;                          // 只创建了头部部分
A = imread(argv[1], IMREAD_COLOR); // 这里我们将知道所使用的方法(分配矩阵)
Mat B(A);                                 // 使用复制构造函数
C = A;                                    // 赋值运算

最后,所有上述对象都指向同一个数据矩阵,使用它们中的任何一个进行修改都会影响所有其他对象。实际上,不同的对象只是为相同的底层数据提供了不同的访问方法。然而,它们的头部是不同的。真正有趣的是,您可以创建只引用完整数据的一个子部分的标题。例如,要在图像中创建感兴趣区域,只需创建一个带有新边界的新头部:

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle 使用巨型
Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries

矩阵本身属于多个Mat对象,当不再需要它时,简短来说最后一个使用它的对象,负责释放空间。这是通过使用引用计数机制来处理的。每当有人复制Mat对象的头部时,矩阵的计数器就会增加。每当清除一个头部,这个计数器就减少。当计数器达到零时,矩阵被释放。

Mat F = A.clone(); 
Mat G; 
A.copyTo(G); 

现在修改F或者G不会影响A的表头指向的矩阵。(相当于深复制)
你需要记住的是:

  • OpenCV函数的输出图像分配是自动的(除非另有说明)。
  • 你不需要考虑用OpenCV的C++接口进行内存管理。
  • 赋值运算符和复制构造函数只复制头。
  • 可以使用cv::Mat::clone()cv::Mat::copyTo()函数复制图像的基础矩阵。

Storing methods

这是关于如何存储像素值。您可以选择颜色空间和使用的数据类型。颜色空间指的是我们如何组合颜色成分来编码给定的颜色。最简单的一种是灰度,我们可以使用的颜色是黑色和白色。这些的结合让我们可以创造出许多灰色的阴影。

对于丰富多彩的方式,我们有更多的方法可以选择。它们中的每一个都可以分解成三四个基本的组成部分,我们可以用它们的组合来创造其他的。最受欢迎的是RGB,主要是因为我们的眼睛也是这样建立颜色的。它的基本颜色是红色、绿色和蓝色。为了对颜色的透明度进行编码,有时会添加第四个元素:阿尔法(α)。
然而,还有许多其他颜色系统,每个都有自己的优势:

  • RGB是最常见的,因为我们的眼睛使用类似的东西,但是请记住OpenCV标准显示系统使用BGR颜色空间(红色和蓝色通道交换位置)组成颜色。
  • HSV和HLS将颜色分解成它们的色调、饱和度和值/亮度分量,这是我们描述颜色的更自然的方法。例如,您可能会忽略最后一个组件,使您的算法对输入图像的光照条件不太敏感。
  • YCrCb被流行的JPEG图像格式使用。
  • CIE Lab*是一个感知上均匀的颜色空间,如果你需要测量一种给定颜色到另一种颜色的距离,它会很方便。

每个构建组件都有自己的有效域。这导致使用的数据类型。我们如何存储一个组件定义了我们对其域的控制。可能的最小数据类型是char,这意味着一个字节或8位。这可以是无符号的(因此可以存储从0到255的值)或有符号的(从-127到+127的值)。虽然在三个组件的情况下,这已经给出了1600万种可能的颜色来表示(就像在RGB的情况下),但是我们可以通过为每个组件使用浮点(4字节= 32位)或双(8字节= 64位)数据类型来获得更好的控制。然而,请记住,增加组件的大小也会增加内存中整个图片的大小。

Creating a Mat object explicitly

Load, Modify, 和Save an Image 教程中,您已经学习了如何使用cv::imwrite()函数将矩阵写入图像文件。然而,出于调试目的,查看实际值要方便得多。您可以使用Mat的<<运算符来实现这一点。请注意,这仅适用于二维矩阵。
然而,出于调试目的,查看实际值要方便得多。您可以使用Mat的<<运算符来实现这一点。请注意,这仅适用于二维矩阵。
虽然Mat作为一个图像容器确实很好用,但它也是一个通用的矩阵类。因此,创建和操作多维矩阵是可能的。您可以通过多种方式创建Mat对象:

  • cv::Mat::Mat构造函数
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl; 

在这里插入图片描述
对于二维和多通道图像,我们首先定义它们的大小:行和列计数方式。
然后我们需要指定用于存储元素的数据类型和每个矩阵点的通道数。为此,我们根据以下约定构建了多个定义:

CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]
CV _[每个项目的位数][有符号或无符号][类型前缀]C[频道号]

例如,CV_8UC3意味着我们使用8位长的无符号字符类型,每个像素有三个这样的字符来形成三个通道。最多可为四个通道预定义类型。cv::Scalar是四元素短向量。指定它,您可以用自定义值初始化所有矩阵点。

更多内容请查看官方手册:https://docs.opencv.org/master/d6/d6d/tutorial_mat_the_basic_image_container.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值