OpenCV 安装和使用

安装

这里讲述在 Linux/Mac 中安装 OpenCV 的方法。

1. 简单快速安装

下面是一个简单的安装流程,不涉及任何和 python 有关的环境配置等步骤。以在 Mac 中安装 OpenCV 3.4.8 为例,Linux 下安装方法其实完全相同。

cd /usr/local/Cellar/
mkdir -p opencv/3.4.8/repo
cd opencv/3.4.8/repo

## clone the repo
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git

## checkout to corresponding version
## 这一步也可以直接手动下载你想要安装的版本的 source code 再解压到本地,就不用 checkout 分支了。
cd opencv
git checkout 3.4.8 # or some other version you want to install
cd ../opencv_contrib/
git checkout 3.4.8 # or some other version you want to install

cd ../..
## 下面只是一个 cmake options 的例子。另一个更详细的例子参见本文后面的 "编译 OpenCV 源码时使用的 CMake 命令"一章。
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON -DOPENCV_ENABLE_NON_FREE=ON repo/opencv  -DOPENCV_EXTRA_MODULES_PATH=repo/opencv_contrib/modules

make -j7 && make install

## 记得链接一下 OpenCV 目录到 cmake,方便将来查找
ln -s /usr/local/Cellar/opencv/3.4.8/  /usr/local/lib/cmake/opencv3.4.8

注意:

  • 上面方法是直接将 OpenCV 和 OpenCV-contrib 安装到了你所在的当前目录/usr/local/Cellar/opencv/3.4.8里面。可以设置-DCMAKE_INSTALL_PREFIX=<install_path>来修改安装位置,最后用 ln 链接时修改安装目录即可。
  • -DOPENCV_EXTRA_MODULES_PATH记得指向 opencv_contrib 路径下的 modules 文件夹;
  • OPENCV_ENABLE_NON_FREE=ON这个很重要,增加编译 non-free 库(内部有例如 SIFT 等工具)。
  • 也可以关掉编译例子-DBUILD_EXAMPLES=OFF

其余相关的一些 FLAGS:

  • -DWITH_FFMPEG=ON:增加 ffmpeg 支持
  • -DCMAKE_CXX_FLAGS="-std=c++11"
  • -DBUILD_SHARED_LIBS=ON:编译动态库

2. 详细安装

上面的简单流程并未涉及 python 虚拟环境配置。这里以 Mac 安装 OpenCV 4.1.2 为例。

详细安装流程主要参考这个教程一步一步来就行:Mac 平台 OpenCV4.1.2 环境搭建。该链接是从零开始安装的,图文并茂,并且还有安装后的测试。不过,该链接更像是这个英文链接的中文翻译:Install OpenCV 4 on macOS

下面总结一下这个教程中的步骤并补充一些细节。

2.1 安装 XCode, Homebrew, Python3, pip 等

感觉这几个通常都已经提前安装过了。参考上面给出的链接即可。

2.2 建立并配置 python 虚拟环境(Optional)

这一步其实是可选的,好处是你可以接下来在这个虚拟环境中安装你想要的 OpenCV 版本,方便进行版本管理,例如你有多个版本的 OpenCV 时。不过个人认为,你只有一个版本或者完全不需要版本管理时,这个步骤完全没必要,跳过即可

原流程链接使用的版本工具是 virtualenvwrapper,具体步骤这里就不解释了。这里给出一些有关 virtualenvwrapper 的使用方法以及可能遇到的问题。

安装 virtualenvwrapper 时可能会显示某个路径没有权限

此时,在 sudo 后面增加 -H 参数:

sudo -H pip3 install virtualenv virtualenvwrapper

配置 zshrc 环境时找不到 virtualenvwrapper.sh 的位置

笔者的电脑中,virtualenvwrapper.sh 并不在默认的 /usr/local/bin 路径下。此时,可以通过

sudo find / -name virtualenvwrapper.sh

寻找该文件的路径。笔者的路径是:

/Library/Frameworks/Python.framework/Versions/3.8/bin/virtualenvwrapper.sh

将该路径替换默认路径,添加到你的 ~/.zshrc 配置文件中,教程里面有添加方法。

virtual environment 常见操作

