一. 基本数据类型
1.一些基本类型缩写:
| b | w | s | i | f | d |
|---|---|---|---|---|---|
| unsigned char | unsigned short | short | int | float | double |
2. Point类
Point类属于模板类, 简单且开销小. Point类通过别名调用, 并使用 . 访问成员变量, 例如:
| cv::Point2i | cv::Point2f | cv::Point2d | cv::Point3i | cv::Point3f | cv::Point3d |
|---|
操作函数如下:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::Point2i p; cv::Point3i p; |
| 复制构造函数 | cv::Point3f p2(p1); |
| 值构造函数 | cv::Point2i p(x0,x2); cv::Point3d p(x0, x1, x2); |
| 构造成固定向量类 | (cv::Vec3f) p; |
| 成员函数 | p.x, p.y, //三维中:P.Z |
| 点乘 | float x = p1.dot(p2); |
| 双精度点乘 | double x = p1.dot(p2) |
| 叉乘 | p1.cross(p2) //三维中使用 |
| 判断一个点p是否在矩形r内 | p.inside(r) //三维中使用 |
可以转化到固定向量类.
3. Scalar类
一个四维点类. 所有成员都是双精度浮点数. 一些操作函数如下:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::Scalar s; |
| 复制构造函数 | cv::Scalar s2(s1); |
| 值构造函数 | cv::Scalar s(x0); cv::Scalar s(x0, x1, x2, x3); |
| 元素相乘 | s1.mul(s2); |
| (四元数)共轭 | s.conj(); //return cv::Scalar(s0, -s1, -s2, 0s2); |
| (四元数)真值测试 | s.isReal(); //return true, if s1 == s2 == s3 == 0 |
4. size类
与Point类类似, 只要区别在于Point的数据成员是x和y(点), 而size类对应的数据成员是width和height.
size类拥有的三个别名分别为:
| cv::Size | cv::Size2i | cv::Size2f |
|---|
支持的操作为:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::Size sz cv::Size2i sz cv::Size2f sz; |
| 复制构造函数 | cv::SIze sz2(sz1) |
| 值构造函数 | cv::Size2f sz(w, h) |
| 成员访问 | sz.width; sz.height |
| 计算面积 | sz.area() |
不支持转换到固定向量类.
5. cv::Rect类
矩形类包含Point类的成员x和y(矩形左上角)和size类的成员width和height(代表的矩形的大小和位置).
但矩阵并非继承自Point类或者size类, 所以不具有其相关操作.
支持的操作:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::Rect r |
| 复制构造函数 | cv::Rect r2(r1) |
| 值构造函数 | cv::Rect(x, y, width, height) |
| 由起始点和大小构造 | cv::Rect(p, sz) |
| 由两个对角构造 | cv::Rect(p1, p2) |
| 成员访问 | r.x; r.y; r.width; r.height |
| 计算面积 | r.area() |
| 提取左上角 | r.tl() |
| 提取右下角 | r.br() |
| 判断点p是否在矩形内 | r.contains(p) |
覆写操作符:
| 操作 | 示例 |
|---|---|
| 矩形r1和矩形r2的交集 | cv::Rect r3 = r1 & r2; r1 &= r2; |
| 同时包含矩形r1和矩形r2的最小面积矩形 | cv::Rect r3 = r1 | r2; r1 |= r2; |
| 矩形r平移x个数量 | cv::Rect rx = r + x; r += x |
| 矩形r扩大s大小 | cv::Rect rs = r + s; r += s; |
| 比较矩形r1与矩形r2是否相等 | bool eq = (r1 == r2) |
| 比较矩形r1与矩形r2是否不想等 | bool ne = (r1 != r2) |
6. cv::RotatedRect类
包含一个中心点cv::Point2f, 一个大小cv::Size2f和一个角度float的容器.
其中float表示图形围绕中心点旋转的角度.
cv::RotatedRect和cv::Rect有一个非常重要的不同点是cv::RotatedRect是以中心为原点, 而cv::Rect则以左上角为原点. 支持的操作:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::RotatedRect rr() |
| 复制构造函数 | cv::RotatedRect rr2(rr1) |
| 从两个点构造 | cv::RotatedRect(p1, p2) |
| 值构造函数, 需要一个中心点point, 一个大小size和一个角度float | cv::Rotated rr(point, size, float) |
| 成员访问 | rr.center, rr.size, rr.float |
| 返回四个角的列表 | rr.points(ptr[4]) |
7. 固定矩阵类
固定矩阵类需要在创建时知道矩阵的纬度(大小). 其分配和清楚都很快. 在小型矩阵(2x2, 3x3, 等等)矩阵上做过特别的优化.
一般来说, 在编译的时候知道矩阵的大小的时候使用固定矩阵类(比如相机矩阵), 而如果对象是一个图像或大型点列表这样的大数组的时候, 通常使用cv::Mat.
cv::Matx支持的操作:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | cv::Matx33f m33f; cv::Matx43d m43d; |
| 复制构造函数 | cv::Matx22d m22d(n22d) |
| 值构造函数 | cv::Matx21f m(x0, x1); cv::Matx44d m(x0, x1, ...., x14, x15); |
| 含相同元素的矩阵 | m33f = cv::Matx33f::all( x ) |
| 全零矩阵 | m23d = cv::Matx23d::zeros(); |
| 全一矩阵 | m16f = cv::Matx16f::ones(); |
| 创建一个单位矩阵 | m33f = cv::Matx33f::eye(); |
| 成员访问 | m( i, j ), m( i ) |
8. 固定向量类
固定向量类从固定矩阵类派生出来, 可以说固定向量类是列为1的cv::Matx<>.
cv::Vec支持的操作:
| 操作 | 示例 |
|---|---|
| 默认构造函数 | Vec2s v2s; Vec6f v6f; |
| 复制构造函数 | Vec3f u3f( v3f ); |
| 值构造函数 | Vec2f v2f(x0, x1); Vec6d v6d(x0, x1, x2, x3, x4, x5); |
| 成员访问 | v4f[ i ]; v3w( j ); //operator()和operator[]都可以 |
| 向量叉乘 | v3f.cross( u3f ) |
9. 复数类
二. 一些辅助对象
1. cv::TermCriteria类
用于算法的终止条件(有限迭代次数或某种形式的误差(epsilon)).
含有三个变量, 可以通过构造函数进行设置TermCriteria(int type, int maxCount, double epsilon);
type参数必须经过相应的设置以使用maxCount或epsilon. (可以设置为为TermCriteria::COUNT或者TermCriterai::EPS).
2. Range类
用于确定一个连续的整数序列, 拥有两个元素start和end, 可以在构造函数cv::Range(int start, int end)中进行设定, 范围包含初始值start, 但不包含终止值end
使用size()函数可以得到range类的元素数量.
三. 图像和大型数组类型
1. cv::Mat类N维稠密数组
可作为任意维度的数组使用, 数据按照栅格扫描顺序存储的n维数组.
所有的矩阵中都包含表示数组类型的元素flag, 表示维度的元素dims 分别表示行和列的数目元素row和cols 一个指示真正存储位置的data值镇 以及表示该内存区域有多少个引用的refcount元素.
初始化函数
构造函数
| 构造函数 | 说明 |
|---|---|
| cv::Mat | 默认构造函数 |
| cv::Mat( int rows, int cols, int type ); | 指定类型的二维数组 |
| cv::Mat( int rows, int cols, int type, const Scalar&s ); | 指定类型的二维数组, 并指定初始化值 |
| cv::Mat( int rows, int cols, int type, void *data, size_t step=AUTO_STEP ); | 指定类型的二维数组, 并指定预先存储的数据 |
| cv::Mat( cv::Size sz, int type ); | 指定类型的二维数组, (大小由sz指定) |
| cv::Mat( cv::Size sz, int type, const Scalar&s ); | 指定类型的二维数组, 并指定初始化值 |
| cv::Mat( cv::Size sz, int type, void *data, size_t step=AUTO_STEP ); | 指定类型的二维数组,并指定预先存储的数据 |
| cv::Mat( int ndims, const int* sizes, int type ); | 指定类型的多维数组 |
| cv::Mat( int ndims, const int* sizes, int type, const Scalar&s ); | 指定类型的多维数组, 并指定初始化值 |
| cv::Mat( int ndims, const int* sizes, int type, void* data, size_t step=AUTO_STEP ); | 指定类型的多维数组, 并指定预先存储的数据 |
其可以分为三个类型: 输入行数和列数构造一个二维数组 使用cv::Size构造一个二维数组 构造n维数组并通过一个整型的序列来确定每一维数据
复制构造函数
| 复制构造函数 | 描述 |
|---|---|
| cv::Mat( const Mat& mat ); | 复制构造函数 |
| cv::Mat( const Mat& mat, const cv::Range& rows, const cv::Range& cols ); | 从指定的行列中复制数据 |
| cv::Mat( const Mat& mat, const cv::Rec& roi ); | 从感兴趣的区域中复制数据 |
| cv::Mat( const Mat& mat, cosnt cv::Range* ranges ); | 服务于n维数组的, 从泛化的感兴趣区域中复制数据 |
| cv::Mat( const cv::MatExpr& expr ); | 从其他矩阵的线性代数表述中生成新矩阵 |
其复制构造函数也分为三个类别: 输入行和列的范围 使用cv::Rect来指定一个矩形的子区域(只对二维矩阵有效) 输入一个range数组(range所指向的有效范围必须和mat的维度相等)
模板构造函数
| 模板构造函数 | 描述 |
|---|---|
| cv::Mat( const cv::Vec<T, n>& vec, bool copyData = true ); | 构造一个如同cv::Vec所指定的 数据类型为T, 大小为n的一位 |
| cv::Mat( const cv::Matx<T, m, n>& vec, bool copyData = true ); | 构造一个如同cv:Matx所指定的 数据类型为T, 大小为m x nd的二维数组 |
| cv::Mat( cosnt std::vector< T >& vec, bool copyData = true ); | 构造STL的vector所指定的 数据类型为T, 大小为vector的元素的一维数组 |
静态方法构造函数
| 函数 | 描述 |
|---|---|
| cv::Mat::zeros ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 值全为0的矩阵 |
| cv::Mat::ones ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 值全为1的矩阵 |
| cv::Mat::eye ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 的单位矩阵 |
数组数据获取
独立获取数组元素
通过模板函数**at<>()**来直接访问数组中的某一个元素, 其对不同维度的数组有不同的参数要求.
模板类要求先将**at<>()**转化为矩阵所包含的数据类型, 然后使用你所想要的数据的行和列的位置访问该元素.
例子:
//单通道访问单一元素
cv::Mat m = cv::Mat::eye(10, 10, CV_32FC1); //单通道浮点型单位矩阵
cout << m.at<float>(3, 3) << endl; //模板类使用float
//双通道访问单一元素
cv::Mat m2 = cv::Mat::eye(10, 10, CV_32FC2); //双通道浮点型单位矩阵
cout << m.at<cv::Vec2f>(3, 3)[0] << endl; //模板类使用Vec2f 访问0通道元素
注意: 当你想要对多维数组指定一个类似于at<>() 的模板函数时, 最好的方式是使用cv:Vec<>对象
at<>()访问函数的变体:
| 示例 | 描述 |
|---|---|
| M.at< int >( i ); | 整型数组M中元素i |
| M.at< float >( i, j ); | 浮点型数组M中的元素( i, j ) |
| M.at< int >( pt ); | 整型矩阵M中处于( pt.x, pt.y )的元素 |
| M.at< float >( i, j, k ); | 三维浮点型矩阵M中处于(i, j, k )位置的元素 |
| M.at< uchar >( idx ); | 无符号字符数组M中位于idx[]索引的n维位置的元素 |
也可以使用**ptr<>()**来访问数组, 得到指向数组的指针.
例如: 给定一个类型为float三通道的矩阵mtx, 结构体**mtx.ptr< Vec3f >(3)**将返回mtx的第三行指向第一个元素第一个(浮点)通道的指针.
经过本人实践,使用at会很大概率有问题。为了开发效率考虑,不要使用at;不要使用at;不要使用at;
请使用ptr;请使用prt;请使用ptr
注意访问图片的类型(char就是char,int就是int,不能混淆)
使用数组迭代器
cv::MatIterator<>
使用cv::Mat内嵌的迭代器机制, 这种机制在某种程度上是基于STL容器所提供的机制. 基础想法是OpenCV提供一对迭代器模板, 一个用于只读(Const)数组和一个用于非只读的数组. 上述迭代器分别被命名为cv::MatConstIterator<>和cv::MatIterator<>. cv::Mat的成员函数begin()和end()会返回这种类型的对象.
例子: 使用迭代器计算三通道三维数组中"最长"元素(一个三维向量域)
int sz[3] = {4, 4, 4};
cv::Mat m(3, sz, CV_32FC3);
//在m中填充-1.0 - 1.0的小数
cv::randu(m, -1.0f, 1.0f);
float max, len = 0.0f;
//使用迭代器模板类获取m的起始位置
cv::MatConstIterator_<cv::Vec3f> it = m.begin<cv::Vec3f>();
while(it != m.end<cv::Vec3f>())
{
len = (*it)[0]*(*it)[0] + (*it)[1]*(*it)[1] + (*it)[2]*(*it)[2];
if (len > max)
max = len;
it++;
}
NAryMatIlterator
该迭代器只要求被迭代的数组有相同的几何结构(维度以及每一个维度的范围).
与MatIterator不同, 该迭代器不会返回一个用于迭代的单独元素, 而通过返回一堆数组来进行N-ary迭代器操作, 这些返回的数组也称作"面"(Plane). 一个面表示输入数组有连续内存的部分.
通过块访问数组元素
该方法适用于: 需要将一个数组的子集作为另一个数组访问, 这个子集可能是一行或者一列, 也可能是原始数组的一个子集.
cv::Mat区块访问
| 示例 | 描述 |
|---|---|
| m.row( i ); | m中第 i 行数组 |
| m.col( j ); | m中第 j 列数组 |
| m.rowRange( i0, i1 ); | m中第 i |
| m.rowRange( cv::Range( i0, i1 )); | m中第 i |
| m.colRange( j0, j1 ); | m中第 j |
| m.colRange( cv::Range( i0, i1 )); | m中第 j |
| m.diag( d ); | m中偏移为 d 的对角线所构成的数组 (若参数为0, 将会是主队角, 若为正数, 则相对于主队角向数组上半部分 偏移, 反之负数向下半部分偏移) |
| m( cv::Range( i0, i1), cv::Range( j0, j1 )); | m中从点( i0, j0 )到点( i1, j1 )所包含数据构成的数组 |
| m( cv;:Rect( i0, i1, w, h )); | m中从点( i0, j0 )到点( i0+w-1, j0+h-1 )所包含的数据 构成的数组 |
| m( ranges ); | m中依据ranges[0]到ranges[ndim-1]所索引区域 构成的数组 |
矩阵表达式
使用运算重载符运算矩阵cv::Mat
| 示例 | 描述 |
|---|---|
| m0 + m1, m0 - m1 | 矩阵的加法和减法 |
| m0 + s; m0 - s; s - m1; | 矩阵和单个元素的加和减 |
| -m0; | 矩阵取负 |
| s * m0; m0 * s; | 缩放矩阵; |
| m0.mul( m1 ); m0/m1; | 将元素m0和m1相乘, 按元素将m0和m1 相除; |
| m0 * m1; | m0和m1进行矩阵乘法 |
| m0.inv( method ); | 对m0矩阵进行求逆(默认使用DECONP_LU); |
| m0.t(); | 对m0矩阵求转置; |
| m0>m1; m0>=m1; m0==m1; .... | 按元素进行比较, 返回元素只有0和255的 uchar类型矩阵; |
| min(m0, m1); max(m0, m1); min(m0, s); min(s, m0); max(m0, s); max(s, m0); | 矩阵和矩阵之间或者矩阵和单个元素之间按元素 取最大或者最小值. |
| cv::abs( m0 ); | 对m0按元素取绝对值; |
其他一些操作:
| 示例 | 描述 |
|---|---|
| m0.cross( m1 ); m0.dot( m1 ); | 向量叉乘和点乘操作; (叉乘操作只适用于3x1矩阵); |
| cv::Mat::eye( Nr, Nc, type ); cv::Mat::zeros( Nr, Nc, type ); cv::Mat::ones( Nr, Nc, type ); | 用于返回规定类型NxN矩阵的静态方法; |
矩阵求逆操作inv()实际上是一系列矩阵求逆操作的接口:
- 使用cv::DECOMP_LU进行LU分解, 此方法对任意的非奇异矩阵都有效.
- 使用cv::DECOMP_CHOLESKY, 表示使用Cholesky分解, 其在矩阵半正定的时候有效, 且在处理大型矩阵的时候比LU分解快很多.
- 使用cv::DECOMP_SVD, 为奇异值分解(SVD)进行求逆, 在矩阵奇异或者非方阵的情况下都可以工作(可以求出矩阵的伪逆).
其他的一些成员函数
| 示例 | 描述 |
|---|---|
| m1 = m0.clone(); | 从m0进行完全复制所有的数据元素 |
| m0.copyTo( m1 ); | 将m0复制给m1, 如果有必要, 将给m1重分配内存空间 (等同于m1 = m0.clone() ); |
| m0.copyTo( m1, mask ); | 只复制mask所指示的区域 |
| m0.convertTo( m1, type, scale, offset ); | 转换m0中元素的类型, 并且在尺度变换和增加偏置后赋值给m1; |
| m0.assignTo( m1, type ); | 只在内部使用(集成在convertTo中) |
| m0.set( s, mask ); | 将m0所有元素设为s, 如果存在mask, 则只对mask指示区域进行操作 |
| m0.reshape( chan, rows ); | 改变二维数组的有效形状, chan和rows若为0, 则便是不做更改 |
| m0.push_back( s ); | 在末尾增加一个mx1大小的数组 |
| m0.push_back( m1 ); | 向m x n大小的矩阵m0增加k行并复制到m1中, m1大小必须是k x n (扩充矩阵) |
| m0.pop_back( n ); | 从m x n大小的矩阵移除n行 |
| m0.locateROI( size, offset ); | 将m0的全尺寸写入cv::Size size, 如果m0是一个大矩阵的一块区域, 还会 写入Point类型的offset |
| m0.adjustROI( t, b, l, r ); | 通过t(最上), b(最下), l(最左), r(最右), 调整ROI范围 |
| m0.total(); | 计算数组序列的元素的数目(不包括通道) |
| m0.isContinous(); | 如果m0的行之间没有空隙, 将返回true |
| m0.elemSize(); | 返回m0的位长度(比如三通道浮点矩阵将返回12) |
| m0.elemSize1(); | 返回m0的最基本元素的位长度(比如三通道浮点矩阵将返回4) |
| m0.type(); | 返回m0的元素类型(比如: CV_32FC3) |
| m0.depth(); | 返回m0通道中的元素类型(比如: CV_32F) |
| m0.channels(); | 返回m0的通道数目 |
| m0.size(); | 以从cv::Size返回m0的大小 |
| m0.empty(); | 如果数组没有元素, 返回true |
2. cv::SparseMat稀疏数据类
cv::SparseMat稀疏类矩阵在非0元素非常少的情况下使用(0很多), 在一些特殊的应用中, 大部分元素为空的时候使用稀疏类矩阵可以节约大量的内存. 但稀疏类矩阵计算时需要对每个元素进行计算, 其运算速度较慢.
Opencv的稀疏矩阵类cv::SparseMat函数在很多方面与稠密矩阵类cv::Mat的定义十分相似.
cv::Mat使用接近C风格(数据按照序列被打包, 且地址可以通过元素的索引计算出来), 而cv::SparseMat使用哈希表来存储非0元素, 这个哈希表会自动维护, 当数组中的非0数据变得太多以致于无法高效进行查找的时候, 表也会增长.
访问稀疏数组中的元素
函数访问
稀疏数组提供四种访问机制:
| cv::SparseMat::ptr() | cv::Sparse::ref() | cv::SparseMat::value() | cv::SparseMat::find() |
|---|
1. ptr方法
cv::SparseMat::ptr()方法有几种变体, 最简单的如下:
uchar* cv::SparseMat::ptr(int i0, bool createMissing, size_t* hashval=0)
这个版本用于访问一维数组, 第一个参数i0是请求元素的索引, 第二个参数creatMissing表示这个元素是否应该被创建
2. ref方法
cv::SparseMat::ref<>()用于返回一个指向数组中特定元素的引用, 这个函数类似于SparseMat::ptr(), 可以接受一个、两个或者三个索引, 或者一个索引数组, 并且同样支持一个可选的用于查找的哈希值.
3. value方法
cv::SparseMat::value<>()和cv::SparseMat::ref<>()彼此独立, 它将返回一个值而不是返回值的引用, 因此, 这个方法也叫"只读方法"
4. find方法
返回一个请求对象的指针, 其指针类型由cv::SparseMat::find<>()的模板指定. 是一个"只读方法".
迭代器访问
迭代器访问可使用cv::SparseMatIterator_ <>() 和 cv::SparseMatConstIterator_ <>(), 其包含模板类和非模板类(可以不使用模板, 返回一个非模板的迭代器).
使用迭代器例子:
int size[] = {10, 10};
cv::SparseMat sm(2, size, CV_32F);
for (int i=0; i<10; i++)
{
int idx[2];
idx[0] = size[0] * rand();
idx[1] = size[1] * rand();
sm.ref<float>(idx) += 1.0f; //对sm中的随即索引赋值
}
cv::SparseMatIterator_<float> it = sm.begin<float>();
cv::SparseMatIterator_<float> it_end = sm.end<float>();
for (; it != it_end; ++it)
{
const cv::SparseMat::Node* node = it.node();
cout << node->idx[0] << endl;
cout << node->idx[1] << endl;
cout << *it << endl << endl;
}
以上的例子中, 路过了定义在迭代器中的方法node(), node()返回一个指向被迭代器索引指向的稀疏矩阵的实际数据区域, 它返回一个类型为cv::SparseMat::Node的对象:
struct Node
{
size_t hashval;
size_t next;
int idx[cv::MAX_DIM];
};
稀疏矩阵的特有函数
| 示例 | 描述 |
|---|---|
3. 大型数组模板结构
四. 矩阵操作
矩阵函数
输入为矩阵类型, 输出为矩阵类型, 或者输入输出同为矩阵类型.
| 函数名称 | 描述 |
|---|---|
| cv::abs() | 计算矩阵中所有元素的绝对值 |
| cv::absdiff() | 计算两个矩阵 差值的绝对值 |
| cv::add() | 实现两个矩阵按元素相加 |
| cv::addWeighted()1 | 实现两个矩阵按元素加权求和 |
| cv::bitwise_and() | 计算两个矩阵逐元素按位与 |
| cv::bitwise_or() | 按位或 |
| cv::bitwise_xor() | 按位异或 |
| cv::bitwise_not | 按位非 |
| cv::calcCovarMatrix() | 计算一组n维向量的协方差 |
| cv::cartToPolar() | 计算二维向量的角度和幅度 |
| cv::checkRange() | 检查矩阵的无效值 |
| cv::compare() | 对两个矩阵中的所有元素应用所选择的比较运算符 |
| cv::completeSymm() | 通过将一半元素复制到另一半来使矩阵对称 |
| cv::convertScaleAbs() | 缩放矩阵, 取绝对值, 然后转换为8位无符号数 |
| cv::countNonZero() | |
| cv::arrToMat() | |
| cv::dct() | |
| cv::determinant() | |
| cv::dft() | |
| cv::divide() | |
| cv::eigen() | |
| cv::exp() | |
| cv::extractImageCOI() | |
| cv::flip()2 | 将图像绕着x轴或y轴或同时绕x,y进行翻转,默认设置为0只绕着x轴翻转 |
| cv::gemm() | |
| cv::getConvertElem() | |
| cv::getConvertScaleElem() | |
| cv::idct() | |
| cv::idft() | |
| cv::inRange() | |
| cv::invert() | |
| cv::log() | |
| cv::magnitude() | |
| cv::LUT() | |
| cv::Mahalanobis() | |
| cv::max() | |
| cv::mean()3 | 计算输入矩阵src中未被屏蔽的所有像素的平均值 |
| cv::meanStdDev()4 | 计算输入矩阵src中未被屏蔽的像素的平均值以及它们的标准差, 如果src是多通道的, 则以每个通道为基础计算平均值和标准差 |
| cv::merge() | |
| cv::min() | |
| cv::minMaxLoc() | |
| cv::mixChannels() | |
| cv::mulSpectrums() | |
| cv::multiply() | |
| cv::mulTransposed() | |
| cv::norm() | |
| cv::normalize() | |
| cv::perspectiveTransform() | |
| cv::phase() | |
| cv::polarToCart() | |
| cv::pow() | |
| cv::randu() | |
| cv::randn() | |
| cv::randShuffle() | |
| cv::reduce() | |
| cv::repeat() | |
| cv::saturate_cast<>() | |
| cv::scaleAdd() | |
| cv::setIdentity() | |
| cv::solve() | |
| cv::solveCubic() | |
| cv::solvePoly() | |
| cv::sort() | |
| cv::sortIdx() | |
| cv::split()5 | 将一个多通道矩阵分割成多个单通道矩阵 |
| cv::sqrt() | |
| cv::subtract() | |
| cv::sum() | |
| cv::theRNG() | |
| cv::trace() | |
| cv::transform() | |
| cv::transpose() |
cv::addWeight()
void cv::addWeight(
cv::InputArray src1, //第一个输入矩阵
double alpha, //第一个输入矩阵权重
cv::InputArrary src2, //第二个输入矩阵
double beta, //第二个输入矩阵权重
double gamma, //offset added to weighted sum
cv::OutputArray dst, //输入结果矩阵
int dtype = -1 //结果输出类型
);
此函数可以用于实现alpha混合. 参数是src1的混合强度, 是src2的混合强度.
cv::mean()
cv::Scalar cv::mean(
cv::InputArray src,
cv::InputArray mask = cv::noArray()
);
计算src中未被mask屏蔽的像素均值
cv::meanStdDev()
void cv::meanStdDev(
cv::InputArray src,
cv::OutputArray mean, //输出的均值
cv::OutputArray stdDev, //输出的均方差
cv::InputArray mask = cv::noArray()
);
计算输入矩阵src中未被屏蔽的像素的平均值以及它们的标准差, 如果src是多通道的, 则以每个通道为基础计算平均值和标准差.
void cv::flip(
cv::InputArray src,
cv::OutputArray dst,
int flipCode = 0
);
该函数将图像绕着X轴或Y轴或者同时绕着X轴和Y轴翻转,默认情况下,flipCode将被设置为0,图像只绕着X轴翻转
如果flipCode被设置为大于0的数,图像会绕Y轴翻转,如果被设置为一个负值,图像将围绕X轴和Y轴翻转(0为绕着X轴翻转)
cv::split()
void cv::split(
const cv::Mat& mtx,
cv::Mat* mv
);
void cv::split(
const cv::Mat& mtx,
vector<Mat>& mv //STL类型
);
使用cv::split()将多通道矩阵中的通道分为多个单通道矩阵.
务必确保可用的cv::Mat对象的数量(至少)等于mtx中的通道数, 如果使用STL向量形式, cv::split()将为你分配结果矩阵的内存.
五. 绘图和注释
绘图
opencv的绘图函数可以在任意深度的图像上工作, 但在大多数情况下只对图像的前三个通道有影响.
想指定颜色的时候, 通常的做法是使用cv::Scalar对象, 即使大多数的时候只能用到最前面的三个值. 按照惯例, opencv在对多通道图像进行色彩渲染时使用了BGR的顺序.
绘图函数
| 函数 | 描述 |
|---|---|
| cv::circle() | 画一个圆 |
| cv::clipLine() | 判断一条直线是否在给定的矩形内 |
| cv::ellipse() | 画一个椭圆 |
| cv::ellipse2Poly() | 计算一个近似椭圆的多边形 |
| cv::fillConvexPoly() | 画一个填充的简单多边形 |
| cv::line() | 画一个简单直线 |
| cv::rectangle() | 画一个简单矩形 |
| cv::polyLines() | 画多重折线 |
cv::circle()
void circle(
cv::Mat &img,
cv::Point center,
int radius,
const cv::Scalar& color,
int thickness = 1,
int lineType = 8,
int shift = 0
);
- 第二个参数为圆心, 一个二维坐标; 第三个参数是半径
- 颜色, 粗细, 线的类型
cv::clipLine()
cv::line()
void line(
cv::Mat& img,
cv::Point pt1,
cv::Point pt2,
const cv::Scalar& color,
int lineType = 8,
int shift = 0
);
函数在图像img上绘制一条从pt1到pt2的直线, 直线自动被图像边缘截断
cv::rectangle()
void rectangle(
cv::Mat& img,
cv::Point pt1,
cv::Point pt2,
const cv::Scalar& color,
int lineType = 8,
int shift = 0
);
void rectangle(
cv::Mat& img,
cv::Rect r,
const cv::Scalar& color,
int lineType = 8,
int shift = 0
);
使用cv::rectangle()函数在img上绘制一个从角点pt1到pt2的矩形,或可以使用cv::Rect类型的参数来指定矩形
字体和文字
文字绘制函数
| 函数名称 | 描述 |
|---|---|
| cv::putText() | 在图像种绘制指定文字 |
| cv::getTextSize() | 获取一个文字的宽度和高度 |
cv::putText()
cv::getTextSize()
六. OpenCV中的函数子
主成分分析cv::PCA
奇异值分解cv::SVD
随机数发生器cv::RNG
七. 图像、视频和数据文件
图像
从磁盘读取图像或者将图像保存到磁盘是我们完成的最基本的任务, 最简单的实现方法是利用OpenCV的cv::imread() 和 cv::imwrite() 函数, 这些函数会帮助你完成包括与文件系统进行交互, 图像编码和解码在内的所有工作.
cv::imread()
用来读取图像, 其函数定义:
cv::Mat cv::imread(
const string& filename, //inut name
int flags = cv::IMREAD_COLOR //flags set how to interpret file
);
flags可选参数:
| 标志 | 含义 | 默认值 |
|---|---|---|
| cv::IMREAD_COLOR | 总是读取三通道图形图像 | 是 |
| cv::IMREAD_GRAYSCALE | 总是读取单通道图像(灰度图像) | 否 |
| cv::IMREAD_ANYCOLOR | 通道数由文件实际通道数(不超过3) | 否 |
| cv::IMREAD_ANYDEPTH | 允许加载超过8bit深度 | 否 |
| cv::IMREA_UNCHANGED | cv::IMREAD_ANYCOLOR和cv::IMREAD_ANYDEPTH组合 | 否 |
若cv::imread()载入图像失败, 并不会抛出一场, 而是返回一空的cv::Mat(可以用cv::Mat::empty()==ture来进行判断)
cv::imwrite()
用来保存图像, 其函数定义:
bool cv::imwrite(
const string& filename, //Input filename
cv::InputArry image, //Image to write to file
const vector<int>& params = vector<int>() //(Optional) for paramterized fmts
);
第一个参数定义了文件名, 文件拓展名部分可用来决定如何保存图像, OpenCV支持的常用拓展名:
- .jpg或者.jpeg 8位数据;单通道或三通道输入
- .jp2
- .tif或.tiff 8位或者16位数据; 单通道, 三通道或四通道输入
- .png 8/16位数据; 单, 三, 四通道输入
- .bmp 8位数据; 单, 三, 四通道输入
- .ppm 8位数据; 单, 三通道
第三个参数被用作特殊类型文件的写入操作时所需的数据. 输入参数为内部为整型数据的一个STL vector.
其具体内容为: 一系列的参数ID, 以及与该参数对应的参数值, 每个参数ID之后跟着其对应的值
参数ID起的别名以及取值范围:
| 标志 | 含义 | 取值范围 | 默认值 |
|---|---|---|---|
| cv::IMWRITE_JPG_QUALITY | JPEG的质量 | 0~100 | 95 |
| cv::IMWRITE_PNG_COMPRESSION | PNG的压缩值(值高压缩率高) | 0~9 | 3 |
| cv::IMWEITE_PXM_BINARY | 对PPM, PNG或PBM文件是否使用二进制 | 0或1 | 1 |
若成功保存图像则返回true, 反之, 返回false
cv::imencode()
用于对图像进行编码
cv::imdecode()
用于对图像进行解码
视频
cv::VideoCapture
构造方式
cv::VideoCapture::VideoCapture(
const string& filename //input filename
);
cv::VideoCapture::VideoCapture(
int device //Viedo capture device id
);
cv::VideoCapture::VideoCapture();
第一个构造函数中, 需要给出视频的文件名, 若打开成功会返回true, 可以通过使用cv::VideoCapture::isOpen()来对返回值进行检查.
第二个构造函数中, 若当前系统中只有一个摄像机时参数为0, 当有多个相机时, 参数随之递增.
第三个构造函数可以通过先声明对象而不提供关于即将打开的硬件设备的任何信息.
cv::VideoCapture cap;
cap.open("my_video.avi")
在这种情况下, cv::VideoCapture::open()函数最终产生的效果与调用相同参数的cv::VideoCapture构造函数相同.
read()
用于从视频流中读取图像
bool cv::VideoCapture::read(
cv::OutputArray image //image into which to read data
);
该函数的作用是从cv::VideoCapture表示的文件中读取下一帧数据, 并将数据插入你提供的变量中, 同时, 这个行为将使VideoCapture对象更新, 以方便读取下一帧. 若读取没有成功(比如已经读取到文件的最后一帧), 那么该函数将返回false.
opertor>>()
从视频流中读取图像数据.
cv::VideoCapture& cv::VideoCapture::operator>>(
cv::Mat& image //image into which to read data
);
作为对cv::VideoCapture::read()函数的补充, 你还可以使用重载函数cv::VideoCapture::operator>>() (输入流操作符号)从VideoCapture对象中读取下一帧数据.
grab()和etrieve()
*一个捕获操作(类似于内存的拷贝) 和 恢复操作(对已抓取的数据进行解码). *
bool cv::VideoCapture::grab(void);
bool cv::VideoCapture::retrieve(
cv::OutputArray image,
int channel = 0 //used for mulihead devices
);
grab()函数将当前的图像数据拷贝到用户看不见的内存缓冲区. grab()函数只是设计用于将原始图像数据尽快获取到电脑.
tips
之所以将捕获(grab) 和 恢复(retrieve)分开进行而不是像read()那样将捕获和解码同时进行是因为, 多相机的捕获是最常见的一种情况(例如立体成像). 在这种情况下需要缩短多个相机获取的图像帧之间的时间差. 因为对于数据的抓取, 将数据安全放入缓存区之后在进行解码恢复, 这样做非常有意义.
get()和set()
用于获取相机属性
double cv::VideoCapture::get(
int propid //Property identifier
);
bool cv::VideoCapture::set(
int propid, //Property identifier
double value //Value to which to set the property
);
cv::VideoCapture::get()常用属性:
| 视频捕获属性 | 是否只有在摄像头 模式下使用 | 含义 |
|---|---|---|
| cv::CAP_PROP_POS_MSEC | 视频文件中的当前位置(毫秒)或视频捕获 时间戳 | |
| cv::CAP_PROP_POS_FRAMES | 从零开始下一帧的索引 | |
| cv::CAP_PROP_POS_AVI_RATIO | 视频中的相对位置(范围从0.0到1.0) | |
| cv::CAP_PROP_FRAME_WIDTH | 视频帧的像素宽度 | |
| cv::CAP_PROP_FRAME_HEIGHT | 视频帧的像素高度 | |
| cv::CAP_PROP_FPS | 录制视频的帧速率 | |
| cv::CAP_PROP_FOURCC | 四个字符代码指示编解码 | |
| cv::CAP_PROP_FRAME_COUNT | 视频文件的帧总数 | |
| cv::CAP_PROP_FORMAT | 返回的Mat对象的格式(例如CV_8UC3) | |
| cv::CAP_RPOP_MODE | 表示捕捉模式, 值是特定与正在使用的后端 | |
| cv::CAP_PROP_BRIGHTNESS | :heavy_check_mark: | 相机的亮度设置(支持时) |
| cv::CAP_PROP_CONTRAST | :heavy_check_mark: | 相机的对比度设置(支持时) |
| cv::CAP_PROP_SATURATION | :heavy_check_mark: | 相机饱和度设置(支持时) |
| cv::CAP_PROP_HUE | :heavy_check_mark: | 相机色调设置(支持时) |
| cv::CAP_PROP_GAIN | :heavy_check_mark: | 相机的增益设置(支持时) |
| cv::CAP_PROP_EXPOSURE | :heavy_check_mark: | 相机曝光设置(支持时) |
| cv::CAP_PROP_CONVERT_RGB | :heavy_check_mark: | 如果非零, 捕获的图像被转化为具有三个通道 |
| cv::CAP_PROP_WHITE_BALANCe | :heavy_check_mark: | 相机的白平衡设置(支持时) |
| cv::CAP_PROP_RECTIFICATION | :heavy_check_mark: | 立体相机整流标志 |
这里的所有参数都以double类型返回
例子: 获取视频宽高:
cv::VideoCapture cap("../../resource/video/test_for_car.mp4");
double videoWidth = cap.get(cv::CAP_PROP_FRAME_WIDTH);
double videoHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
cv::VideoWriter
用于将视频写入磁盘
构造方法
//默认构造函数
cv::VideoWriter::VideoWriter
//参数构造函数
cv::VideoWriter::VideoWriter(
const string& filename, //input filename
int fourcc, //编码
double fps, //帧率
cv::SIze frame_size,
bool is_color = ture
);
也可以使用open方法对其初始化的对象进行设置:
cv::ViedoWriter out;
out.open(
"my_video.mpg",
CV_FOURCC('D', 'I', 'V', 'X'),
30.0,
cv::Size(640, 480)
);
可是使用isOpened函数对VideoWriter对象进行查询其是否已经完全就绪, 若就绪, 返回true, 否则返回false
wirte()
确定号VideoWriter对象后可以同多wirte对其进行写入:
cv::VideoWrite::write(
const Mat& image //要写入的数据帧
);
operator<<()
也可以像cout一样使用<<()向视频写入图像数据:
myVideoWriter << myFrame;
数据存储
cv::FileStorage
用于将不同数据类型的和数据以YAML或XML格式写入磁盘或者从磁盘读取, 这些方法可以用来加载或保存任何OpenCV的数值变量(包括基本数据变量)到一个文件中.
构造方法
FileSorage::FileStorage();
FileStorage;:FileStorage(
string fileName,
int flag
);
写入
默认构造函数创建对象后, 可以通过open函数进行打开XML或YAML格式的文件.
flag参数可以为cv::FileStorage::WRITE或cv::FileStorage::APPEND
FileStorage::open( string fileName, int flag );
打开文件后可以通过**cv::FileStorage::operator<<()**进行写入操作
在cv::FileStorage内部数据的存储主要有两种形式, "mapping" (健/值对)和 "sequence" (一系列为命名的条目)
在最顶层, 所写入的数据都在一个mapping中, 在该mapping中, 你可以放置其他的mapping或者sequences, 甚至在mapping中继续放入mapping等.
myFileStorage << "someInteger" << 27; //存储一个整数
myFileStorage << "anArray" << cv::Mat::eye( 3, 3, CV_32F ); //存储一个矩阵
可以使用 { 创建mapping, 使用 [ 创建sequences.
创建maping:
myFileStorage << "theCat" << "{";
myFileStorage << "fur" << "gray" << "eyes" << "green" << "weightLbs" << 16;
myFileStorage << "}";
创建一个mapping后, 需要按照顺序输入条目名以及对应的值. 如果创建的是sequence, 只需要一个接一个输入元素即可, 知道sequence结束.
myFileStorage << "theTeam" << "[";
myFileStorage << "eddie" << "tom" << "scott";
myFileStorage << "]";
一旦完成工作, 可以使用**cv::FileStorage::release()**关闭该文件.
读取
cv::FileStorage对象在打开之后既可以用于写入, 也可以用作读取. 唯一的区别是参数flag的值为cv::FileStorage::READ. 和写入时一样, 也可以用默认构造函数和创建一个FileStorage对象, 之后再通过**cv::FileStorage::open*()**函数打开.
八. 平台
鼠标
OpenCV通过回调函数使用鼠标,以下为定义回调函数必须匹配的协议:
void your_mouse_callback(
int event, //事件类型
int x, //鼠标事件的X位置
int y, //y
int flags, //鼠标事件的更多细节
void *param //Parameters from cv::setMouseCallback()
);
第一个参数表:
| 事件 | 数值 |
|---|---|
| cv::EVENT_MOUSEMOVE | 0 |
| cv::EVENT_LBUTTONDOWN | 1 |
| cv::EVENT_RBUTTONDOWN | 2 |
| cv::EVENT_MBUTTONDOWN | 3 |
| cv::EVENT_LBUTTONUP | 4 |
| cv::EVENT_RBUTTONUP | 5 |
| cv::EVENT_MBUTTONUP | 6 |
| cv::EVENT_LBUTTONDBLCLK | 7 |
| cv::EVENT_RBUTTONDBLCLK | 8 |
| cv::EVENT_MBUTTONDBCLK | 9 |
第四个标志为鼠标事件标志, 用于鼠标组合键(如: shift+鼠标左键):
| 标志 | 数值 |
|---|---|
| cv::EVENT_FLAG_LBUTTON | 1 |
| cv::EVENT_FLAG_RBUTTON | 2 |
| cv::EVENT_FLAG_MBUTTON | 4 |
| cv::EVENT_FLAG_CTRLKEY | 8 |
| cv::EVENT_FLAG_SHIFTKEY | 16 |
| cv::EVENT_FLAG_ALTKEY | 32 |
最后一个参数是void类型的指针, OpcnCV可以用来传递额外的信息到任何类型的结构.
需要一个函数来注册这个回调函数, 即cv::setMouseCallback(), 它需要三个参数:
void cv::setMouseCallback(
const string& windowName, //应用回调函数的窗口
cv::MouseCallback on_mouse, //要注册的回调函数
void* param = NULL //回调函数的额外参数, 可以给回调函数传递信息
);
滑动条,滚动条和开关
HighGUI中创建一个滑动条的函数如下:
int cv::creatTrackbar(
const string& trackbarname, //滑动条名称
cosnt string& windowName, //窗口名称
int* value, //滑动的条当前位置
int cout, //滑动条总的数量
cv::TrackbarCallback onChange = NULL, //回调函数(可选) (当滑动按钮移动时, 回调函数自动调用)
void* param //额外参数
);
这个回调函数并非必须的. 若无回调函数, 移动滑动钮的唯一响应就是value指向的变量值的变化(可以通过这个值来对值的变化做出响应)
回调函数所满足的形式:
void yourTrackbarCallback(
int pos, //滑动条位置
void* param = NULL //cv::setTrackbarCallback()的参数
);
c::creatTrackbar()的最后一个参数是params, 可以是一个任意类型的指针, 一旦回调执行, 这个参数可以传递给回调函数的params参数, 这样一来, 不创建全局变量也可以处理滑动条事件.
有两个函数可以用来获取和设置滑动条的位置值:
int cv::getTrackbarPos(
const string& trackbarName, //滑动条名称
const string& windowName //窗口名称
);
void cv::setTrackbarPos(
const string& trackbarName,
const string& windowName,
int pos //设置的位置值
);
通过Qt后端工作
文字蒙版
用于在窗口上方显示文字条幅, 其函数API:
int cv::displayOverlay(
const string& name, //显示的窗口名称
const string& text, //显示的文字
int delay //显示的ms时间(0 = 'forever')
);
最后一个参数中, 若delay的值设置为0, 那么蒙版将一直存在或直到你再次调用cv::displayOverlay()覆盖原来的蒙版, 若在delay结束前调用cv::dispalyOverlay(), 则将显示新的蒙版, 且延时重置.
创建按钮
Qt接口提供创建按钮的函数, 包括正常的按钮, 单选按钮和复选框, 所有创建的按钮都位于控制面板上.
所有三种类型的按钮都通过一个方法完成:
int cv::creatButton(
const string& buttonName, //按钮名称
cv::ButtonCallback onChange = NULL, //按钮事件的回调函数(可无)
void* params, //额外的按钮事件参数
int buttonType = cv::PUSH_BUTTON, //按钮类型(PUSH_BUTTON or RADIOBOX or CHECKBOX)
int initalState = 0 //按钮的初始化状态
);
按钮回调函数定义:
void yourButtonCallback(
int state, //按钮状态
void* params //cv::creatButton传递的参数
);
你传递给cv::creatButton()的params指针也会被传给回调函数的params参数.
buttonType可以取值为: cv::PUSH_BUTTON, cv::RADIOBOX或者cv::CHECKBOX:
第一个为标准按钮, 当你点击按钮便会调用回调函数
第二个为复选框, 按钮参数根据有没有选择复选框设置为1或者0
第三个为单选按钮, 区别在于点击按钮同时调用当前点击按钮的回调函数和当前没有被点击的按钮(互斥状态).
文本和文字
使用Qt接口写文本必须首先创建一个CvFont对象, 任何时候想在屏幕中添加文本都可以使用这个对象, 字体通过函数cv::fontQt()创建:
CvFont fontQt(
const string& fontName, //字体名称
int pointSize, //字体大小
cv::Scalar color = cv::Scalar::all(0), //BGR颜色
int weight = cv::FONT_NORMAL, //字体宽度(粗细), 1-100(Table 9-3)
int spacing = 0 //字体间距
);
Qt字体粗细的预定以名称及与其关联的值:
| Camere capture constant | 数值 |
|---|---|
| cv::FONT_LIGHT | 25 |
| cv::FONT_NORMAL | 50 |
| cv::FONT_DEMIBOLD | 63 |
| cv::FONT_BOLD | 75 |
| cv::FONT_BLACK | 87 |
最后一个参数spacing控制字符间距, 值正负都可以
放置文本函数:
void cv::addText(
cv::Mat& image, //要写入的图片
const string& text, //要写入的文本
cv::Point location, //左下角定位的文本位置
CvFont* font //OpenCV字体的结构体
);
文本的位置为首字符左下角的位置(文本基线的起点)
设置和获取窗口属性
大部分基于Qt后端创建的窗口状态属性都是可以获取的, 并且大部分都能够在创建窗口后更改:
void cv::setWindowProperty(
const string& name, //窗口名称
int prop_id, //窗口属性ID
double prop_value //设置的属性值
);
double cv::getWindowProperty(
const string& name, //窗口名称
int prop_id //窗口的属性ID
);
获取属性通过提供窗口名和属性ID(prop_id)就可以, 这是属性需要额外提供属性表:
| 属性名称 | 描述 |
|---|---|
| cv::WIND_PROP_FULL_SIZE | 设置为cv::WINDOW_FULLSCREEN可以将窗口显示为全屏 设置为cv::WINDOW_NORMAL窗口显示为正常尺寸 |
| cv::WIND_PROP_AUTOSIZE | 设置为cv::WINDOW_AUTOSIZE窗口大小自动调整为图像大小 设置为cv::WINDOW_NORMAL显示图像大小调整为窗口大小 |
| cv::WIND_PROP_ASPECTRATIO | 设置为cv::WINDOW_FREERATIO可以使窗口有任意长宽比(用户可调整) 设置为cv::WINDOW_KEEPRATIO只能按照固定长宽比调整窗口大小 |
保存和恢复窗口的状态
Qt接口可以保存和恢复窗口的状态:
void cv::savaWindowParameters(
const string& name //窗口名
);
void cv::loadWindowParameters(
const string& name
);
保存窗口时使用cv::saveWindowParameters即可, 恢复窗口通过cv::loadWIndowParameters()即可.
即使你退出或者重启程序, 属性加载命令依然能够正常工作.
和OpenGL交互
回调函数需要满足的定义:
void yourOpenGLCallback(
void* params //从cv::createOpenGLCallback()传过来的参数
);
可以通过cv::createOpenGLCallback()配置回调函数:
void cv::createOpenGLCallback(
const string& windowName, //窗口名称
cv::OpenGLCallback callback, //OpenGL的回调函数
void* params = NULL //传给回调函数的参数
);
参数可以在调用回调函数时给函数以额外的参数.
九. 滤波与卷积
边界外推和边界处理
自定义边框
在对图像进行卷积、核操作或想要为图像添加边框时可以对图像增加边框。
cv::copyMakeBorder()
void cv::copyMakeBorder(
cv::InputArray src, //输入图像
cv::OutputArray dst, //输出图像
int top,
int bottom,
int left,
int right,
int borderType, //像素外推方法
const cv::Scalar& value = cv::Scalar()
);
borderType:
- cv::BORDER_CONSTANT 为每个边框像素赋予一个相同的值*
- cv::BORDER_WRAP 为每个像素分配一个距离,距离是像素点到源图像边缘的距离,然后将距离对边相同的像素的值赋予对应的边框像素*
- cv::BORDER_REPLICATE 将源图像以外的像素全部赋值为图像边缘像素的值
- cv::BORDER_REFLECT 为边框内每个像素复制了源图像内距离同一边界n+1对应的像素点的值(镜像复制拓展边界)
- cv::BORDER_REFLECT_101与cv::BORDER_REFLECT效果类似,通常是OpenCV的默认选项(镜像复制拓展边界,边界像素除外)
自定义外推
阈值化操作
cv::threshold函数
double cv::threshold(
cv::InputArray src,
cv::OutputArray dst,
double thresh, //阈值值
double maxValue, //最大值
int thresholdType //阈值处理类型
);
每种阈值化操作类型对应于第i个源像素(srci) 和阈值thresh之间的比较运算方式, 根据源像素和阈值之间的关系, 目标像素dsti可以被赋值为0, srci或给定的最大值maxValue.
cv::threshold()中thresholdType的可选项:
| 阈值类型 | 操作 |
|---|---|
| cv::THRESH_BINARY | DST |
| cv::THRESH_BINARY_INV | DST |
| cv::THRESH_TRUNC | DST |
| cv::THRESH_T0ZERO | DST |
| cv::THRESH_T0ZERO_INV | DST |
Otus算法
函数cv::threshold()也可以自动决定最优的阈值, 只需要对参数thresh传递值cv::THRESH_OTUS即可.
Otus算法遍历所有可能的阈值, 然后对每个阈值结果的两类像素计算方差(即低于阈值和高于阈值的两类像素).Otus算法计算方差使下列表达式最小:
式中的和是根据两类像素的数量计算而来的权值, 和表示两类像素的方差. 实际上, 这种方法不是一个相对高效的过程.
自适应阈值
阈值可以在整个过程中自动产生变化:
void cv::adaptiveThreshold(
cv::InputArray src, //输入图像
cv::OutputArray ds, //输出图像
double maxValue, //最大值
int adaptiveMethod, //方法或者高斯分布
int thresholdType, //阈值类型
int blockSize, //块的大小
double C //常数
);
根据adaptiveMethod的设置, 可以使用两种不同的自适应阈值方法. 两种方法都是逐个像素地计算自适应阈值T(x, y), 方法是通过计算每个像素位置周围的b x b区域的加权平均值然后减去常数C, 其中b由blockSize给定.
当图像中出现较大的明暗差异时, 自适应阈值非常有效. 这个函数仅醋栗单通道8位或浮点型图像, 并且要求源图像和目标图像不同.
平滑
平滑也成为"模糊", 平滑图像的目的有很多, 但通常都是为了减少噪声和伪影. 在降低分辨率的时候, 平滑也十分重要.
简单模糊和方框型滤波器
void cv::blur(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize, //核大小
cv::Point anchor = cv::Point(-1, -1), //Location of anchor point
int borderType = cv::BORDER_DEFAULT //border extrapolation to use(边界插值)
);
图像中的每个值都是源图像中相应位置一个窗口(核)中像素的平均值(窗口尺寸通过ksize声明),
anchor指定计算时核与源图像的对齐方式, 默认情况下anchor位cv::Point(-1, -1), 表示核相对于滤波器剧中.
简单模糊是方框型滤波器(Box Filter)的一种特殊形式. 方框型滤波器中所有值全部相等(为1或者1/A, 其中A为滤波器的面积).
void cv::boxFilter(
cv::InputArray src,
cv::OutputArray dst,
int ddepth, //输出深度(e.g. CV_8U)
cv::Size ksize,
cv::Point anchor = cv::Point(-1, -1),
bool normalize = true, //ture: divide by box area
int borderType = cv::BORDER_DEFAULt
);
变量ddepth的值设为-1则目标图像的深度与源图像保持一致, 否则可以设置为其他任何一种常用别名.
中值滤波器
将每个像素围绕这个像素的矩形邻域内的中值或者"中值"像素(相对于平均像素).
少量具有较大偏差的点也会严重影响到均值滤波, 中值滤波可以采用取中间点的方式来消除异常值.
void cv::medianBlut(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize
);
高斯滤波器
void cv::GaussianBlur(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize,
double sigmaX, //Gaussian half-width in x-direction
double sigmaY = 0.0,
int borderType = cv::BORDER_DEFAULT //border extrapllation to use
);
参数sigmaX表示高斯核在x方向上的sigma值(最大值的半宽); 第四个参数表示y方向上的sigam值.
若两者都设为0, 则高斯参数根据以下公式确定:
双边滤波器
void cv::bilateraFilter(
cv::InputArray src,
cv::OutputArray dst,
int d, //像素领域大小(最大距离)
double sigmaColor, //Width param for color weight function
double sigmaSpace, //width param for spatial weight function
int borderType = cv::BORDER_DEFAULT
);
第三个参数是像素邻域的直径d, 第四个参数是颜色空间滤波器的sigma值sigmaColor, 第五个参数是坐标空间中滤波器的sigma值sigmaSpace. 第三个参数越大, 平滑时所包括的强度(色彩)越大(因此图像的不连续性将会更显著).
双边滤波器是一种比较大的图像分析算子, 也就是边缘保持平滑.
tips:
- 滤波器的大小d对算法的效率有很大影响, 通常在视频处理时不大于5, 但在非实时应用是这个值可以放大到9, 你也可以在调用这个函数时将其设为-1, 函数将自动为图像计算sigmaSpcae变量的值.
- 实际情况中, 小的sigmaSpace值比如10会带来一个轻微的但也明显的效果; 而大的sigmaSpace值比如150会对图像产生非常显著的影响, 使图像有一种卡通的效果.
- 高斯模糊的过程是减缓像素在空间上的变化, 因此与邻域的关系紧密, 高斯平滑很好地减弱了噪声并且保留了小信号, 但破坏了边缘信息, 最终是高斯模糊把边缘也模糊了.
- 可以把双边滤波当作是高斯平滑, 只是相似程度更高的像素权值更高, 边缘更明显, 对比度更高. 双边滤波的效果就是将源图像变成一幅水彩画, 这种效果在多次迭代后效果显著, 因此这种方法在图像分割领域十分有用.
导数和梯度
索贝尔导数
void cv::Sobel(
cv::InputArray src,
cv::OutputArray dst,
int ddepth, //输出的图像深度(e.g. CV_8U)
int xorder, //x中相应导数的阶
int yorder, //y中相应导数的阶
cv::Size ksize = 3
);
xorder和yorder是求导顺序, 其取值范围为0, 1和2. 0表示在这个方向上不进行求导(xorder和yorder不能同时取0).
Scharr滤波器
拉普拉斯变换
图像形态学
膨胀和腐蚀
最基础的形态学变换是膨胀和腐蚀, 常应用于: 消除噪声, 元素分割和连接等. 基于这两种操作, 可以实现更复杂的形态学操作, 用来定位强度峰值或孔洞, 另一种形式的图像梯度等.
膨胀是一种卷积操作, 它将目标像素的值替换为卷积核覆盖区域的局部最大值. 膨胀的作用是使填充区域生长.
与膨胀相反, 腐蚀操作计算的是核覆盖范围内的局部最小值.
tips:
图像的形态学操作通常在阈值化操作后的布尔图像上进行.
总的来说, 膨胀扩张了明亮区域, 腐蚀缩减了明亮区域. 另外, 膨胀填充凹面, 腐蚀消除突起.
//腐蚀
void cv::erode(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element, //Structuring, a cv::Mat()
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1, //迭代次数
int borderType = cv::BORDER_CONSTANT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
//膨胀
void cv::dilate(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = cv::BORDER_CONSTAN,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
腐蚀和膨胀支持原地调用(源图像和目标图像是同一副图像).
第三个元素是核, 可以向它传递一个未被初始化的cv::Mat().


通用形态学操作
void cv::morphologEx(
cv::InputArray src,
cv::OutputArray dst,
int op,
cv::InputArray element,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = cv::BORDER_DEFUALT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
参数op:
| 操作值 | 形态学操作 | 是否需要临时图像 |
|---|---|---|
| cv::MOP_OPEN | 开操作 | 否 |
| cv::MOP_CLOSE | 闭操作 | 否 |
| cv::MOP_GRADIENT | 形态学梯度 | 总是需要 |
| cv::MOP_TOPHAT | 顶帽操作 | 就地调用需要(src = dst) |
| cv::MOP_BLACKHAT | 底帽操作 | 就地调用需要(src = dst) |
开操作和闭操作
- 开操作先将图像腐蚀,然后对腐蚀的结果膨胀(常用于对二值图像中的区域进行计数)(放大裂缝和局部小洞)
- 闭操作先将图像进行膨胀, 然后对膨胀的结果进行腐蚀(常用于减少无用或噪声驱动的片段)
形态学梯度
顶帽和黑帽
分别用于显示与其邻域相比更亮或更暗的部分.
顶帽操作用源图像减去对其开操作后的图像
黑帽操作用闭操作后的图像减去源图像
自定义核
自定义形态学核的函数:
cv::Mat cv::getStructuringElement(
int shapce, //Element shape
cv::Size ksize, //size fo structuring element(odd num!)
cv::Point anchor = cv::Point(-1, -1) //默认锚点在元素中心
);
第一个参数控制构造元素的基本形状, 第二第三个参数确定元素的大小和锚点位置.
cv::getStructuringElement()的元素形状:
| 形状值 | 元素 | 描述 |
|---|---|---|
| cv::MORPH_RECT | 矩形 | |
| cv::MORPH_ELLIPSE | 椭圆形 | 以ksize, width和ksize,height为 两个半径做椭圆 |
| cv::MORPH_CROSS | 交叉 | 当或 |
用线性滤波器做卷积
用cv::filter2D()做卷积
通过cv::sepFilter2D使用可分核
生成卷积核
十. 常见的图像变换
拉伸,收缩,扭曲和旋转
均匀调整
void cv::resize(
cv::InputArray src,
cv::OutputArray dst,
cv::Size dsize,
double fx = 0,
double fy = 0,
int interpolation = CV::INTER-LINEAR //(插值) 插值方法
);
有两种方式指定输出图像的大小:
- 使用绝对尺寸, dsize参数直接设置为我们想要的结果图像dst的大小
- 使用相对尺寸, dsize设置为cv::Size(0,0), 并分别将fx和fy设置为我们要应用于x轴和y轴的比例因子
插值参数:
| 插值 | 含义 |
|---|---|
| cv::INTER_NEAREST | 最近邻插值 |
| cv::INTER_LINEAR | 双线性插值 |
| cv::INTER_AREA | 像素区域重采样 |
| cv::INTER_CUBIC | 双三次插值 |
| cv::INTER_LANCZ0S4 | 插值(超过8x8个邻域) |
图像金字塔
cv::pyrDown()
void cv::pyrDown(
cv:InputArray src,
cv::OutputArray dst,
const cv::Size& dstsize = cv::Size() //输出图像大小
);
若第三个参数dstsize设置为默认值cv::Size(), 则输出图像为为原图的1/4, 具体为:
( (src.cols+1)/2), (src.rows+1)/2 )
cv::buildPyramid()
构建一系列的新图像, 每个都从其前身缩减.
void cv::buildPyramid(
cv::InputArray src,
cv::OutputArrayOfArrays dst,
int maxlevel //金字塔等级
);
参数dst为cv::OutputArrayOfArray, 可以视为只是一个STL向量<>或cv::OutputArray类型的对象. 最常见的例子是< cv::Mat >向量.
cv::pyrUp()
放大图像:
void cv::pyrUp(
cv::InputArray src,
cv::OutputArray dst,
const cv::Size& dstsize = cv::Size()
);
类似于cv::pyrDown(), 如果dstsize设置为默认值cv::Size(), 结果图像将与src的大小(每个维度)的两倍大小相同.
拉普拉斯金字塔
不均匀映射
拉伸, 收缩, 扭曲, 旋转 --> 几何变换
仿射变换
十一.图像分析
Canny边缘检测
cv::Canny()
在Canny算法中, 先在x和y方向上求得一阶导, 然后将它们组合成四个方向的导数. 其中方向导数是局部最大值的点是组成边缘的候选项. Canny算法最明显的创新点就是将单个的边缘候选像素加入轮廓.
void cv::Canny(
cv::InputArray image,
cv::OutputArray edges,
double threshold1,
double threshold2,
int apertureSize = 3,
bool L2gradient = false
);
- 第一个参数是输入图像, 必须是单通道图像, 输出图像是灰度图(实际上是布尔图)
- 接下来的两个参数是低阈值和高阈值
- apertureSize是cv::Canny()内部调用的Sobel导数算子所用的aperture的大小
- 最后的一个参数L2gradient用于选择是使用适当的L
2范数"正确"计算方向梯度
Hough变换
Hough变换是一种用于检测线, 圆或者图像中其他简单形状的方法.
Hough线变换
OpenCV支持三种Hough线变换: 标准Hough变换(SHT), 多尺度Hough变换(MHT)和渐进概率Hough变换(PPHT).
cv::HoughLines():标准和多尺度Hough变换
都由函数cv::HoughLines()实现, 两种不同的用法可由两个可选的参数区分.
void cv::HoughLines(
cv::InputArray image,
cv::OutputArray lines,
double rho,
double theta,
int threshold,
double srn = 0,
double stn = 0
);
- 第一个参数是输入图像, 必须是8位图像, 被当做二值图
- 第二个参数是存放结果位置, 是一个Nx1的双通道浮点型数组(N为返回的直线数目)
- 两个通道包含每条线的rho()和theta()值, 用于设置直线所需的分辨率, rho的单位是像素, 而theta的单位是弧度, 累加平面可以考虑成一个由弧度大小为像素的单元组成的二维直方图. (例如theta可设置为CV_PI/180)
- threshold是累加平面中算法用于判断线条属于一条直线的阈值.
- 参数srn和stn用于控制称为"多尺度Hough变换"(MHT)的SHT算法的拓展, 将这些参数设置为0时, 函数将采用SHT算法
cv::HoughLinesP(): 渐进概率Hough变换
void cv::HoughLinesP(
cv::InputArray image,
cv::OutputArray lines,
double rho,
double theta,
int threshold,
double minLineLength = 0,
double maxLineGap = 0
);
与HoughLines的区别在于:
- lines参数变为四通道(或者一个Vec4i类型的向量). 四通道分别是找出线段两个端点的坐标和.
- 第二个重要的区别是两个参数的含义, PPHT中, minLineLength和maxLineLengGap参数设置了返回线段的最小长度, 以及共线线段之间的最小间隔, 防止算法把他们连接成一条.
Hough圆变换
void cv::HoughCircles(
cv::InputArray image,
cv::OutputArray circles,
int method,
double dp,
double minDist,
double param1 = 100,
double param2 = 100,
int minRadius = 0,
int maxRadius = 0
);
- 输入图像是一幅8位图像, 不要求是二值图(其内部使用的cv::Sobel())
- 数组Circles是一个矩阵数组或向量, 取决于传入cv::HoughCircles()的内容, 如果是个矩阵, 它将是CV::F32C3类型的一维数组, 三个通道分别是圆的坐标及半径. 如果是个向量, 那么它的类型是vector< Vec3f >
- method参数的值实际设置为cv::HOUGHGRADIENT
- 参数dp是所使用的累加图像的分辨率, 如果dp设置为1, 那么分辨率不发生变化, 如果设置为一个较大的数比如2, 累加器的分辨率会减少一半, dp的值不能小于1
- 参数minDist是两个圆之间必须存在的最小距离
- param1和param2分别是边阈值和累加器阈值, Canny边检测器需要两个阈值
- 最后连个参数是可以找到的圆的最小和最大半径, 这意味着它们是累加器所能表示的圆的半径
分割
漫水填充
漫水填充通常用于标记或隔离图像中的部分以做进一步的处理和分析,也可以从输入图像中导出可以由后续例程使用的掩膜。函数cv::floodFill()提供了一个可供选参数mask用于控制填充区域,例如对同一个图像的多个填充。
算法会线选取一个种子点,然后将所有与它相似的点的包括本身染上一种特定的颜色,区别是相邻像素不一定全部上同一种颜色,漫水填充的结果是一个单连通区域,如果像素灰度值与任一当前像素差异在指定范围(loDiff-upDiff)内火灾指定原始种子像素灰度值的指定范围内,cv::floodFill()函数都将对它进行上色. 漫水填充也可以通过可选的掩膜参数进行约束.
int cv::floodFill(
cv::InputOutputArray image,
cv::Point seed,
cv::Scalar newVal, //value for painted pixels
cv::Rect* rect, //output bounds painted domain
cv::Scalar lowDiff = cv::Scalar(), //maximum down color distance
cv::Scalar highDiff = cv::Scalar(), //maximum up color distance
int flags //local/global, and mask-only
);
int cv::floodFill(
cv::InputOutputArray image,
cv::InputOutputArray mask,
cv::Point seed,
cv::Scalar newVal,
cv::Rect* rect,
cv::Scalar lowDiff = cv::Scalar(),
cv::Scalar highDiff = cv::Scalar(),
int flags
);
- image是输入图像,可以是8位或浮点型图像,通道必须是1或3. 该函数会修改image
-
- mask参数表示一个可以同时用作cv::floodFill()的输入掩膜(这种情况下会限制可以被填充的区域),并且可以作为cv::floodFill()的输出。mask必须是单通道8位图像,大小比原图像宽高分别大2像素
- mask作为cv::floodFill()的输入时,算法不会填充掩膜中非零区域,因此,如果不想屏蔽掩膜操作,应该在使用前将其置零。
- mask存在时,也将用作输出,当算法运行后,掩膜中每个“填充”像素将被设置为掩膜中的非零值。你可以向flags添加cv::FLOODFILL_MASK_ONLY(通过布尔OR)执行此选项, 这样, 输入图像就不会被修改, 相反, 只有掩膜会被修改.
- 漫水填充从种子seed开始
- 所有符合符合条件的值会被设为newVal颜色
- 如果一个像素的强度不小于一个着色相邻像素的强度减去lowDiff,并且不大于着色像素的强度加上upDiff,则该像素将被着色。
-
- 如果flags参数包含了cv::FLOODFILL_FIXE_RANGE,那么比较时将会选用种子点而不是相邻像素。通常flags参数控制填充的连接性。填充相对于何处填充,是否只填充一个掩膜区域,以及填充掩膜的值。
- flags还有cv::FLOODFILL_MASK_NOLY, 出了这些, 你还可以为它加数值4或8. 这样, 你就声明了连通方式是4连通还是8连通. 前一种情况下, 4连通数组指的是
分水岭算法
Mean-Shift分割算法
void cv::pyrMeanShiftFiltering(
cv::InputArray src,
cv::OuputArray dst,
cv::double sp,
cv::double sr,
int maxLevel = 1,
cv::TermCriteria termcrit = TermCriteria(
cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
5,
1
)
);
十四. 轮廓
轮廓查找
轮廓具有层次结构
cv::findContours()
void cv::findContours(
cv::InputOutputArray image,
cv::OutputArrayOfArrays contours, //vector<vector<Point>>
cv::OutputArray hierarchy,
int mode,
int method,
cv::Point offset = cv::Point()
);
void cv::findContours(
cv::InputOutputArray image,
cv::OutputArrayOfArrays contours,
int mode,
int method,
cv::Point offset = cv::Point()
);
第一个参数是输入图像, 图像必须是8位单通道图像, 并且应该被转化为二值的. 该图像在运行时会被直接涂改. 第二个参数是Vector数组. 第三个参数hierarchy, 如果给定参数则hierarchy将输出所有轮廓的树结构, 输出仍然为数组, 数组中的每个值都是四元数组.
参数mode使用OpenCV期望的轮廓提取方式, 有4中取值:
-
cv::RETR_EXTERNAL
只检索最外层轮廓
-
cv::RETR_LIST
检索所有轮廓并保存到表(List)中, 通过hierarchy[i][0进行连接
-
cv::RETR_CCOMP
检索所有的轮廓, 并将它们组织成双层结构, 顶层边界是所有成分的外部边界, 地二层边界是孔的边界.
-
cv::RETR_TREE
检索所有轮廓并重新建立网状轮廓结构
以下的值和method有关:
-
cv::CHAIN_APPROX_NONE
将轮廓编码中的所有点转换为点
-
cv::CHAIN_APPROX_SIMPLE
压缩水平, 垂直, 斜的部分, 只保留最后一个点, 在许多特殊情况下, 这一操作将大大减少返回的点数
-
cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC90_KCOS
使用Teh-Chin链逼近算法中一个
最后一个参数是offset, 返回的轮廓中所有点都会根据参数值发生偏移.
绘制轮廓
void cv::drawContours(
cv::InputOutputArray image,
cv::InputArrayOfArrays contours,
int contourIdx,
const cv::Scalar& color,
int thickness = 1,
int lineType = 8,
cv::InputArray hierarchy = noArray(),
int maxLevel = INT_MAX,
cv::Point offset = cv::Point()
);
第一个参数为待绘制轮廓的图像, 第二个参数contours是要绘制的轮廓, 与findContours的输出contours相同. 第三个参数contourIdx用于告诉cv::drawContours()需要绘制的是contoures参数中的某一条轮廓还是全部轮廓. 假如contourIdx是一个正数, 则对应的轮廓被绘制, 假如contourIdx为负数(-1), 则所有的轮廓将被绘制.
参数color为四元组cv::Scalar, 参数thickness是一个整数, 表示绘制的线的粗细, 参数lineType可以是4和8, 代表绘制的线将是四联通(不美观), 八连通(较美观)或是cv::AA线(美观).
参数hierarchy对应cv::findContours()函数输出的层次, 其与参数maxLevel共同起作用, maxLevel限制将在图上绘制的轮廓层次深度, 将maxLevel设为0表示只绘制"第0层",(最高层次) 的轮廓. 设为非0正数, 则表示绘制最高层一下相同数量层级的轮廓. 这对轮廓树很有用, 同样, 当你希望在连接成分(cv::RETR_CCOMP)时希望只显示最外层轮廓(而不显示内部孔轮廓)时, 这也很用帮助.
offset是绘制时的偏移.
轮廓几何
多边形逼近
几何及其特性
获取轮廓长度
double cv::arcLength(
cv::InputArray points,
bool closed //轮廓是否闭合
);
获取矩形包围框
cv::Rect cv::boundingRect(
cv::InputArray points
);
获得最小矩形框
cv::RotatedRect cv::minAreaRect(
cv::InputArray points
);
拟合
直线拟合
void cv::fitLine(
cv::InputArray points,
cv::OutputArray line,
int distType,
double param,
double reps,
double aeps
);
-
参数Points表示了一组cv::Mat数组或标准模板库向量形式的点集.
-
cv::fitLine()可以处理二维点, 也可以处理三维点, 不同之处体现在line上, cv::Vec4f(二维直线), cv::Vec6f(三维直线).
-
第三个参数distType允许我们选择所用的距离度量.
-
param用于为其中一些距离度量提供参数值, 设置为0cv::fitLine()函数将自动为选中的距离度量提供最优值.
-
参数reps和aeps代表你对拟合直线的原点精度要求和角度精度要求, 常用的值为1e-2
十六. 关键点和描述子
关键点和跟踪基础
角点检测
cv::goodFeaturesToTrack()
cv::goodFeaturesToTrack()使用了哈尔的方法,并在性能上略有改善,该函数便于计算必要的导数运算符,并对其进行分析,返回符合我们定义的适合跟踪的点的列表:
void cv::goodFeaturesToTrack(
cv::InputArray image,
cv::OutputArray corners,
int maxCorners,
double qualityLevel,
double minDistance,
cv::InputArray mask = noArray()
int blockSize = 3,
bool useHarrisDetector = false,
double k = 0.04
);
- 输入图像可以是任意8位或32位(即8U和32F)的单通道图像.
- 输出角点将是包含所有到角点的向量或数组(取决你提供的内容), 如果它是vector<>形式的, 它应该是cv::Point2f对象的vector, 如果它是一个cv::Mat, 它的一行将是角点, 两列分别是点的x和y的位置.
- 你可以使用maxCorners限制找到的角点数量, 用quality Level来设置点的(通常介于0.10和0.01之间, 绝对不超过1.0)的返回质量以及用minDistance设置相邻角之间的最小间距.
- 如果要设置mask参数, 必须与图像的尺寸相同, 角点将不会在mask为0的地方生成
- blockSize参数表示计算角点时需要考虑的区域大小, 默认值为3, 单对于高分辨率图像, 你可能想让它再稍微大一点
- useHarrisDistance参数(如果设置为true)将设定cv::goodFeaturesTotrack()使用哈尔原始算法的精确角点强度公式, 如果设置为false, 将使用Shi和Tomasi的方法.
- 参数k只能用于哈尔算法, 并且最好保留其默认值.
这是一个测试
test
test