一、图像操作
1、读入图像
Cv2.imread()
2、显示图像
Cv2.imshow()
3、保存图像
Cv2.imwrite()
二、视频操作
1、用摄像头捕获视频
Cap = Cv2.VideoCapture()
2、读取每帧数据
Cap.read()
3、保存视频
创建VideoWriter对象,指定FourCC编码(视频的编码格式)
三、绘图
(1)画线
Cv2.line() 起点+终点
(2)画矩形
Cv2.rectangle() 左上角顶点+右下角顶点
(3)画圆
Cv2.circle() 中心点坐标+半径大小
(4)图片上添加文字
Cv2.putText()
四、图像基础操作
(1)获取图像的属性
Img.shape获取图像形状,返回值是一个包含行数,列数,通道数的元组;如果图像是灰色图,返回值仅有行数和列数。
(2)返回图像的数据类型:
img.dtype
(3)图像ROI(region
of interest),把图像部分拷贝到其他区域
(4)拆分及合并图像通道
有时需要对BGR三个通道分别进行操作,这时就需要把BGR拆分成单个通道。有时需要把独立通道的图片合并成一个BGR图像。
b,g,r = cv2.split(img)
Img = cv2.merge(b,g,r)
(5)为图像扩边(填充)
在图像周围创建一个边,就像相框一样,可以使用cv2.copyMakeBorder()函数。
(6)图像混合
这其实也是加法,但是不同的是两幅图像的权重不同,这就会给人一种混合或者透明的感觉。函数cv2.addWeighted()对图片进行混合操作。
五、程序性能检测及优化
(1)使用opencv检测程序效率
Cv2.getTickCount函数返回从参考点到这个函数被执行的时钟数。
Cv2.getTickFrequency返回时钟频率,或者说每秒钟的时钟数。
六、opencv中的图像处理
(1)物体跟踪
将一副图像从BGR转换到HSV,可以利用这一点来提取带有某个特定颜色的物体。在HSV颜色空间中要比在BGR空间中更容易表示一个特定颜色。
(2)扩展缩放:只是改变图像的尺寸大小
Cv2.resize()
(3)平移
平移就是将对象换一个位置。如果要沿(x,y)方向移动,移动的距离是(Tx,
Ty),可以以下面的方式构建移动矩阵。
我们可以使用numpy数组构建这个矩阵(数据类型是np.float32),然后把它传给函数cv2.warpAffine()。例子如下:
M = np.float32([[1, 0, 10], [0, 1, 10]])
img2 = cv2.warpAffine(img, M, (img.shape[1],
img.shape[0]))
第三个参数是输出图像的大小,它的格式是图像的(宽,高)。
(4)旋转
对一个图像旋转角度,需要使用到下面形式的旋转矩阵。
Opencv允许在任意地方进行旋转,此时旋转矩阵的形式修改为:
其中:
为了构建这个旋转矩阵,opencv提供了一个函数:cv2.getRotationMatrix2D来产生这个矩阵。
例子:
M = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 0.6)
dst = cv2.warpAffine(img, M, (2*cols, 2*rows))
(5)仿射变换
在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。然后cv2.getAffineTransform会创建一个2*3的矩阵,最后这个矩阵会被传给函数cv2.warpAffine。
例子如下:
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))
(6)透视变换
对于视角变化,我们需要一个3*3变换矩阵。在变换前后直线还是直线。要构建这个变换矩阵,需要在输入图像上找4个点,以及他们在输出图像上对应的位置。这四个点中的任意三个都不能共线。这个变换矩阵可以由cv2.getPerspectiveTransform构建,然后把这个矩阵传给cv2.warpPerspective。
七、图像阈值
(1)简单阈值
当像素值高于阈值时,我们给这个像素赋予一个新值,否则我们给它赋予另外一种颜色。这个函数就是cv2.threshhold()
(2)自适应阈值
当同一幅图像上的不同部分具有不同亮度时,我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
函数cv2.adaptiveThreshold
(3)Otsu’s
二值化
在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答案就是不听的尝试。如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)。我们岂不是应该在两个峰之间的峰谷选一个值作为阈值,这就是Otsu二值化要做的。简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。
八、图像平滑
(1)2D卷积
我们可以对2D图像实施低通滤波(LPF),高通滤波(HPF)等。LPF帮助我们去除噪音,模糊图像。HPF帮助我们找到图像的边缘。
函数cv.filter2D()可以让我们对一副图像进行卷积操作。
(2)opencv提供的四种模糊技术
1、平均
这是由一个归一化卷积框完成,它只是用卷积框覆盖区域所有像素的平均值来代替中心元素,可以使用cv2.blur()和cv2.boxFilter()来完成。
2、高斯模糊
把卷积核换成高斯核(简单来说,方框不变,原来每个方框的值是相等的,现在里面的值是符合高斯分布的,方框中心的值最大,其余方框根据距离中心元素的距离递减,构成一个高斯小山包。原来的求平均数现在变成求加权平均数,全就是方框里的值)。实现的函数是cv.GaussianBlur()。
3、中值模糊
用与卷积框对应像素的中值来替代中心像素的值。这个滤波器经常用来去除椒盐噪声。前面的滤波器都是用计算得到的一个新值来取代中心像素的值,而中值滤波是用中心像素周围的值来取代他。函数是:
cv2.medianBlur()。效果如下:
4、双边滤波
函数cv2.bilateralFilter()能在保持边界清洗的情况下有效的去除噪声。
上图中的纹理被模糊掉了,但是边界还在。
九、形态学转换
两个基本的形态学操作是腐蚀和膨胀,他们的变体构成了开运算、闭运算、梯度等。
(1)腐蚀
这个操作会把前景物体的边界腐蚀掉。卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是1,那么中心元素就保持原来的像素值,否则就变为零。函数:
Cv2.erode()
(2)膨胀
与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是1,中心元素的像素值就是1.函数: cv2.dilate()
(3)开运算
先进行腐蚀再进行膨胀就叫做开运算,它被用来去除噪声。
函数: cv2.morphologyEx(img, cv2.MORPH_OPEN,
kernel)
(4)闭运算
先膨胀再腐蚀,它经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。
函数: cv2.morphologyEx(img, cv2.MORPH_CLOSE,
kernel)
(5)形态学梯度
结果看上去就像前景物体的轮廓,函数: cv2.morphologyEx(img,
cv2.MORPH_GRADIENT, kernel)
十、图像梯度
梯度简单来说就是求导,分别是Sobel/Scharr/Laplacian。
Sobel算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。我们可以指定求导的方向(xorder或yorder)。一般,往哪个方向求导,则哪个方向的边框就比较清晰。
十一、Canny边缘检测
Canny边缘检测是一种非常流行的边缘检测算法,它分为很多步:
(1)噪声去除
由于边缘检测很容易收到噪声影响,所以第一步是使用5*5的高斯滤波器去除噪声,即使用高斯平滑函数
(2)计算图像梯度
对平滑后的图像使用sobel算子计算水平方向和竖直方向的一阶导数(图像梯度Gx和Gy),根据得到的这两幅梯度图(Gx和Gy)找到边界的梯度和方向,公式如下:
梯度的方向一般总是与边界垂直,梯度方向被归为四类:垂直、水平和两个对角线。
(3)非极大值抑制
在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
(4)滞后阈值
现在要确定哪些边界才是真正的边界,这时我们需要设置两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真的边界,那些低于minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。
A高于阈值maxVal所以是真正的边界点,C虽然低于maxVal但高于minVal并且与A相连,所以也被认为是真正的边界点,而B就会被抛弃。
(5)使用cv2.Canny()就可以完成以上几步。
十二、图像金字塔
一般情况下,我们要处理是一副具有固定分辨率的图像。但是有些情况下,我们需要对同一图像的不同分辨率的子图像进行处理。比如,我们要在一副图像中查找某个目标,比如脸,我们不知道目标在图像中的尺寸大小。这种情况下,我们需要创建一组图像,这些图像是具有不同分辨率的原始图像。我们把这组图像叫做图像金字塔。如果我们把最大的图像放在底部,最小的放在顶部,看起来像一座金字塔,故而得名图像金字塔。
图像金字塔的一个应用是图像融合。例如,在图像缝合中,你需要将两幅图叠在一起,但是由于连接区域图像像素的不连续性,整幅图的效果看起来会很差。这时图像金字塔就可以派上用场,他可以帮助实现无缝连接。
十三、轮廓
1、初识轮廓
轮廓认为是将连续的点连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
(1)查找轮廓:cv2.findContours()
(2)绘制轮廓:cv2.drawContours(),上面函数的返回值作为它的一个参数
2、轮廓特征
(1)矩:可以帮助我们计算图像的质心、面积。
Cv2.moments()会将计算得到的矩以一个字典的形式返回。
对象的重心:
(2)轮廓面积:cv2.contourArea()
(3)轮廓周长:cv2.arcLength()
(4)轮廓近似:
将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定。
假设我们要在一副图像中查找一个矩形,但是由于图像的种种原因,我们不能得到一个完美的矩形,而是一个“坏形状”。现在就可以使用这个函数来近似这个形状了。这个函数的第二个参数叫epsilon,它是原始轮廓到近似轮廓的最大距离。
(5)凸包:
函数cv2.convexHull()可以检测一个曲线是否具有凸性缺陷,并能纠正缺陷。一般来说,凸性曲线总是凸出来的,至少是平的。如果有地方凹进去了就被叫做凸性缺陷。
函数cv2.isContourConvex()可以用来检测一个曲线是不是凸的。
示例代码:
import cv2
img =
cv2.imread('D:\\tmp\\wu.jpg', 0)
_,thresh = cv2.threshold(img,
0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
image, contours, hierarchy =
cv2.findContours(thresh, 3, 2)
cnt =
contours[0]
k =
cv2.isContourConvex(cnt)
print(k)
hull =
cv2.convexHull(cnt)
image = cv2.cvtColor(image,
cv2.COLOR_GRAY2BGR)
cv2.polylines(image, [hull],
True, (0, 255, 0), 2)
cv2.imshow('hull',
image)
cv2.waitKey(0)
(6)
其他形状的轮廓
直边界矩形:cv2.boundingRect()
旋转的边界矩形:这个边界矩形是面积最小的,因为它考虑了对象的旋转。Cv2.minAreaRect()
最小外接圆:找到一个对象的外切圆,它是所有能够包括对象的圆中面积最小的一个。Cv2.minEnclosingCircle()
椭圆拟合:旋转边界矩形的内切圆,cv2.ellipse()
直线拟合
示例:
import numpy as
np
import cv2
img =
cv2.imread('D:\\tmp\\wu.jpg', 0)
_,thresh = cv2.threshold(img,
127, 255, cv2.THRESH_BINARY)
image,
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
color = cv2.cvtColor(image,
cv2.COLOR_GRAY2BGR)
for c in
contours:
#
直边界矩形
x,y,w,h
= cv2.boundingRect(c)
cv2.rectangle(color,
(x,y), (x+w, y+h), (0,255,0), 2)
#
旋转的边界矩形
rect
= cv2.minAreaRect(c)
box
= cv2.boxPoints(rect)
box
= np.int0(box)
cv2.drawContours(color,
[box], 0, (0,0,255), 3)
#
最小外接圆
(x,y),
radius = cv2.minEnclosingCircle(c)
center
= (int(x), int(y))
radius
= int(radius)
cv2.circle(color,
center, radius, (0,255,0), 1)
#
椭圆拟合
ellipse
= cv2.fitEllipse(c)
cv2.ellipse(color,
ellipse, (0,255,0), 2)
#
直线拟合
rows,cols
= color.shape[:2]
[vx,vy,x,y]
= cv2.fitLine(c, cv2.DIST_L2, 0,0.01,0.01)
lefty
= int((-x*vy/vx) + y)
righty
= int(((cols-x)*vy/vx)+y)
cv2.line(color,
(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow('img',
color)
cv2.waitKey(0)
(7)
凸缺陷
对象上的任何凹陷都被称为凸缺陷,Cv2.convexityDefect()可以帮助我们找到凸缺陷。它会返回一个数组,其中每一行包含的值是[起点,终点,最远的点,到最远点的近似距离]。
(8)
点到轮廓的距离
求解图像中的一个点到一个对象轮廓的最短距离。如果点在轮廓的外部,返回值为负数。如果在轮廓上,返回值为0.如果在轮廓内部,返回值为正。
函数:
cv2.pointPolygonTest()
(9)
形状匹配
函数cv2.matchShape()可以比较两个形状或轮廓的相似度。返回值越小,匹配越好。它是根据Hu 矩来计算的。
Hu矩是归一化中心矩的线性组合,之所以这样做是为了能够获取代表图像的某个特征的矩函数,这些矩函数对某些变化如缩放,旋转,镜像映射具有不变形。
(10)
轮廓的层次结构
使用cv2.findContours来查找轮廓时需要传入一个参数:轮廓提取模式(Contour_Retrieval_Model),得到的结果包含3个数组:第一个图像,第二个是轮廓,第三个是层次结构。在图片中查找一个对象,有时对象可能位于不同的位置;还有些情况,一个形状在另外一个形状的内部。这种情况下我们称外部的形状为父,内部的形状为子。按照这种方式分类,一副图像中的所有轮廓之间就建立父子关系。这样我们就可以确定一个轮廓与其他轮廓是怎样连接的,比如它是不是某个轮廓的子轮廓,或者是父轮廓。
不管层次结构是什么样的,每一个轮廓都包含自己的信息:谁是父,谁是子等,表示方式:[Next,
Previous, First_Child, Parent]。
Next表示同一级组织结构中的下一个轮廓
Previous表示同一级结构中的前一个轮廓
First_Child表示它的第一个子轮廓
Parent表示它的父轮廓
轮廓检索模式:
lRETR_LIST:只提取所有的轮廓,而不去创建任何父子关系
lRETR_EXTERNAL:只返回最外边的轮廓,所有的子轮廓都会被忽略掉
lRETR_CCOMP:在这种模式下会返回所有的轮廓并将轮廓分为两级组织结构
lRETR_TREE:这种模式下会返回所有轮廓,并且创建一个完整的组织结构列表
十四、
直方图
(1)直方图计算和绘制
通过直方图可以对整幅图像的灰度分布有一个整体的了解。直方图的x轴是灰度值(0到255),y轴是图片中具有同一个灰度值的点的数目。
BINS:将像素值N等分,取每组的总和,而这里的每一个小组就被称为BIN。
绘制直方图:
1.
matplotlib方法:plt.hist()
2.
opencv方法:
histr=cv2.calcHist()
Plt.plot(histr,xx)
使用掩模:要统计图像某个局部区域的直方图只需要构建一副掩模图像,将要统计的部分设置成白色,其余部分为黑色,就构成了一副掩模图像。构建掩模图像例子mask =
np.zeros(img.shape[:2], np.uuint8)
Mask[100:300,
100:400] = 255
Masked_img =
cv2.bitwise_and(img, img, mask=mask)
(2)直方图均衡化
如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像元取值的动态范围,提高对比度和灰度色调的变化,使图像更加清晰。
直方图均衡化函数: cv2.equalizeHist()
(3)
CLAHE有限对比适应性直方图均衡化
的确在进行完直方图均衡化之后,图片背景的对比度被改变了。但是由于太亮也丢失了很多信息,造成这种结果的根本原因在于这副图像的直方图并不是集中在某一个区域。
为了解决这个问题,我们需要使用自适应的直方图均衡化。这种情况下,整幅图像会被分成很多小块,这些小块被称为”titles”,然后再对每一个小块分别进行直方图均衡化。所以在每一个的区域中,直方图会集中在某一个小的区域中。如果有噪声的话,噪声会被放大。为了避免这种情况的出现要使用对比度限制。对于每个小块来说,如果直方图中的bin超过对比度的上限,就把其中的像素均匀分散到其他bins中,然后再进行直方图均衡化。最后,为了去除每一个小块之间“人造的”边界,再使用双线性差值,对小块进行缝合。
使用函数: cv2.createCLAHE()
(4)
2D直方图
一维直方图是因为只考虑了图像的一个特征:灰度值。2D直方图是考虑两个图像特征:每个的颜色(Hue)和饱和度(Saturation)。示例:
import
cv2
from matplotlib
import pyplot as plt
img =
cv2.imread('D:\\tmp\\football.jpg')
hsv =
cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hist =
cv2.calcHist([hsv], [0,1], None, [180,256],
[0,180,0,256])
plt.imshow(hist,
interpolation='nearest')
plt.show()
得到:
X轴显示S值,Y轴显示H值。
(5)
直方图反向投影
作用:它可以用来做图像分割,或者在图像中找寻我们感兴趣的部分。简单来说,它会输出与输入图像同样大小的图像,其中的每一个像素值代表了输入图像上对应点属于目标对象的概率。用更简单的话来解释,输出图像中像素值越高(越白)的点就越可能代表我们要搜索的目标。(有点难,先掠过)
十五、
模板匹配
原理:
在一副大图中搜索查找模板图像位置的方法。函数:cv2.matchTemplate()。它用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和其对应的输入图像的子区域进行比较。返回的结果是一个灰度图像,每一个像素表示了此区域与模板的匹配程度。
使用cv2.minMaxLoc()来找到其中的最小值和最大值的位置。
如果需要多对象的模板匹配,就需要使用阈值了。
十六、
图像变化
1、傅里叶变化
原理
傅里叶变化经常被用来分析不同滤波器的频率特性,我们可以使用2D离散傅里叶变换(DFT)分析图像的频域特性,实现DFT的一个快速算法被称为快速傅里叶变化(FFT)。
对于一个正弦信号,如果它的幅度变化非常快,我们可以说它是高频信号,如果变化非常慢,我们称之为低频信号。对于图像来说,图像那里的幅度变化非常大则是边界点或者噪声。所以我们说边界和噪声是图像中的高频分量,如果没有如此大的幅度变化我们称之为低频分量。
2、Hough直线变换
霍夫变换用来检测图像中的各种形状。如果要检测的形状可以用数学表达式写出,就可以使用霍夫变换检测它。即使要检测的形状存在一点破坏或者扭曲也可以使用。
调用函数: cv2.HoughLines()
Probabilistic_Hough_Transform是对霍夫变换的一种优化,它不会对每一个点都进行计算,而是从一副图像中随机选取一个点集进行计算,对于直线检测来说这已经足够,但是使用这种变换我们必须要降低阈值。调用函数:
cv2.HoughLinesP()
3、
Hough圆环变换
圆形的数学表达式为(x - a)2+
(y - b)2=
r2,其中(a,b)为圆心的坐标,r为圆的直径,所以一个圆环需要3个参数来确定。如果进行圆环霍夫变换的累加器是3维的,这样的话效率就会很低,openct用一个比较巧妙的办法霍夫梯度法,它可以使用边界的梯度信息。
调用函数: cv2.HoughCircles()
十七、
分水岭算法图像分隔
任何一副灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水,随着水位升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分隔。
十八、
图像特征提取与描述
1、
图像特征
蓝色框中的区域是一个平面很难被找到和跟踪,无论你向哪个方向移动蓝色框,长的都一样。对于黑色框中的区域,它是一个边缘,如果沿垂直方向移动,它会改变,但是如果沿着水平方向移动就不会改变。而红色框中的角点,无论向哪个方向移动,得到的结果都不同,这说明它是唯一的。所以,基本上来说角点是一个好的图像特征。
特征检测:在图像中找到一些区域,无论向哪个方向移动这些区域变换都很大。
特征描述:对特征周围的区域进行描述,这样它才能在其他图像中找到相同的特征。
2、
Harris角点检测
向任何方向移动变换都很大转换成数学形式如下:
要使值最大,就是说必须使方程右侧的第二项的取值最大。
调用函数:cv2.cornerHarris()
亚像素级精确度的角点,使用函数cv2.cornerSubPix(),在使用这个函数时我们要定义一个迭代停止条件,当迭代次数达到或者精度条件满足后迭代就会停止。
3、
Shi-Tomasi角点检测&适合于跟踪的图像特征
该算法在Harris基础上做了优化,调用函数:cv2.goodFeaturesToTrack(),这个函数可以帮助我们使用Shi-Tomasi方法获取图像中N个最好的角点。该函数很适合在目标跟踪中使用。
4、
SIFT算法(尺度不变特征变换)
Harris算法具有旋转不变特性,即使图片发生了旋转,我们也能找到同样的角点。但如果对图像进行缩放,角点可能就不再是角点了,比如下图:
从上图可以很明显的看出来在不同的尺度空间不能使用相同的窗口检测极值点。对小的角点要用小的窗口,对大的角点只能使用大的窗口。为了达到这个目的我们要使用尺度空间滤波器。
调用函数:cv2.SIFT(),这个在当前版本中用不了,提示找不到
找到关键点:sift.detect()
绘制关键点:cv2.drawKeyPoints()
SIFT的优化版本: SURF算法
5、
角点检测的FAST算法
上面的特征检测器效果很好,但是从实时处理的角度来看这些算法都不够快。
步骤:
(1)在图像中选取一个像素点p,来判断它是不是关键点。Ip等于像素点p的灰度值。
(2)选择适当的阈值t
(3)如下图所示在像素点p的周围选择16个像素点进行测试
(4)如果在这16个像素点中存在n个连续像素点的灰度值都高于Ip+t,或者低于Ip-t,那么像素点p就被认为是一个角点。
(5)
为了获得更快的效果,还采用了另外的加速办法。首先对候选点的周围每隔90度的点1/9/5/13进行测试。如果p是角点,那么这四个点中至少有3个要符合阈值要求。如果不是的话肯定不是角点,就放弃。对通过这步测试的点再继续进行测试。
使用极大值抑制的方法,可以解决检测到的特征点相连的问题:
(1)
对所有检测到的特征点构建一个打分函数V。V就是像素点p与周围16个像素点差值的绝对值之和。
(2)
计算临近两个特征点的打分函数V
(3)
忽略V值最低的特征点
调用函数:cv2.FastFeatureDetector() 该函数也使用不了
6、
BRIEF(Binary Robust
Independent Elementary Features)算法
匹配过程中太多的维度是没有必要的,我们希望降维或者将描述符转换成二进制字符串。对这些字符串再使用汉明距离进行匹配。汉明距离的计算只需要进行XOR位运算以及位计算,这种计算很适合在现代的CPU上进行。
BRIEF不去计算描述符而是直接找到一个二进制字符串。这种算法使用的是已经平滑后的图像,它会按照一种特定的方式选取一组像素点对nd(x,y),然后在这些像素点对之间进行灰度值对比。例如,第一个点对的灰度值分别为p和q。如果p小于q,结果就是1,否则就是0.就这样对nd个点对进行对比得到一个nd维的二进制字符串。
7、
ORB(Oriented FAST and Rotated
BRIEF)
SIFT和SURF算法是有专利保护的,但是ORB不需要。
ORB基本是FAST关键点检测和BRIEF关键点描述器的结合体,并通过很多修改增强了性能。
示例代码:
import
cv2
from
matplotlib import pyplot as plt
img =
cv2.imread('D:\\tmp\\timg.jpg', 0)
orb =
cv2.ORB_create()
kp,des =
orb.detectAndCompute(img, None)
img2 =
cv2.drawKeypoints(img, kp, None, color=(0,255,0),
flags=0)
plt.imshow(img2)
plt.show()
8、
特征匹配
Brute-force匹配的基础:首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行距离测试,最后返回距离最近的关键点。
使用cv2.BFMatcher()创建一个BF-Matcher对象。
示例:
import
cv2
from
matplotlib import pyplot as plt
img1 =
cv2.imread('D:\\tmp\\timg.jpg', 0)
img2 =
cv2.imread('D:\\tmp\\timg.jpg', 0)
orb =
cv2.ORB_create()
kp1,des1 =
orb.detectAndCompute(img1, None)
kp2,des2 =
orb.detectAndCompute(img2, None)
bf =
cv2.BFMatcher()
matches =
bf.knnMatch(des1, des2, k=2)
good =
[]
for m,n in
matches:
if
m.distance < 0.75 * n.distance:
good.append([m])
img3 =
cv2.drawMatchesKnn(img1, kp1, img2, kp2, good[:10], None,
flags=2)
plt.imshow(img3)
plt.show()
十九、
视频分析
1、
Meanshift算法
作用:在视频中找到并跟踪目标对象
原理:假设我们有一堆点和一个小的圆形窗口,我们要完成的任务就是将这个窗口移动到最大灰度密度处(或者是点最多的地方)。
通常情况下我们要使用直方图方向投影得到的图像和目标对象的起始位置,目标对象的移动会反映到直方图反向投影图中。就这样,meanshift算法就把我们的窗口移动到图像中灰度密度最大的区域了。
2、
Camshift算法
meanShift窗口的大小是固定的,而汽车由远及近是一个逐渐变大的过程,固定的窗口是不合适的。所以需要根据目标的大小和角度来对窗口的大小和角度进行修订。该算法叫CamShift算法。
这个算法首先使用meanshift,meanshift找到并覆盖目标之后,再去调整窗口大小。它还会计算目标对象的最佳外接椭圆的角度,并以此调节窗口角度。然后使用更新后的窗口大小和角度来在原来的位置继续进行meanshift。重复这个过程直到达到需要的精度。
3、
光流
概念:由于目标对象或者摄像机的移动造成的图像对象在连续两帧图像中的移动被称为光流。它是一个2D向量场,可以用来显示一个点从第一帧图像到第二帧图像之间的移动。
光流的作用:
(1)
由运动重建结构
(2)
视频压缩
(3)
Video
Stabilization等
光流基于以下假设:
(1)
在连续两帧图像之间目标对象的像素的灰度值不改变
(2)
相邻的像素具有相同的运动
Lucas-Kanade法:领域内的所有点具有相似的运动。它利用一个3*3领域的9个点具有相同运动这一点。这样就可以找到9个点的光流方程,用它们组成一个具有两个未知数9个等式的方程组,这是一个约束条件过多的方程组。一个好的解决方法就是使用最小二乘拟合。
调用函数:cv2.calcOpticalFlowPyrLK()
使用函数cv2.goodFeatureToTrack()来确定要跟踪的点
4、
背景减除
目的:例如顾客统计,使用一个静态摄像头来记录进入和离开房间的人数,需要从静止的背景中提取移动的前景。
(1)
BackgroundSubtractorMOG
这是一个以混合高斯模型为算法的前景/背景分割算法。它使用K个高斯分布混合对背景像素进行建模,使用这些颜色(在整个视频中)存在时间的长短作为混合的权重。
使用cv2.createBackgroundSubtractorMOG()创建一个背景对象
使用backgroundsubtractor.apply()就可以得到前景的掩模了。
(2)
BackgroundSubtractorGMG
此算法结合了静态背景图像估计和每个像素的贝叶斯分隔。