OpenCV(一个C++人工智能领域重要开源基础库) 简介

返回:OpenCV系列文章目录(持续更新中......)

上一篇: OpenCV4.9.0配置选项参考

 下一篇:OpenCV4.9.0开源计算机视觉库安装概述

引言:        

        OpenCV(全称Open Source Computer Vision Library)是一个基于开放源代码发行的跨平台计算机视觉库,可以用来进行图像处理、计算机视觉和机器学习等领域的开发。

该库由英特尔公司于1999年开始开发,最初是为了加速处理器和多媒体指令集(MMX)优化实时视觉应用程序。现在,这个开源项目已经成为计算机视觉领域中最广泛使用的库之一,它在很多流行领域都有应用,比如安防、自动驾驶、人脸识别、物体识别、AR/VR等。

OpenCV支持多种编程语言,包括C/C++、Python、Java等,同时还提供了针对Android和IOS等移动设备平台的移动版本。该库的功能非常丰富,包括图像处理、机器视觉、计算机视觉、物体识别等,支持形态学、滤波器、图像分割、图像匹配、特征提取、立体视觉、运动跟踪和相机标定等功能。创作不易,要是对您有用请加个关注或点个赞,非常感谢了!前面500粉丝我会互关。

 有想尝试的朋友可以参考我的两篇博客,《OpenCV4.9.0开源计算机视觉库安装概述》和《OpenCV4.9.0配置选项参考》

后续我将持续发布相关文章敬请期待。

OpenCV主要提供了以下几个模块:

1. 核心模块:定义基本数据结构,比如点,矩形,矢量、数组等。
2. 影像处理模块:包含图像的一些基本处理方法,如滤波、边缘检测、图像纠偏、基本的几何变化等。
3. 人脸检测模块:包含基于 Haar 级联检测器的人脸检测程序,也可以使用该模块对图像中的人脸进行快速训练和识别。
4. 物体检测模块:提供了 HOG 特征和 SVN 分类器,可用于各种目标检测任务。
5. 机器学习模块:提供了统计建模和机器学习的功能,能够分类和识别图像,还有一些常见的机器学习算法,比如 K-Means,SVM 和 PCA。
6. 视频处理模块:包括基本的视频处理功能,如帧差法、背景减除等,还有一些视频稳定和跟踪的方法,比如 Lucas-Kanade、均值滤波器、Camshift 缺陷等。

总之,OpenCV是一个功能强大、易于学习和使用的库,被广泛应用于研究、教育和商业领域,它可以解决计算机视觉领域中的许多问题。

OpenCV简介:

        OpenCV (Open Source Computer Vision Library: http://opencv.org) 是一个开源库,拥有数百个计算机视觉算法。本文介绍的是所谓的 OpenCV 2.x API,它是基于 C++ 的 API,而不是基于 C 的 OpenCV 1.x API (C API自从OpenCV 2.4版本以来一直被停用并且无法再使用"C"编译器测试)。

        OpenCV 采用了模块化结构,其包装括多个共享或静态库。以下是一些可用的模块:核心功能(core)- 这是一个紧凑的模块,定义了基本数据结构,包括密集的多维数组 Mat 和所有其它模块使用的基本函数。图像处理(imgproc)- 该图像处理模块包括线性和非线性图像过滤,几何变换(resize、仿射和透视形变、通用表格映射),颜色空间转换、直方图等。视频分析(video)- 该视频分析模块包括运动估计、背景消除和目标跟踪算法。相机标定和三维重构(calib3d)- 该模块提供基本的多视图几何算法、单目标定和双目标定、物体姿态估计、立体对应算法和三维重建的基本要素。2D 特征框架(features2d)- 突出的特征检测器、描述符和描述符匹配器。目标检测(objdetect)- 检测预定义类别的对象和实例(例如人脸、眼睛、杯子、人、汽车等)。高级 GUI(highgui)- 一个易于使用的界面,提供了简单的用户界面功能。视频输入输出(videoio)- 视频捕获和视频编解码器的简单接口。...以及像 FLANN 和 Google 测试包裹器、Python 绑定和其他一些辅助模块。

本文的更多章节描述了每个模块的功能。但首先,请确保熟悉库中彻底使用的常见API概念。

API概念

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.);

自动内存管理

OpenCV 自动处理所有内存。

        首先,由函数和方法使用的 std::vector、cv::Mat 和其他数据结构具有在必要时释放底层内存缓冲区的析构函数。这意味着,析构函数并不总是像 Mat 的情况下一样释放缓冲区。它们考虑到可能的数据共享。析构函数会减少与矩阵数据缓冲区关联的引用计数器。仅当引用计数器降到零时,也就是没有其他结构引用相同缓冲区时,缓冲区才会被释放。类似地,当复制 Mat 实例时,不会复制任何实际数据。而是增加了引用计数器以记住同一数据的另一个所有者。还有 cv::Mat::clone 方法,它创建矩阵数据的完整副本,如下面的示例:

// 创建一个大的8Mb矩阵

Mat A(1000, 1000, CV_64F);

// 为同一矩阵创建另一个头部;这是一个即时操作,无论矩阵大小如何。 

Mat B = A; 
// 为 A 的第三行创建另一个头部;没有实际数据被复制 
Mat C = B.row(3); 
// 现在创建矩阵的单独副本 
Mat D = B.clone(); 
// 将 B 的第五行复制到 C,也就是将A的第五行复制到A的第三行。 
B.row(5).copyTo(C); 
// 现在让 A 和 D 共享数据;所有修改后的版本 // A 仍然被 B 和 C 引用。
 A = D; 
// 现在使 B 成为一个空矩阵(不引用任何内存缓冲区), // 但是修改后的版本仍然被 C 引用, // 尽管 C 只是原始 A 的单个行。 
B.release(); 
// 最后,进行 C 的完全拷贝。此结果,修改后的大矩阵将被释放,因为没有任何人引用它。 
C = C.clone();

您可以看到,使用 Mat 和其他基本结构是非常简单的。但如果没有考虑自动内存管理的高级类或甚至创建,那怎么办?对于它们,OpenCV 提供了 cv::Ptr 模板类,与 C++11 的 std::shared_ptr 类似。因此,与其使用普通指针:

T* ptr = new T(...);

你可以使用:

Ptr ptr(new T(...));

或者模板类指针:

Ptr ptr = makePtr(...);

Ptr 封装了一个 T 实例的指针和与指针相关的引用计数器。有关详细信息,请参阅 cv::Ptr 描述。

自动分配输出数据

        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;
}

        由于视频帧分辨率和位深度已知,因此 >> 操作符自动分配 frame 数组。cvtColor 函数自动分配 edges 数组。它的大小和位深度与输入数组相同。通道数为 1,因为传递了颜色转换代码 cv::COLOR_BGR2GRAY,这意味着从彩色转换为灰度。请注意,frame 和 edges 只分配了一次,因为下一个视频帧具有相同的分辨率。如果您以某种方式更改了视频分辨率,则自动重新分配数组。

        此技术的关键组件是 cv::Mat::create 方法。它需要所需的数组大小和类型。如果数组已经具有指定的大小和类型,则该方法不执行任何操作。否则,如果存在先前分配的数据,则释放这些数据(此部分涉及递减引用计数器并将其与零进行比较),然后分配所需大小的新缓冲区。大多数函数为每个输出数组调用 cv::Mat::create 方法,因此自动输出数据分配已实现。

        一些著名的例外情况是 cv::mixChannels、cv::RNG::fill 和几个其他函数和方法。它们无法分配输出数组,因此您必须事先执行此操作。

饱和度算术

        作为计算机视觉库,OpenCV 大量处理以紧凑的每通道8位或16位形式编码的图像像素,并因此具有受限值范围。此外,图像的某些操作,如颜色空间转换、亮度/对比度调整、锐化、复杂插值(双三次插值、Lanczos 插值)可能会产生超出可用范围的值。如果您仅存储结果的最低8(16)位,则会产生视觉上的伪影,并且可能影响进一步的图像分析。为了解决这个问题,采用所谓的饱和度算术。例如,要将一个操作的结果 r 存储到8位图像中,请在 0..255 范围内找到最近的值:

I(x,y)=min(max(round(r),0),255)

类似的规则适用于 8 位带符号、16 位带符号和无符号类型。此语义在库的所有地方都适用。在 C++ 代码中,它使用类似于标准 C++ 转换操作的 cv::saturate_cast<> 函数执行。请参见以下提供的公式实现:

I.at(y, x) = saturate_cast(r);

        其中 cv::uchar 是 OpenCV 的 8 位无符号整数类型。在优化的 SIMD 代码中,使用 paddusb、 packuswb 等 SSE2 指令。它们有助于实现与 C++ 代码完全相同的行为。

注意:结果是32位整数时不适用饱和。