可以按照教程推荐的,新命名一个环境名,例如 py3cv4 之类。常见的几个命令:

mkvirtualenv test # 新建一个名为 test 的环境
mkvirtualenv test --python=python3 # 也可以指定环境
lsvirtualenv # 列出全部环境
workon test # 进入该环境
deactivate # 退出环境
rmvirtualenv test # 移除该环境

有关 virtualenvwrapper 的使用可以参考:python环境神器virtualenvwrapper安装与使用

2.3 开始编译和安装 OpenCV

如果你没有跳过上一步 2.2,那么你可以按照原链接给出的方法,首先打开你建立的虚拟环境并在其中安装 OpenCV。如果跳过了 2.2,那么直接安装。

2.3.1 下载源码

wget -O opencv4.1.2.zip https://github.com/opencv/opencv/archive/4.1.2.zip
wget -O opencv_contrib4.1.2.zip https://github.com/opencv/opencv_contrib/archive/4.1.2.zip

这里第二个 opencv_contrib 是 OpenCV 的 extra modules,包含一些重要的 non-free 库等。

解压后随便放在同一个目录下,比如~/dev吧。然后在终端前进到该目录后,首先进入 opencv4.1.2 目录下,创建 build 目录并前进到该目录:

cd opencv-4.1.2
mkdir build
cd build

2.3.2 编译并安装

接下来,如果你使用了步骤 2.2 中的虚拟环境,那么使用如下的 cmake 命令:

cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D OPENCV_EXTRA_MODULES_PATH=~/dev/opencv_contrib-4.1.2/modules \
    -D PYTHON3_LIBRARY=`python -c 'import subprocess ; import sys ; s = subprocess.check_output("python-config --configdir", shell=True).decode("utf-8").strip() ; (M, m) = sys.version_info[:2] ; print("{}/libpython{}.{}.dylib".format(s, M, m))'` \
    -D PYTHON3_INCLUDE_DIR=`python -c 'import distutils.sysconfig as s; print(s.get_python_inc())'` \
    -D PYTHON3_EXECUTABLE=$VIRTUAL_ENV/bin/python \
    -D BUILD_opencv_python2=OFF \
    -D BUILD_opencv_python3=ON \
    -D INSTALL_PYTHON_EXAMPLES=ON \
    -D INSTALL_C_EXAMPLES=OFF \
    -D OPENCV_ENABLE_NONFREE=ON \
    -D BUILD_EXAMPLES=ON ..

如果你跳过了步骤 2.2(即没有虚拟环境),那么使用:

cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D OPENCV_EXTRA_MODULES_PATH=~/dev/opencv_contrib-4.1.2/modules \
    -D BUILD_opencv_python2=OFF \
    -D BUILD_opencv_python3=ON \
    -D INSTALL_PYTHON_EXAMPLES=ON \
    -D INSTALL_C_EXAMPLES=OFF \
    -D OPENCV_ENABLE_NONFREE=ON \
    -D BUILD_EXAMPLES=ON ..

注意的几点:

  • 其实上面两者的区别就是是否指定 python 的路径。如果是虚拟环境则需要显示指定,否则系统会默认找到你安装的 python3 的位置(当然你显式指定也行)。
  • 两个重要选项的设置:OPENCV_EXTRA_MODULES_PATH路径就是你的 opencv_contrib 的路径的 modules 文件夹的位置,一定要设置好。另外,下面的 OPENCV_ENABLE_NONFREE=ON是打开包含 SIFT 等 non-free feature 库的支持(源码就在 opencv_contrib 中)。
  • 只用 python3,关闭了 python2;
  • CMAKE_INSTALL_PREFIX是将来安装 OpenCV 的路径,OpenCV 此时会被安装到 /usr/local 下面。另一个常见的安装路径是 /usr/local/Cellar

接着,使用 make 来编译:

sudo make -j8

编译所需时间挺长的。成功后,安装:

sudo make install

这会将 OpenCV 安装到你设置的 CMAKE_INSTALL_PREFIX路径下。

你可以再检查一下你的 cmake 安装路径下有没有 opencv4 的 config 文件的目录:

 ls /usr/local/lib/cmake/opencv4

