OpenCV 4 中文文档
原文https://www.docs.opencv.org/4.5.3/
目录
- OpenCV 4 中文文档
- 一、介绍
- 一、API概念
- 1. cv Namespace(cv名称空间)
- 2. 自动内存管理
- 3. 输出数据的自动分配
- 4. 饱和算法
- 5. 固定像素类型。模板的有限使用
- 6. 输入阵列和输出阵列
- 7. 错误处理
- 8. 多线程和可重入性
一、介绍
OpenCV(开源计算机视觉库:http://opencv.org)是一个开源库,它包括数百种计算机视觉算法。该文件描述了OpenCV 2.x API,它本质上是一个C++ API,而不是基于C的OpenCV 1.x API(C API 自 OpenCV 2.4 发布以来已弃用且未使用"C"编译器进行测试)。
OpenCV 具有模块化结构,这意味着该包包括多个共享或静态库。提供以下模块:
- 核心功能(core):定义基本数据结构的紧凑型模块,包括密集的多维阵列 Mat 和所有其他模块使用的基本功能。
- 图像处理(imgproc) : 图像处理模块,包括线性和非线性图像过滤、图像几何变换(大小、仿射和透视扭曲、基于通用表的重映射)、色彩空间转换、直方图等。
- 视频分析(video):包括运动估计、背景消除和对象跟踪算法的视频分析模块。
- 相机校准和 3D 重建(calib3d):基本的多视图几何算法、单声相机和立体相机校准、对象姿势估计、立体声通信算法和 3D 重建元素。
- 2D 功能框架(features2d):显著特征探测器、描述符和描述符匹配器。
- 对象检测(objdetect):检测预定类的对象和实例(例如,人脸、眼睛、杯子、人、汽车等)。
- 高级GUI (highgui):简单UI功能的易用界面。
- 视频 I/O (videoio ):易于使用的视频捕获和视频编解码器接口。
- 其他一些辅助模块,如FLNN和谷歌测试包装器,Python绑定等。
文档的其他章节描述了每个模块的功能。但首先,请务必熟悉库中彻底使用的常见 API 概念。
一、API概念
1. cv Namespace(cv名称空间)
所有 OpenCV 类和函数都放在 cv
命名空间中。因此,要从您的代码访问此功能,请使用 cv::
说明符或 using namespace cv;
标识:
#include "opencv2/core.hpp"
...
cv::Mat H = cv::findHomography(points1, points2, cv::RANSAC, 5);
...
或
#include "opencv2/core.hpp"
using namespace cv;
...
Mat H = findHomography(points1, points2, RANSAC, 5 );
...
某些当前或未来的 OpenCV 外部名称可能与 STL 或其他库冲突。在这种情况下,使用显式命名空间说明符来解决名称冲突:
Mat a(100, 100, CV_32F);
randu(a, Scalar::all(1), Scalar::all(std::rand()));
cv::log(a, a);
a /= std::log(2.);
2. 自动内存管理
OpenCV 自动处理所有内存。
首先,函数和方法使用的 std::vector
、cv::Mat
和其他数据结构具有析构函数,可在需要时释放底层内存缓冲区。这意味着析构函数并不总是像 Mat 那样释放缓冲区。他们考虑了可能的数据共享。析构函数递减与矩阵数据缓冲区关联的参考计数器。当且仅当引用计数器达到零时,即当没有其他结构引用同一缓冲区时,才会释放缓冲区。类似地,当复制 Mat 实例时,并没有真正复制实际数据。相反,引用计数器递增以记住相同数据的另一个所有者。还有 Mat::clone
方法可以创建矩阵数据的完整副本。请参阅下面的示例:
// create a big 8Mb matrix
//创建一个大的 8Mb 矩阵
Mat A(1000, 1000, CV_64F);
// create another header for the same matrix;
// this is an instant operation, regardless of the matrix size.
//为相同矩阵创建另一个标题;
//这是一个即时操作,无论矩阵大小如何。
Mat B = A;
// create another header for the 3-rd row of A; no data is copied either
//为第3行创建另一个标题;任何一个数据都没有复制
Mat C = B.row(3);
// now create a separate copy of the matrix
//现在创建一个单独的矩阵副本
Mat D = B.clone();
// copy the 5-th row of B to C, that is, copy the 5-th row of A to the 3-rd row of A.
//复制B的第5行到C,即复制A的第5行到A的第3行。
B.row(5).copyTo(C);
// now let A and D share the data; after that the modified version of A is still referenced by B and C.
//现在让A和D共享数据;之后修改了A仍被B和C引用的版本。
A = D;
// now make B an empty matrix (which references no memory buffers),
// but the modified version of A will still be referenced by C,
// despite that C is just a single row of the original A
//现在让B变为一个空矩阵(没有内存缓冲区的引用),
//但A修改后的版本将仍然被C引用,
//尽管C只是原来A中的一行
B.release();
// finally, make a full copy of C. As a result, the big modified
// matrix will be deallocated, since it is not referenced by anyone
//最后,制作C的完整副本。结果,大修改了
//矩阵将被释放,因为它没有被任何人引用
C = C.clone();
你看到使用 Mat
和其他基本结构很简单。但是在不考虑自动内存管理的情况下创建高级类甚至用户数据类型呢?对于他们而言,OpenCV提供了类似于C ++ 11的 std::hared_ptr
的 cv::Ptr
模板类。因此,不要使用普通指针:
T* ptr = new T(...);
你可以使用:
Ptr<T> ptr(new T(...));
//或者
Ptr<T> ptr = makePtr<T>(...);
Ptr< T>
封装指向T实例的指针和与指针关联的引用计数器。有关详细信息,请参见 cv::Ptr
说明。
3. 输出数据的自动分配
OpenCV自动释放内存,并在大多数情况下自动为输出函数参数分配内存。因此,如果函数有一个或多个输入数组(例如cv::Mat
)和一些输出数组,则输出数组将自动分配或重新分配。输出数组的大小和类型由输入数组的大小和类型决定。如果需要,这些函数将使用额外的参数来帮助确定输出数组属性。
例如:
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0);
if(!cap.isOpened()) return -1;
Mat frame, edges;
namedWindow("edges", WINDOW_AUTOSIZE);
for(;;)
{
cap >> frame;
cvtColor(frame, edges, COLOR_BGR2GRAY);
GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
imshow("edges", edges);
if(waitKey(30) >= 0) break;
}
return 0;
}
阵列帧由 >>
操作自动分配,因为视频捕获模块知道视频帧分辨率和位深度。阵列边缘由 cvtColor
函数自动分配。它与输入数组具有相同的大小和位深度。通道数为1,因为传递了颜色转换代码 cv::color_BGR2GRAY
,这意味着彩色到灰度的转换。注意,在循环体的第一次执行期间,帧和边仅分配一次,因为所有下一个视频帧具有相同的分辨率。如果您以某种方式更改视频分辨率,阵列将自动重新分配。
该技术的关键组件是 Mat::create
方法。它采用所需的数组大小和类型。如果数组已经具有指定的大小和类型,则该方法不执行任何操作。否则,它将释放先前分配的数据(如果有)(这部分涉及减少引用计数器并将其与零进行比较),然后分配所需大小的新缓冲区。大多数函数为每个输出数组调用 Mat::create
方法,因此实现了自动输出数据分配。
此方案的一些显著例外是 cv::MixChannel
、cv::RNG::fill
和其他一些函数和方法。他们无法分配输出数组,因此你必须提前这样做。
4. 饱和算法
作为一个计算机视觉库,OpenCV处理很多图像像素,这些像素通常以紧凑的、每通道8位或16位的形式编码,因此具有有限的值范围。此外,图像上的某些操作,如颜色空间转换、亮度/对比度调整、锐化、复杂插值(双立方插值、Lanczos)可能会产生超出可用范围的值。如果只存储结果的最低8(16)位,则会产生视觉伪影,并可能影响进一步的图像分析。为了解决这个问题,使用了所谓的饱和算法。例如,要将操作结果r存储到8位图像中,你可以找到0…255范围内最接近的值:
I
(
x
,
y
)
=
m
i
n
(
m
a
x
(
r
o
u
n
d
(
r
)
,
0
)
,
255
)
I(x,y)=min(max(round(r),0),255)
I(x,y)=min(max(round(r),0),255)
类似的规则适用于8位有符号、16位有符号和无符号类型。这种语义在库中随处可见。在C++代码中,它使用类似于标准C++ CAST操作的 cv::saturate_cast<>
函数。见上文公式的实施情况:
I.at<uchar>(y, x) = saturate_cast<uchar>(r);
其中 cv::uchar
是OpenCV 8位无符号整数类型。在优化的SIMD代码中,使用了诸如ppaddusb, packuswb等SSE2指令。它们有助于实现与C++代码完全相同的行为。
注:当结果为32位整数时,不应用饱和。
5. 固定像素类型。模板的有限使用
模板是C++的一大特色,它能够实现非常强大、高效且安全的数据结构和算法。但是,模板的广泛使用可能会显著增加编译时间和代码大小。此外,如果只使用模板,则很难将接口和实现分开。这对于基本算法来说可能很好,但对于计算机视觉库来说并不好,因为在计算机视觉库中,单个算法可能跨越数千行代码。因此,为了简化其他语言(如Python、Java、Matlab)绑定的开发,这些语言根本没有模板或模板功能有限,当前的OpenCV实现基于多态性和运行时模板调度。在运行时调度太慢(如像素访问操作符)、不可能(通用 cv::Ptr
实现)或非常不方便( cv::saturate_cast<>()
)的地方,当前的实现引入了小模板类、方法和函数。在当前OpenCV版本的任何其他地方,模板的使用都是有限的。
因此,库可以操作的基本数据类型集是有限的。也就是说,数组元素应具有以下类型之一:
- 8-bit unsigned integer (uchar)
- 8-bit signed integer (schar)
- 16-bit unsigned integer (ushort)
- 16-bit signed integer (short)
- 32-bit signed integer (int)
- 32-bit floating-point number (float)
- 64-bit floating-point number (double)
- 多个元素的元组,其中所有元素都具有相同的类型(上述元素之一)。元素为元组的数组称为多通道数组,与元素为标量值的单通道数组相反。通道的最大可能数量由
CV_CN_MAX
常量定义,该常量当前设置为512。
对于这些基本类型,将应用以下枚举:
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };
可以使用以下选项指定多通道(n通道)类型:
CV_8UC1
…CV_64FC4
常数(适用于从1到4的多个通道)CV_8UC(n)
…CV_64FC(n)
orCV_MAKETYPE(CV_8U, n)
…CV_MAKETYPE(CV_64F, n)
编译时通道数大于4或未知时的宏。
注:
CV_32FC1 == CV_32F
,CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2)
, andCV_MAKETYPE(depth, n) == ((depth&7) + ((n-1)<<3)
. 这意味着常数类型由深度形成,取最低的3位,通道数减1,取下一个log2(CV_CN_MAX)
位。
例如:
Mat mtx(3, 3, CV_32F);
// make a 3x3 floating-point matrix
// 创建一个3x3浮点矩阵
Mat cmtx(10, 1, CV_64FC2);
// make a 10x1 2-channel floating-point
// matrix (10-element complex vector)
// 创建一个10x1的2通道浮点值
// 矩阵(10元素的复杂向量)
Mat img(Size(1920, 1080), CV_8UC3);
// make a 3-channel (color) image of 1920 columns and 1080 rows.
// 创建一个有1920列和1080行的3通道(彩色)图像。
Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1));
// make a 1-channel image of the same size and same channel type as img
// 创建一个与img的尺寸和通道类型相同的单通道图像。
无法使用OpenCV构造或处理包含更复杂元素的数组。此外,每个函数或方法只能处理所有可能数组类型的子集。通常,算法越复杂,支持的格式子集越小。见以下此类限制的典型示例:
- 人脸检测算法仅适用于8位灰度或彩色图像。
- 线性代数函数和大多数机器学习算法仅适用于浮点数组。
- 基本函数,如
cv::add
,支持所有类型。 - 颜色空间转换函数支持8位无符号、16位无符号和32位浮点类型。
每个功能支持的类型子集已根据实际需要定义,并可在将来根据用户请求进行扩展。
6. 输入阵列和输出阵列
许多OpenCV函数处理密集的二维或多维数值数组。通常,此类函数将cppMat作为参数,但在某些情况下,使用std::vector<>
(例如,对于点集)或 cv::Matx<>
(对于3x3单应矩阵等)更方便。为了避免API中的许多重复,引入了特殊的“代理”类。基本的“代理”类是 cv::InputArray
。它用于在函数输入上传递只读数组。派生自InputArray类cv::OutputArray
用于指定函数的输出数组。通常,你不应该关心这些中间类型(也不应该显式地声明这些类型的变量)——它将自动工作。您可以假设可以始终使用Mat
、std::vector<>
、cv::Matx<>
、cv::Vec<>
或cv::Scalar
代替InputArray/OutputArray
。如果函数具有可选的输入或输出数组,而你没有想要或不想要,请传递cv::noArray()
。
7. 错误处理
OpenCV使用异常来表示严重错误。当输入数据具有正确的格式并且属于指定的值范围,但由于某种原因(例如,优化算法没有收敛)算法无法成功时,它将返回一个特殊的错误代码(通常,只是一个布尔变量)。
异常可以是 cv::Exception
类或其派生类的实例。反过来,cv::Exception
是std::Exception
的派生。因此,它可以在代码中优雅地使用其他标准的C++库组件来处理。
通常使用CV_Error(errcode, description)
宏或printf-like的CV_Error_(errcode, (printf-spec, printf-args))
变量引发异常,或者使用CV_Assert(condition)
宏检查条件并在不满足条件时引发异常。对于性能关键代码,CV_DbgAssert(condition)
仅保留在调试配置中。由于自动内存管理,所有中间缓冲区在发生突然错误时自动释放。如果需要,只需添加try语句即可捕获异常:
try
{
... // call OpenCV
}
catch (const cv::Exception& e)
{
const char* err_msg = e.what();
std::cout << "exception caught: " << err_msg << std::endl;
}
8. 多线程和可重入性
当前的OpenCV实现是完全可重新输入的。也就是说,可以从不同线程调用不同类实例的相同函数或相同方法。同样,相同的Mat也可以在不同的线程中使用,因为引用计数操作使用特定于体系结构的原子指令。