固定像素类型。有限制的模板使用

        模板是 C++ 的一个很好的特性,可以实现非常强大、高效而又安全的数据结构和算法。然而,广泛使用模板可能显著增加编译时间和代码大小。此外,当独占使用模板时,难以区分界面和实现。这对于基本算法可能是可以的,但对于计算机视觉库却不好,因为单个算法可能跨越成千上万行的代码。由于这个原因并为了简化针对其他语言(如Python、Java、Matlab)的绑定开发,它们根本没有模板或仅具有有限的模板功能,因此目前的 OpenCV 实现基于多态和运行时分派,而不是纯模板。在运行时分派过于缓慢的那些地方(如像素访问运算符、一般的 cv::Ptr<> 实现或者仅仅不是非常方便的情况下(如 cv::saturate_cast<>()),当前的实现引入了小型模板类、方法和函数。在当前 OpenCV 版本中的任何其他地方,模板的使用是有限制的。

因此,它具有一组有限的基本数据类型,可以操作,即数组元素应具有以下类型之一:

  1. - 8 位无符号整数(uchar)
  2. - 8 位带符号整数(schar)
  3. - 16 位无符号整数(ushort)
  4. - 16 位带符号整数(short)
  5. - 32 位带符号整数(int)
  6. - 32 位浮点数(float)
  7. - 64 位浮点数(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)
- 当通道数超过 4 或在编译时未知时,使用 CV_8UC(n) ... CV_64FC(n) 或 CV_MAKETYPE(CV_8U, n) ... CV_MAKETYPE(CV_64F, n) 宏

注意:CV_32FC1 == CV_32F,CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2),CV_MAKETYPE(depth, n) == ((depth&7) + ((n-1)<<3)。这意味着,常量类型根据深度确定,取最低的 3 位,通道数减1,取下一个 log2(CV_CN_MAX) 位。
例如:

Mat mtx(3, 3, CV_32F);// 创建一个3x3的浮点矩阵
Mat cmtx(10, 1, CV_64FC2); // 创建一个10x1的2通道浮点矩阵(10元素的复数向量)                           
Mat img(Size(1920, 1080), CV_8UC3);// 创建一个3通道(彩色)图像,大小为1920列和1080行                                  
Mat grayscale(img.size(), CV_MAKETYPE(img.depth(), 1));// 创建一个与img相同大小和通道类型的单通道图像。

        使用OpenCV无法构造或处理具有更复杂元素的数组,而且每个函数或方法仅能处理所有可能的数组类型的一个子集。通常,算法越复杂,支持的格式子集就越小。以下是此类限制的典型示例:

  1. 面部检测算法仅能使用8位灰度或彩色图像。
  2. 线性代数函数和大部分机器学习算法仅能使用浮点数数组。
  3. 基本函数例如cv::add支持所有类型。
  4. 颜色空间变换函数支持8位无符号、16位无符号和32位浮点类型。

每个函数支持的类型子集都是基于实际需求定义的,并可根据用户请求在未来进行扩展。

InputArray和OutputArray

许多OpenCV函数处理密集的二维或多维数值数组。通常,这样的函数以cv::Mat作为参数,但在某些情况下,使用std::vector<>(例如用于点集)或cv::Matx<>(例如3x3的单应矩阵)更方便。为避免在API中重复许多内容,引入了特殊的“代理”类。基本的“代理”类是cv::InputArray,用于将只读数组传递给函数输入。从InputArray派生的类cv::OutputArray用于指定函数的输出数组。通常,您不必关心这些中间类型(也不必显式声明这些类型的变量)-它们都将自动工作。您可以假设InputArray / OutputArray可以始终使用cv::Mat、std::vector<>、cv::Matx<>、cv::Vec<>或cv::Scalar代替。当函数具有可选的输入或输出数组,而您没有或不想使用它时,请传递cv::noArray()。

错误处理

OpenCV使用异常来报告关键错误。当输入数据具有正确的格式并属于指定的值范围,但由于某些原因(例如,优化算法未收敛)无法成功时,它返回一个特殊的错误代码(通常仅是一个布尔变量)。

异常可以是cv::Exception类或其派生类的实例。而cv::Exception又是std::exception的派生类。因此,可以使用其他标准C++库组件在代码中优雅地处理它。

异常通常使用CV_Error(errcode, description)宏或其printf类似的CV_Error_(errcode,(printf-spec,printf-args))变体,或使用检查条件并在不满足条件时引发异常的CV_Assert(condition)宏抛出。对于对性能至关重要的代码,有CV_DbgAssert(condition),它仅在Debug配置中保留。由于自动内存管理,所有中间缓冲区在发生突发错误时会自动被释放。如果需要,您只需要添加try语句来捕获异常。

例如:

try
{
    ... // call OpenCV
}
catch (const cv::Exception& e)
{
    const char* err_msg = e.what();
    std::cout << "exception caught: " << err_msg << std::endl;
}

多线程和可重入性

当前的OpenCV实现是完全可重入的。即,可以从不同的线程调用同一函数或不同类实例的同一方法。由于引用计数操作使用架构特定的原子指令,因此可以在不同的线程中使用相同的Mat。

你可能感兴趣的:(OpenCV,人工智能,人工智能,opencv,c++,图像处理,计算机视觉,开源)