它通常应该存在并且包含了:

OpenCVConfig-version.cmake  OpenCVConfig.cmake          OpenCVModules-release.cmake OpenCVModules.cmake

如果该目录不存在,说明安装不完整,不妨检查一下。

如果你安装的是 OpenCV 3.4 的版本,那么有可能这些 config 文件只放在 OpenCV 安装目录下,并不会自动放在 cmake 目录中。如果这样,你可以显式链接一下就行,类似这样:

ln -s /usr/local/Cellar/opencv/3.4.8/  /usr/local/lib/cmake/opencv3.4.8

一个可能的编译错误

可能有的朋友在这一步会报错。找到新建的 build 目录下,说找不到这几个文件:

fatal error: boostdesc_bgm.i: No such file or directory

你可以 点击这里找到解决方法。个人的解决方法是,将下面的内容全部拷贝到 opencv_contrib-4.1.2 扩展库的 modules/xfeatures2d/src 目录下。

cd opencv-4.1.2
cp build/downloads/xfeatures2d/* ../opencv_contrib-4.1.2/modules/xfeatures2d/src

2.4 链接 OpenCV 到 python 虚拟环境 (optional)

这一步也是可选的,如果你跳过了步骤 2.2,那么也可以跳过这一步。如果你用的是全局的 python(无虚拟环境),那么安装 OpenCV4 成功后,它应该会自动连接到 python 了。

首先找到 python 安装目录,根据你的安装目录。一般默认会在 /usr/local/lib 下,例如:

/usr/local/lib/python3.7

确认该目录下存在这个文件:cv2.cpython-37m-darwin.so,它通常在如下的目录中:

/usr/local/lib/python3.7/site-packages/cv2/python-3.7/cv2.cpython-37m-darwin.so

接下来将该文件链接到你的虚拟环境中。首先使用命令 workon py3cv4进入你的虚拟环境py3cv4。然后,使用which python3命令可以找到虚拟环境中 python 库的安装路径,并前进到该目录下。例如如下所示的目录:

cd /Users/ciggomac/.virtualenvs/cv/lib/python3.7

最后,链接刚刚找到的 cv2.cpython-37m-darwin.so 到你的虚拟环境中:

ln -s /usr/local/lib/python3.7/site-packages/cv2/python-3.7/cv2.cpython-37m-darwin.so  cv2.so

2.5 测试 OpenCV 安装

在 python3 中测试是否能正常打印 OpenCV 版本号:

python3
Python 3.7.4 (default, Sep  7 2019, 18:31:37)
[Clang 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.2'
>>>

使用

cv::Mat Usage 使用说明

参考:Mat - The Basic Image Container

Mat构成

cv::Mat的构成很特殊。它包含两个部分:

  • 矩阵头部(matrix header),它包含了诸如矩阵的尺寸和存储方式等。因此,Mat的header的大小通常是固定的,而整个Mat大小显然不固定,因为矩阵主体数据不固定。
  • 一个指向矩阵主体数据的指针。也就是说,两个不同的cv::Mat的指针可以指向同一块数据区域。这样的好处是,图片通常都比较大,这样无需分配额外内存来存储多余的图片。当然缺点很明显,如果Mat指向的数据区域被release的话,所有的指向它的Mat都会失效。这样的话,将来再次尝试获取某个像素的话就会出错。

因此,Mat的Copy constructor和Assignment operator都是仅仅拷贝了矩阵的header,并未拷贝矩阵主体。换句话说,如果原始的Mat被release的话,新的拷贝Mat所指向的矩阵主体就会为空。同样的,如果修改拷贝的Mat,那么原始的Mat指向的矩阵主体也会改变。

// 此时只是定义了矩阵的header部分,还未给矩阵主体分配空间
cv::Mat A, C; 
// 读入图片同时为矩阵主体分配内存空间
A = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR); 
// 注意:这里的B只拷贝了A的header而已,它的指针指向A指向的矩阵主体部分。
// 此后如果修改B的话,A指向的内容也会被改变,这很危险。
cv::Mat B(A);
// Assignment operator和上面的Copy Constructor作用相同,还是只拷贝了header部分。
C = A; 

那么,如果真的想要整体拷贝一个Mat全部内容怎么办?有两个函数:clone()以及copyTo():

Mat F = A.clone(); // 直接硬拷贝
Mat G;
A.copyTo(G); // copyTo和clone()结果完全一样,就是用法稍有不同

初始化

参考:

这里简单放几个常用的:

// 初始化元素完全相同
Mat M(2,2, CV_8UC3, Scalar(0,0,255)); // 最后一个参数是初始化的constant数值
// 初始化元素不相同
cv::Mat T1 = (cv::Mat_<double> (3,4) <<
    1,0,0,0,
    0,1,0,0,
    0,0,1,0); // cv::Mat_是最底层的Mat类,需要重载元素类型。注意这种初始化方式是行优先的(row-major)

Mat成员函数

例子:

if (img.depth() == CV_8U){
    // depth()函数获取的是深度,即每个像素点中每个元素的类型。这里就是8-bit unsigned char类型
}
if (img.type() == CV_8UC3){
    // type()函数获取的是详细类型,这里就是8-bit unsigned char且有3个channels
}
if (img.channels() == 3){
    // channels()获取的是每个像素点的元素个数,例如如果是BGR图片,这个值就是3
}

Mat的类型

Mat的类型可以通过成员函数type()获取,但是它输出的是一个整数而不是可读性好的类似8UC1这种。例如,0就对应8UC1, 18是8UC3等。并且,OpenCV中居然没有将前者转换成后者的函数。下面给出这个函数,并且注释中有输出的整数和类型的对照表。

//! Convert the type of a cv::Mat object to a readable string
/*
* Correspondence between type number and string is in this table:
*
+--------+----+----+----+----+------+------+------+------+
|        | C1 | C2 | C3 | C4 | C(5) | C(6) | C(7) | C(8) |
+--------+----+----+----+----+------+------+------+------+
| CV_8U  |  0 |  8 | 16 | 24 |   32 |   40 |   48 |   56 |
| CV_8S  |  1 |  9 | 17 | 25 |   33 |   41 |   49 |   57 |
| CV_16U |  2 | 10 | 18 | 26 |   34 |   42 |   50 |   58 |
| CV_16S |  3 | 11 | 19 | 27 |   35 |   43 |   51 |   59 |
| CV_32S |  4 | 12 | 20 | 28 |   36 |   44 |   52 |   60 |
| CV_32F |  5 | 13 | 21 | 29 |   37 |   45 |   53 |   61 |
| CV_64F |  6 | 14 | 22 | 30 |   38 |   46 |   54 |   62 |
+--------+----+----+----+----+------+------+------+------+
*/
std::string getCvMatType(int type)
{
    std::string r;

    uchar depth = type & CV_MAT_DEPTH_MASK;
    uchar chans = 1 + (type >> CV_CN_SHIFT);

    switch (depth)
    {
        case CV_8U:
            r = "8U";
            break;
        case CV_8S:
            r = "8S";
            break;
        case CV_16U:
            r = "16U";
            break;
        case CV_16S:
            r = "16S";
            break;
        case CV_32S:
            r = "32S";
            break;
        case CV_32F:
            r = "32F";
            break;
        case CV_64F:
            r = "64F";
            break;
        default:
            r = "User";
            break;
    }
    r += "C";
    r += (chans + '0');
    return r;
}

