文章链接:https://blog.csdn.net/q_z_r_s
机器感知 一个专注于SLAM、三维重建、机器视觉等相关技术文章分享的公众号
|
第1章 图像编程入门
1.1 简介
1.2 安装OpenCV库
1.3 装载、显示和存储图像
- - imread——读图像
- - namedWindow——定义窗口
- - imshow——显示图像
- - flip——翻转图像
- - waitKey——等待按键
- - imwrite——存储图像
- - setMouseCallback——响应鼠标在图像上的点击操作
- - circle、ellipse、line、rectangle——在图像上绘画对应形状
- - putText——在图像上写入文本
1.4 深入了解cv::Mat
- - Mat::create——重新分配一个新图像
- - Mat::copyTo——把图像复制给另外一个图像
- - Mat::convertTo——转换图像数据类型
1.5 定义兴趣区域
cv::Mat imageROI(image,
cv::Rect(image.cols-logo.cols, //ROI坐标
image.rows-logo.rows,
logo.cols,logo.rows)); //ROI大小
logo.copyTO(imageROI); //插入标志
- - 所谓ROI其实就是Mat类的一种构造函数产生的对原图像内存的新的引用,使用Rect来定义ROI区域时,前两个参数为ROI区域的左上角坐标,后两个参数表示ROI的行列大小。
- - 方法二:
cv::Mat imageROI(image,
cv::Range(image.rows-logo.rows, logo.rows) //ROI行范围
cv::Range(image.rows-logo.rows, logo.rows)); //ROI列范围
logo.copyTO(imageROI); //插入标志
- - 第二种方法是给出行列坐标的起始和终止范围
- - 图像掩码:: OpenCV中有些操作可以定义掩码,掩码必须为8位图像,如果掩码中某个位置的值不为0,则在这个位置上的操作就会起作用;如果掩码中的某些像素位置的值为0,那么对图像中的相应位置的操作将不起作用;OpenCV中大多数基于像素的操作都可以使用掩码。
第2章 操作像素
2.1 简介
2.2 访问像素值
- - Mat::at(row,col)\[n]——对8-bit图像而言,单通道图像时,T=uchar,\[n]不需要;当为三通道时,T=cv::Vec3b,\[n]可以是0、1、2。
2.3 用指针扫描图像
- - Mat::ptr(row)——T为图像数据类型,此方法是把图像看成三维立体的来访问,row对应的不是单纯的行,更准确的说是三维立体的层。
- - 低层次指针算法访问行:
uchar *data = image.data;
data += image.step; //从一行移到下一行
2.4 用迭代器扫描图像
- - cv::MatIterator_ it
- - cv::Mat_::iterator it
cv::Mat_::iterator it_begin =
image.begin(); //获得起始位置
cv::Mat_::iterator it_end =
image.end(); //获得结束位置
2.5 编写高效的图像扫描循环
- - cv::getTickCount——返回从最近一次电脑开机到当前的时钟周期数
- - cv::getTickFrequency——返回每秒的时钟周期数,获得某个函数(或代码段)的运行时间的方法如下:
const int64 start = cv::getTickCount();
function(); //调用的函数
double duration = (cv::getTickCount()-start) / //经过的时间(s)
cv::getTickFrequency()
结果表明:`Mat::at(row,col)\[n]方法应该在需要随机访问数据的时候使用`,绝不要在扫描图像时使用,速度比较慢。
2.6 扫描图像并访问相邻像素
- - 主要就是访问某个像素点周围26个(对三通道而言)的方法,使用的是智能指针Mat::ptr(row)来访问不同行,同一行的访问看作是把二维数组连续排放成一维数组来访问就可以了。
- - cv::saturate_cast——函数的作用是确保结果在该数据类型定义的范围之内。
- - cv::filter2D——调用函数时传入图像和内核
void sharpen2D(const cv::Mat& image, cv::Mat& result) {
cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));
kernel.at(1,1) = 5.0;
kernel.at(0,1) = -1.0;
kernel.at(2,1) = -1.0;
kernel.at(1,0) = -1.0;
kernel.at(1,2) = -1.0;
cv::filter2D(image,result,image.depth(),kernel);
}
2.7 实现简单的图像运算
- - add——实现相加功能
- - addWeighted——实现加权相加
// c[i] = a[i] + b[i]
cv::add(imageA, imageB, resultC);
// c[i] = a[i] + k
cv::add(imageA, cv::Scalar(k), resultC);
// c[i] = k1*a[i] + k2*b[i] + k3
cv::addWeighted(imageA, k1, imageB, k2, k3, resultC);
// c[i] = k*a[i] + b[i]
cv::scaleAdd(imageA, k, imageB, resultC);
- - cv::sqrt、pow、abs、exp、log、
- - split——将图像的三通道分别复制到三个不同的cv::Mat实例中
- - merge——用三个单通道图像创建一个彩色图像
2.8 图像重映射
- - remap——映射方程为:dst(x,y)=src(map_x(x,y),map_y(x,y))
第3章
- - absdiff——计算图像的像素与标量值之间差的绝对值
- - 仿函数或函数对象
- 重载operator()方法,调用类的处理方法就像调用一个纯粹额函数,这种类的实例被称为函数对象或仿函数(functor)。
- - cvtColor——转换图像的色彩空间
- 在`搜寻特定颜色`的物体时,HSV色彩空间非常实用,比如肤色检测。
第4章
4.1 简介
4.2 计算图像直方图
- - calcHist——计算图像直方图,支持单通道灰度图,也支持多通道图像
4.3 利用查找表修改图象外观
- - cv::LUT——对8-bit图像,输入的查找表有256个bin,每个bin对应一个新的值,从而实现非线性映射
- 使用此方法可以`伸展直方图`以提高图像对比度:以灰度图为例,先计算出直方图,然后根据设定的最小灰度值像素数量和最大灰度值像素数量,通过遍历直方图256个bin来找到满足条件的最小灰度值和最大灰度值(最小灰度值:先从灰度0开始查找,相应灰度在直方图中对应的像素说不满足>设定的像素数量时,逐步提高灰度值,直到满足条件;最大灰度值:从灰度255开始反向查找,不满足>设定的像素数量时,逐步减小灰度值),下一步就是建立查找表,方法是小于最小灰度值的bin映射为0,大于最大灰度值的bin映射为255,介于最小最大之间的先进行归一化,然后乘以255,均匀的散列在0~255之间,最后就是调用LUT函数实现重映射。
- - cvRound——对float型的数进行四舍五入
- - cvFloor——返回不大于float型参数的最大整数值
- - cvCeil 返回不小于float型参数的最小整数值
4.4 直方图均衡化
4.5 反向投影直方图检测特定图像内容
- - calcBackProject——从归一化后的直方图中读取概率值并把输入图像中的像素替换成与之对应的概率值
- 直方图是图像内容的一个重要特性。 如果图像的某个区域含有特定的纹理或物体,这个区域的直方图就可以看作一个函数,归一化之后,该函数返回某个像素属于这个特殊纹理或物体的概率。
4.6 均值平移算法查找目标
- - TermCriteria——此类定义迭代算法终止准则
- - meanShift——根据反向映射直方图进行均值平移算法查找目标
- 直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。假设我们已经知道图像中某个物体的大致位置,就可以`用概率分布图`找到物体的准确位置。 最可能出现的位置就是窗口中概率最大的位置。因此我们可以从一个初始位置开始,在周围反复移动,就可能找到物体所在的准确位置。这个实现方法称为均值平移算法。均值偏移算法是一个迭代过程,用于定位概率函数的局部最大值。`定位的方法`是寻找预定义窗口内部数据点的重心或加权平均值。然后把窗口移动到重心的位置,并重复该过程, 直到窗口中心收敛到一个稳定的点。
4.7 比较直方图搜索相似图像
- - compareHist——比较两个直方图的相似性
4.8 用积分图像统计像素
- - 自适应的阈值化
- 采用局部阈值,即根据每个像素的领域计算阈值。将每个像素的值与邻域的平均值进行比较。如果某像素的值与它的局部平均值差别很大,就会被当作异常值在阈值化过程中剔除。求均值过程中可以利用积分图加速计算一个领域内的累计值。
- - 用直方图实现视觉跟踪
第5章 用形态学运算变换图像
5.1 简介
5.2 形态学滤波器腐蚀和膨胀图像
- - erode——腐蚀图像,可以去除噪声点
- - dilate——膨胀图像
5.3 用形态学滤波器开启和闭合图像
- - morphologyEx——根据传入参数实现开启或闭合运算
- 闭合的定义是对图像先膨胀后腐蚀。开启的定义是对图像先腐蚀后膨胀。
5.4 用形态学滤波器检测边缘和角点
- - morphologyEx——根据传入参数实现边缘检测
- 利用腐蚀和膨胀分别对图像进行操作,之所以可以检测出边缘的原理是:腐蚀会使边缘的范围变小,而膨胀会使边缘的范围变大,因此两种操作分别得到的图像的差异主要体现在边缘的范围大小上,所以将两种操作后的图像进行absdiff操作就可以就可以得到边缘了。
- - 角点检测
- 用四个不同的结构元素,分别是正方形、菱形、十字形和X形。先十字形元素膨胀,然后再对其进行菱形元素腐蚀;先用X形元素膨胀,再对其进行正方形元素腐蚀;将以上两幅图像absdiff得到角点。
5.5 用分水岭算法实现图像分割
5.6 用MSER算法提取特征区域
5.7 用GrabCut算法提取前景物体
第6章 图像滤波
- - blur——将每个像素的值替换成该像素邻域的平均值(邻域是矩形的),从而使图像更加平滑,这种滤波器也成为块滤波器(box filter)
- - GaussianBlur——将每个像素的值换成该像素领域的像素值进行高斯加权后的平均值
- - pyrDown——先进行低通滤波,然后图像尺寸缩小一半
- - pyrUp——放大图像
- - resize——对图像进行缩放
- - medianBlur——中值滤波器,`非常有助于消除椒盐噪声`
- 中值滤波器把当前像素和它的领域组成一个集合,然后计算出这个集合的中间值,以此作为当前像素的值(集合中数值经过排序,中间位置的数值就是中间值) 。这正是中值滤波器在消除椒盐噪声中如此高效的原因,相反,简单的均值滤波器会在很大程度上受到这种噪声影响。
- - Soble——因为它只对垂直或水平方向的图像频率起作用(具体方向取决于滤波器选用的内核) ,所以它被认为是一种定向滤波器,其原理是计算垂直和水平方向上的梯度。
- - Prewitt算子
- - Roberts算子
- - Scharr算子(相对来说更精确)
- 因为这些定向滤波器都会计算图像函数的一阶导数,因此,对强度变化大的区域,得到较大的响应;较平坦的区域得到较小的值,故称`计算图像导数的滤波器为高通滤波器`。
- - cv::Laplacian——计算图像的拉普拉斯算子
第7章 提取直线、轮廓与区域
7.1 简介
7.2 用Canny算子检测图像轮廓
- - cv::Canny——根据一个低阈值和一个高阈值来判断哪个点属于轮廓
- Canny算法结合高低阈值得到的两种边缘分布图,生成最优的轮廓分布图。具体做法是在`低阈值边缘分布图上只保留具有连续路径的边缘点`,`同时把那些边缘点连接到属于高阈值边缘分布图的边缘上`,`即把低阈值边缘分布图上的连续边缘点融合到高阈值边缘分布图上`,这样一来, `高阈值分布图上的所有边缘点都保留下来`, 而`低阈值分布图上边缘点的孤立链全部被移除`。 这是一种很好的折中方案,只要指定适当的阈值,就能获得高质量的轮廓。这种基于两个阈值获得二值分布图的策略,称为`滞后阈值化`,可用于任何需要用阈值化获得二值分布图的场景。但是它的计算复杂度比较高。
7.3 用霍夫变换检测直线
- - cv::HoughLines——输入二值分布图,即检测到的边缘图像,返回极坐标的参数rho和角度theta
- - cv::HoughLinesP——霍夫变换的改进版,返回所得直线线段的两个端点坐标,其使用了二维累加器来记录某一直线穿过的像素点数,只有数量达到一定值才会被认为是直线
- - cv::HoughCircles——
7.4 点集的直线拟合
- - cv::fitLine——拟合直线
- - cv::fitEllipse——拟合椭圆,它返回一个旋转的矩形(一个cv::RotatedRect实例), 矩形中有一个内切的椭圆。
7.5 提取区域的轮廓
- - cv::findContours——输入二值图想,输出一个存储轮廓的cv::Point类型的向量
- - cv::drawContours——在图像上画出那些区域的轮廓
- - cv::findContours——检测二值图像中所有的闭合轮廓
- - cv::boundingRect——找出矩形边界框
- - cv::minEnclosingCircle——找出最小圆形边界框
- - cv::approxPolyDP——计算区域轮廓的多边形逼近
- - cv::polylines——绘制多边形
- - cv::moments——计算轮廓矩
- - cv::minAreaRect——计算最小覆盖自由矩形
- - cv::contourArea——估算轮廓的面积
- - cv::pointPolygonTest——判断一个点在轮廓内部还是外部
- - cv::matchShapes——度量两个轮廓之间的相似度
第8章 检测兴趣点
8.1 简介
8.2 检测图像中的角点
- - cv::cornerHarris——检测Harris角点
- 其原理是计算兴趣点周围领域内某个方向上强度值的平均强度值变化:$R≈\sum(I(x+u,y+u)-I(x,y))^2$ ,然后在$I(x,y)$ 处进行一阶泰勒展开得到下式:
- $R\approx\sum((I(x,y)+\frac{\partial I}{\partial \text{x}}u+\frac{\partial I}{\partial \text{x}}v-I(x,y)))^2$=$\sum((\frac{\partial I}{\partial x}u)^2+(\frac{\partial I}{\partial y}v)^2+2\frac{\partial I}{\partial x}\frac{\partial I}{\partial y}uv))$
- 写成矩阵的形式,就是:
- $R\approx[u\quad v]\left[\begin{matrix}\sum(\frac{\partial I}{\partial x})^2&\sum\frac{\partial I}{\partial x}\frac{\partial I}{\partial y}\\\sum\frac{\partial I}{\partial x}\frac{\partial I}{\partial y}&\sum(\frac{\partial I}{\partial y})^2\end{matrix}\right]\left[\begin{matrix}u\\v\end{matrix}\right]$
- 然后根据最小特征值与设定阈值的关系判定是否为角点。`Harris角点算法的原始定义不显式的计算特征值`,而是通过计算下式的评分:$Det(C)-kTrace^2(C)$来判定是否为角点。
- - cv::cornerEigenValsAndVecs——计算Harris角点,并求出$R$的特征值和对应的特征向量
- - cv::cornerMinEitenVal——计算Harris角点,只返回$R$的最小特征值
- - cv::goodFeatureToTrack——从Harris值最强的点开始,只允许一定距离之外的点成为兴趣点,解决特征点聚集的问题
// 计算适合跟踪的特征
std::vector corners;
cv::goodFeaturesToTrack(image, // 输入图像
corners, // 角点图像
500, // 返回角点的最大数量
0.01, // 质量等级
10); // 角点之间允许的最短距离
// KeyPoint类型的向量
std::vector keypoints;
// 适合跟踪的特征检测器的构造函数
cv::Ptr gftt=
new cv::GoodFeaturesToTrackDetector(
500, // 返回角点的最大数量
0.01, // 质量等级
10); // 兴趣点之间允许的最短距离
// 用FeatureDetector方法检测兴趣点
gftt->detect(image,keypoints);
8.3 快速检测特征
- - cv::FAST——根据候选特征点周围的像素值判断该点是否为关键点
- 如果存在这样一段圆弧,它的连续长度超过周长的3/4,并且它上面所有像素的强度值都与圆心的强度值明显不同(全部更黑或更亮),那么就认定这是一个关键点。
// 关键点的向量
std::vector keypoints;
// FAST特征检测器的构造函数
cv::Ptr fast=
new cv::FastFeatureDetector(
40); // 检测用的阈值
// 检测特征点
fast->detect(image,keypoints);
cv::FAST(image, // 输入图像
keypoints, // 输出关键点的向量
40, // 阈值
false); // 是否进行非最大值抑制?
- - cv::DynamicAdaptedFeatureDetector——可以指定被检测兴趣点的数量范围
cv::DynamicAdaptedFeatureDetector fastD(
new cv::FastAdjuster(40), // 特征检测器
150, // 特征的最小数量
200, // 特征的最大数量
50); // 最大迭代次数
fastD.detect(image,keypoints); // 检测特征点
- - cv::GridAdaptedFeatureDetector——在图像上定义一个网格,可以设置每个网格中最大特征点数量
cv::GridAdaptedFeatureDetector fastG(
new cv::FastFeatureDetector(10), // 特征检测器
1200, // 关键点总数的最大值
5, // 网格的行数
2); // 网格的列数
fastG.detect(image,keypoints);
- - cv::PyramidAdaptedFeatureDetector——在一个图像金字塔上应用特征检测器,结果被合并到输出的关键点向量中。
cv::PyramidAdaptedFeatureDetector fastP(
new cv::FastFeatureDetector(60), // 特征检测器
3); // 金字塔的层数
fastP.detect(image,keypoints);
8.4 尺度不变特征的检测
// SURF特征检测器的构造函数
cv::Ptr detector =
new cv::SURF(2000.); // 阈值
// 检测SURF特征
detector->detect(image,keypoints);
// 构造SIFT特征检测器对象
detector = new cv::SIFT();
// 检测SIFT特征
detector->detect(image,keypoints);
8.5 多尺度FAST特征的检测
- - BRISK(Binary Robust Invariant Scalable Keypoints) ——先建立图像金字塔,然后每个图像用`FAST`找关键点,然后再用类似SIFT的方法筛选关键点,在空间尺度上进行插值寻找更精确的关键点,因此在不连续的图像尺度上最后却检测到对应尺度连续的关键点
// 构造BRISK特征检测器对象
detector = new cv::BRISK();
// 检测BRISK特征
detector->detect(image,keypoints);
- - ORB(Oriented `FAST` and Rotated `BRIEF`)——先建立图像金字塔,然后使用FAST检索每个图像找出关键点,之后通过计算Harris评分:$Det(C)-kTrace^2(C)$去除一些边缘点,根据计算FAST找到的关键点周围领域内的像素值重心,与关键点连线形成角度,实现旋转不变性
// 构造ORB特征检测器对象
detector = new cv::ORB(200, // 关键点的总数
1.2, // 图层之间的缩放因子
8); // 金字塔的图层数量
// 检测ORB特征
detector->detect(image,keypoints);
第9章
9.1 简介
9.2 局部模板匹配
- - SSD(Sum of Squared Differences)——计算以两个以关键点为中心的正方形区域(即图像块)中对应像素的SSD,满足设定阈值即认为这两个点是匹配的
9.3 描述局部强度值模式
- - SURF、SIFT——计算特征描述子之间差距
- - 提高匹配质量的方法
- - 交叉检查匹配项:假设有特征点集A和B,$A_x$与B集中的$B_y$最匹配,然后检查$B_y$与A集中最匹配的点,如果这两个关键点互为最佳匹配,才认为是一个有效的匹配项
- - 比率检验法:为每个关键点找两个最佳匹配项,如果两个最佳匹配项与关键点的距离比值(即相似度)超过设定比率,则剔除此关键点
- - 匹配插值的阈值化:虽然关键点对应的点是最匹配的,但是匹配差值太大的也是不行的,超过阈值的关键点须剔除
9.4 用二值特征描述关键点
- - BRIEF、BRISK、FREAK、ORB(FAST+BRIEF)