其它使用方法

// 获取一个Mat中的一小块矩形部分
cv::Mat small_img = img(cv::Rect(0, 0, 100, 200)); // 注意这里依然是只拷贝了header
// 修改这个小块。注意此时img指向的矩阵主体也会改变,因为它们指向同一个主体部分
small_img.setTo(0);

Access an image 操作图片像素

参考:How to scan images, lookup tables and time measurement with OpenCV

OpenCV中,一张图片中每个像素点可能只有一个unsigned char变量,即8-bit元素,例如灰度图片;也可能是多个unsigned char变量,例如RGB或者RGBA彩色图片。这里每个像素中有几个量就叫做通道(channels)。灰度图片(grayscale)就是单通道,RGB图片就是三通道。下图演示了RGB图片中的数据存储:

OpenCV_image_channels

注意

  • OpenCV中读入彩色图片到cv::Mat中后,默认是按照BGR顺序,即蓝绿红,如上图所示。而通常很多图片存储后是按照RGB顺序的。这点一定要注意。
  • 可以通过cv::Mat类的成员函数channels()获取该图片的通道个数。

Get one pixel 获取一个像素

如果只是想要获取某一个像素的话,可以使用OpenCV中为Mat定义的at()函数:

// 灰度图像每个像素点就是一个unsigne char
unsigned char gray = gray_img.at<uchar>(i,j); 
// 彩色图像每个像素点包含RGB这三个channels(有时候是RGBA四个)
unsigned char b = color_img.at<cv::Vec3b>(i,j)[0]; 
unsigned char g = color_img.at<cv::Vec3b>(i,j)[1];
unsigned char r = color_img.at<cv::Vec3b>(i,j)[2];

Scan entire image 遍历整张图片

1. The efficient way 最快的方法:基于C指针

需要注意的是,虽然一张图片的物理尺寸是width x height,但是实际上,由于内存通常都足够大,因此OpenCV中的cv::Mat读入图片后,其实通常是将图片中全部的行按照顺序存储到一行中的。即,类似于将所有的行“平铺”到一行,这样的话,一张图片其实就是一个一维数组。这样做的好处是,可以加快扫描整张图片的速度。当然,这只是底层的存储方式有变化而已,该图片的顶层的各种参数全都不变。当然这也不一定,因此在实现中,可以用isContinuous()函数来判断一个cv::Mat变量是否是连续存储的。

如果是只存储一行,那么最快的获取每个像素的方法就是使用C中的操作符[]。下面代码的作用是,遍历整张图片,并将每个像素的灰度值通过table来in-place得映射到另一个值。

// 遍历整张图片,并将每个像素的灰度值通过table来in-place得映射到另一个值
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    int channels = I.channels();
    int nRows = I.rows;
    int nCols = I.cols * channels;
    // 检查一下是否是连续存储的。如果是,则更新行和列的值。
    if (I.isContinuous())
    {
        nCols *= nRows;
        nRows = 1;
    }
    int i,j;
    uchar* p;
    for( i = 0; i < nRows; ++i)
    {
        p = I.ptr<uchar>(i); // 先获取每一行开头的指针
        for ( j = 0; j < nCols; ++j)
        {
            // p[j]就是每个像素了。
            // Here it maps each pixel's grayscale value to another value.
            p[j] = table[p[j]];
        }
    }
    return I;
}

上面代码调用了isContinuous()来判断是否为连续存储的(即一行)。

2. The iterator (safe) way 迭代器做法(更安全)

上面的基于C的快速法中,用户需要自行确保判断了全部的channels、跳过有可能出现的行和行之间的gaps,并且确保不能越界。相比之下,使用C++的iterator更加安全一些。只需获取Mat的起始和终止指针,在中间遍历即可。不过,该方法还是比上面的C指针的方法慢一些。下面代码作用和上面的代码相同,只是增加了对BGR彩色图片的处理。

// 依然是遍历整张图片,并将每个像素的每个通道的颜色值通过table来in-place得映射到另一个值
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    const int channels = I.channels();
    switch(channels)
    {
    case 1:
        {
            MatIterator_<uchar> it, end;
            for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
                *it = table[*it];
            break;
        }
    case 3:
        {
            MatIterator_<Vec3b> it, end;
            for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
            {
                (*it)[0] = table[(*it)[0]];
                (*it)[1] = table[(*it)[1]];
                (*it)[2] = table[(*it)[2]];
            }
        }
    }
    return I;
}

3. On-the-fly address calculation 即时计算每个像素的地址

另一种方法是,我们直接使用前面讲述的获取每个像素的函数at(),获取每个像素点的位置指针就行了。但是,这种方法通常不推荐,因为它通常用于随机获取任意位置的像素。

Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    const int channels = I.channels();
    switch(channels)
    {
    case 1:
        {
            for( int i = 0; i < I.rows; ++i)
                for( int j = 0; j < I.cols; ++j )
                    I.at<uchar>(i,j) = table[I.at<uchar>(i,j)]; // 和前一种方法唯一的不同之处
            break;
        }
    case 3:
        {
        // 下面使用了Mat_类型,它是一种Mat的指针类型。使用它的好处是,在循环中就不用每次都要显式的像I.at<Vec3b>这样指定类型了。当然它的速度其实是和直接使用.at<Vec3b>一模一样,因此它的好处只是让代码更简洁一点而已。
         Mat_<Vec3b> _I = I;
         for( int i = 0; i < I.rows; ++i)
            for( int j = 0; j < I.cols; ++j )
               {
                   _I(i,j)[0] = table[_I(i,j)[0]];
                   _I(i,j)[1] = table[_I(i,j)[1]];
                   _I(i,j)[2] = table[_I(i,j)[2]];
            }
         I = _I;
         break;
        }
    }

    return I;
}

4. The Core Function 直接使用OpenCV相关函数

OpenCV定义了大量的和图片处理相关的函数供使用。因此,如果满足需求的话,可以直接使用这些函数,而不用再遍历整张图片每个像素了。例如,上面的例子所做的事情都是遍历整张图片,并将每个像素的每个通道的颜色值通过table来in-place得映射到另一个值。即,输入的table充当的是look-up table的作用。而OpenCV中已经定义了一个LUT()的函数来做这件事情(参见:https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#lut。实验结果表明,直接使用LUT()是本章这些方法中最快的。这是因为,OpenCV中定义的函数通常使用了基于Intel TBB(Threaded Building Blocks)的多线程以加速。

Mat& ScanImageAndReduceCoreFunction(Mat& I, const uchar* const table)
{
    // 定义look-up table变量是一个1x256的矩阵
    Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.data;
    for( int i = 0; i < 256; ++i)
        p[i] = table[i]; // 因为lookUpTable只有一行,因此直接赋值即可
    cv::Mat J = I.clone();
    cv::LUT(I, lookUpTable, J); // 调用已定义的函数,J是输出
    return J;
}

Summary 用法总结

在实现中,如果你的需求可以直接使用OpenCV的函数实现,那么直接使用它即可。显然它是最快并且基本上是最安全的方法。否则,尽可能使用上面第1中方法,即基于C指针的快速法。

一些常见的函数

作为函数参数的 Mask

很多 OpenCV 的函数都会有一个称为 Mask 的 Mat 矩阵作为输入或者输出。它的 size 通常是和作为主体输入或输出的 Mat 相同(例如一张图片),而每个元素的 type 通常是 unsigned char,即它通常类型是 8UC1。而它的元素值通常只是 binary 的,即, 0 表示对应的主体 Mat 的点没有被选中,1 表示被选中了。

cv::Mat mask;
cv::Mat E = cv::findEssentialMat(selected_points1,
                                 selected_points2,
                                 Kd.at<double>(0, 0),
                                 cv::Point2I d(image1.cols / 2., image1.rows / 2.),
                                 cv::RANSAC,
                                 0.999,
                                 1.0,
                                 mask);
vector<cv::Point2f> inlier_match_points1, inlier_match_points2;
for (int i = 0; i < mask.rows; i++) {
    if (mask.at<unsigned char>(i)) {
        // 只对选中的特征点进行操作
        inlier_match_points1.push_back(selected_points1[i]);
        inlier_match_points2.push_back(selected_points2[i]);
    }
}

常见的 cv::Mat 的操作

几乎所有常见的 Mat 操作都在这个链接里了:Operations on Arrays。其中,常见的有:

  • 两个 Mat 之间的加减乘除
  • 和另一个 Mat 的位操作,例如与或非;
  • 一些特殊的计算,例如指数对数;
  • 特征值特征向量;
  • 求和、平方和、最大值、最小值、方差、均值、直方图等各种统计;

不过注意,上面这个文档是 OpenCV 2.4 的,最新的 OpenCV 4 中的函数定义可能会稍有不同,不过函数名称几乎都不变。例如,个人记得是,OpenCV 2 中,如果除数是 0,那么最终除法的结果也是 0(即,避开了被除数是 0 的情况)。而 OpenCV 4 中,如果被除数是 0,那么最终除法的结果是 NaN 之类的值(需要额外增加一个判断),无论除数是否为 0。

Search

    Table of Contents