文章图片
文章目录
- 一、图像处理
-
-
- 简单操作
- 简单绘图
- 鼠标事件
- 图像操作
- 颜色空间
- 几何变换
- 图像阈值、二值化
- 图像平滑、形态学转换(平均.高斯.中值.双边滤波、膨胀腐蚀开闭运算)
- 边缘检测(Sobel、Laplacian、Canny)
- 图像金字塔
- 轮廓
- 直方图
- 傅里叶变换(DFT、FFT)
- 模板匹配
- 霍夫变换
- 分水岭算法图像分割
- 交互式前景提取
- 角点检测(Harris、Shi-Tomasi、SIFT、SURF、FAST、BRIEF、ORB)
- 特征匹配(Brute-Force、FLANN)
- 图像去噪
- #图像修补
- Haar级联检测
-
- 二、视频分析
-
-
- 基本操作
- 目标跟踪(Meanshift、Camshift)
- 光流
- 背景剪除(BackgroundSubtractorMOG、MOG2、GMG)
-
- 三、摄像机标定和3D重构
-
-
- 摄像机标定
- 姿势估计
- 对极几何
- 深度地图
-
最后更新
2020.08.18
opencv-python 是只包含了主要模块的包,opencv-contrib-python包含了主要模块以及扩展模块,扩展模块主要是包含了一些带专利的收费算法(如shift特征检测)以及一些在测试的新的算法(稳定后会合并到主要模块)
使用opencv-contrib-python包,如果装的是opencv-python,则需要将其卸载,然后重装opencv-contrib-python,可以使用命令:
pip uninstall opencv-python
pip install opencv-contrib-python
一、图像处理 首先导入一些必要的包
import cv2# 此处用的是opencv-python 4.0
from pylab import *# matplotlib画图的子包
import numpy as np
import random
import time
import os
简单操作
img = cv2.imread('rabbit.jpg')# ,cv2.COLOR_BGR2GRAY或,0 读黑白图,-1 读带透明通道的
print(img.shape)# (960, 960, 3)
img = cv2.resize(img, (600, 600))
cv2.namedWindow('img', cv2.WINDOW_NORMAL)# ,cv2.WINDOW_NORMAL 可调整窗口大小
cv2.imshow('img', img)# 显示图片
key = cv2.waitKey(0)# 传参图片显示时间,为0则一直显示
print(key, ord('S'))# 不做操作,key为-1,可以做判断,继续还是卡住
# key的值为按键的ASCII码,比如空格32、回车13、1是49、Esc27
cv2.destroyAllWindows()
# cv2.imwrite('rabbit1.jpg',img)
imshow(img)# cv加载图片是BGR模式,pylab是RGB,彩色无法正确显示
show()
文章图片
文章图片
文章图片
简单绘图
blackimg = np.zeros((512, 512, 3), np.uint8)# 创建512*512黑图
cv2.line(blackimg, (0, 0), (513, 511), (255, 0, 0), 5)# 线段,始终,蓝,粗细
cv2.rectangle(blackimg, (33, 33), (333, 222), (0, 255, 0), 2)# 矩形,绿
cv2.circle(blackimg, (333, 333), 33, (0, 0, 255), -1)# 圆中点,半径,红,-1为填充
cv2.ellipse(blackimg, (255, 255), (100, 50), 90, 0, 180, (123, 1, 66), -1)
# 椭圆中点,(100,50)长短轴长度,开始时的角度,始终角度,紫
cv2.putText(blackimg, 'zhoger', (10, 500), cv2.FONT_HERSHEY_SIMPLEX, 2, (90, 250, 0), 2)# 写文字,起点,绿,粗细
# cv2.polylines(img,pts) # 可画多条线,pts要画的线的参数列表
# 所有绘图函数的返回值都是None
cv2.imshow('img', blackimg)
cv2.waitKey(0)
文章图片
鼠标事件
even = [i for i in dir(cv2) if 'EVENT' in i]
print(even)# 所有鼠标事件
img = np.zeros((512, 512, 3), np.uint8)# 创建黑图# 点击圆环
def draw_circle(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img, (x, y), 30, (random.random() * 255, random.random() * 255, random.random() * 255), 3)
# 3是圆环效果,-1就是实心圆cv2.namedWindow('img')
cv2.setMouseCallback('img', draw_circle)
while 1:
cv2.imshow('img', img)
if cv2.waitKey(20) & 0xFF == 27:
break# 鼠标画圆
drawing = False# true if mouse is pressed
ix, iy = -1, -1
def draw_circle(event, x, y, flags, param):
global ix, iy, drawing, mode
if event == cv2.EVENT_LBUTTONDOWN:# 起手
drawing = True
ix, iy = x, y
elif event == cv2.EVENT_MOUSEMOVE: # 鼠标移动
if drawing is True:
cv2.circle(img, (x, y), abs(y-iy), (random.random() * 255, random.random() * 255, random.random() * 255), -1)
elif event == cv2.EVENT_LBUTTONUP:# 收手
drawing = False
cv2.circle(img, (ix, iy), abs(y-iy), (random.random() * 255, random.random() * 255, random.random() * 255), 1) # 随机色边框
img = np.zeros((512, 512, 3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image', draw_circle)
while(1):
cv2.imshow('image', img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()#调色板
def nothing(x):
pass
# 创建一副黑色图像
img = cv2.imread('shui.jpg',-1)
cv2.namedWindow('image')
cv2.createTrackbar('R','image',0,255,nothing)
cv2.createTrackbar('G','image',0,255,nothing)
cv2.createTrackbar('B','image',0,255,nothing)
switch='0:OFF\n1:ON'
cv2.createTrackbar(switch,'image',0,1,nothing)
while(1):
cv2.imshow('image',img)
k=cv2.waitKey(1)&0xFF
if k==27:
break
r=cv2.getTrackbarPos('R','image')
g=cv2.getTrackbarPos('G','image')
b=cv2.getTrackbarPos('B','image')
s=cv2.getTrackbarPos(switch,'image')
if s==0:
img[:]=0
else:
img[:]=[b,g,r]
cv2.destroyAllWindows()
文章图片
文章图片
文章图片
图像操作
img = cv2.imread('shui.jpg')
print(img.dtype)# 数据类型 uint8
for i in range(200, 270):
img[:, i] = [0, 255, 0]# 改像素区域为绿色
img = img[10:500, 20:700]# 获得图像区域
b, g, r = cv2.split(img)# 拆分,还是用切片好
img = cv2.merge([b, g, r])# 合并图像通道,4.0版本需是list
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.imshow('img', img[:, :, 0])# 通道b
cv2.waitKey(0)
文章图片
文章图片
img = cv2.imread('rabbit.jpg')
img = cv2.resize(img, (int(img.shape[0] * 0.4), int(img.shape[1] * 0.4)))
q1 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_CONSTANT)# 填充纯色
q2 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_DEFAULT)# 镜像
q3 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_ISOLATED)# 填充纯色
q4 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_REFLECT)# 镜像
q5 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_REFLECT101)# 镜像
q6 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_REPLICATE)# 边界像素向外扩充
q7 = cv2.copyMakeBorder(img, 40, 40, 40, 40, cv2.BORDER_WRAP)# 图片阵
q8 = cv2.add(img, 200)# 加上像素值
img2 = cv2.imread('shui.jpg')
q9 = cv2.addWeighted(img, 0.4, img2, 0.2, 0.1)
# 图像混合,给两个图像不同的权重,dst = α·img1+β·img2+γ ,需是相同大小的图片
a = [q1, q2, q3, q4, q5, q6, q7, q8, q9]
for i in a:
cv2.imshow('img', i)
cv2.waitKey(0)
文章图片
我做了一个没啥意思的转换图片的幻灯片,但代码逻辑可以参考
q1 = cv2.imread('rabbit.jpg')
q2 = cv2.imread('shui.jpg')
q1 = cv2.resize(q1, (500, 500))
q2 = cv2.resize(q2, (500, 500))
flg = -1
time.sleep(3)
for i in range(1000):
q3 = cv2.addWeighted(q1, i * 0.001, q2, 1 - i * 0.001, 0)
if i * 5 % 1000 == 0: flg += 2
if i * 5 > 500 * (flg) and i * 5 < 500 * (flg + 1):
cv2.rectangle(q3, (500 * flg + 500 - i * 5, 0), (500 * flg + 500 - i * 5, 500),
(random.random() * 255, random.random() * 255, random.random() * 255), 5)
else:
cv2.rectangle(q3, (i * 5 + 500 - 500 * flg, 0), (i * 5 + 500 - 500 * flg, 500),
(random.random() * 255, random.random() * 255, random.random() * 255), 5)
cv2.imshow('img', q3)
cv2.waitKey(10)
颜色空间
img = cv2.imread('bird.jpg')
img = cv2.resize(img, (500, 700))
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# 转换为HSV
# 设定阈值,构建掩模
low = np.array([20, 60, 60])
upp = np.array([40, 255, 255])
mask = cv2.inRange(hsv, low, upp)
res = cv2.bitwise_and(img, img, mask=mask)# 原图像和掩模位运算
cv2.imshow('img', hsv)
cv2.waitKey(0)
# 可以进行简单的物体追踪,颜色单一、和环境不相容的情况green = np.uint8([[[0, 255, 0]]])# 3[]分别对应cvArray,cvMat,IplImage
hsvgreen = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)# RGB绿色对应的HSV绿色
print(hsvgreen)
文章图片
几何变换
img = cv2.imread('rabbit.jpg')
img = cv2.resize(img, (500, 500))# q1=cv2.resize(img,None,fx=1.1,fy=1.1,interpolation=cv2.INTER_CUBIC) # 缩放插值
# M=cv2.getRotationMatrix2D((50,250),45,0.8) # 旋转中心,角度,缩放
# q1=cv2.warpAffine(img,M,(500,500)) # 几何变换#p1 = np.float32([[50, 50], [200, 50], [50, 200]])
#p2 = np.float32([[10, 10], [200, 50], [50, 200]])
#M = cv2.getAffineTransform(p1, p2)# 三点确定一个平面
#q1 = cv2.warpAffine(img, M, (500, 500))# 仿射变换p1=np.float32([[50,50],[400,50],[50,400],[400,400]])
p2=np.float32([[0,0],[0,200],[200,0],[250,350]])
M=cv2.getPerspectiveTransform(p1,p2) # 四点定位,任意三个都不能共线
q1=cv2.warpPerspective(img,M,(500,500)) # 透视变换cv2.imshow('img', q1)
cv2.waitKey(0)hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# 转换到hsv
red = np.array([0, 43, 46])# 设定阈值
dred = np.array([45, 255, 255])
mask = cv2.inRange(hsv, red, dred)# 阈值掩模
res = cv2.bitwise_and(img, img, mask=mask)# 原图像掩模位运算,物体跟踪
res = cv2.medianBlur(res, 9)# must be odd
cv2.imshow('img', res)
cv2.waitKey(0)
文章图片
文章图片
图像阈值、二值化
img = cv2.imread('10.jpg', 0)
# img=cv2.resize(img,(500,700))# 全局阈值
ret, q1 = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)# 二值化
# ret,q1=cv2.threshold(img,170,255,cv2.THRESH_BINARY_INV) # 反向
# ret,q1=cv2.threshold(img,160,255,cv2.THRESH_TRUNC) # 二值化反转
# ret,q1=cv2.threshold(img,100,255,cv2.THRESH_TOZERO) # 截断二值化# 自适应阈值
# 当同一幅图像上的不同部分的具有不同亮度时,我们需要采用自适应阈值
# 此时的阈值是根据图像上的每一个小区域计算与其对应的阈值
# 因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果# 阈值相邻区域的平均值最后两个参数 邻域,bias
img = cv2.medianBlur(img, 5)
q2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# 阈值相邻区域的加权和
q3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)# OTSU二值化自动找最优阈值,非双峰图不用
# 如果是一副双峰图像,OTSU会对它自动根据其直方图计算出一个阈值
ret4, q4 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# OTSU二值化
cv2.imshow('img', q1)
cv2.waitKey(0)
# 不同场景不同使用
文章图片
文章图片
文章图片
文章图片
文章图片
图像平滑、形态学转换(平均.高斯.中值.双边滤波、膨胀腐蚀开闭运算)
# 低通滤波LPF,去噪、模糊,高通滤波HPF,边缘检测
img = cv2.imread('d2.jpg')
img = cv2.resize(img, (400, 600))
k = np.ones((5, 5)) / 25# 平均化的卷积核
img1 = cv2.filter2D(img, -1, k)# 2D卷积,滤波
img2 = cv2.blur(img, (7, 7))# 平均化的卷积
img3 = cv2.GaussianBlur(img, (7, 7), 0)# 高斯模糊,7*7卷积核,标准差0,卷积后方框内像素符合高斯分布
img4 = cv2.medianBlur(img, 5)# 可以有效去除椒盐噪声,用卷积框对应像素的中值来替代,卷积核的大小必须是奇数
img5 = cv2.bilateralFilter(img, 9, 75, 75)# 双边滤波,保持边界清晰,9邻域,75标准差
# 它同时使用空间高斯权重和灰度值相似性高斯权重。
# 空间高斯函数确保只有邻近区域的像素对中心点有影响,灰度值相似性高斯函数确保只有与中心像素灰度值相近的才会被用来做模糊运算k1 = np.ones((5, 5))
img6 = cv2.erode(img, k1, iterations=1)# 腐蚀,卷积核通过非1的都为0,分开两个连接的item,也能去噪
img7 = cv2.dilate(img, k1, iterations=1)# 膨胀,与腐蚀相反,卷积核通过非0的都为1,连接两个item,可以填充物体上的小洞
opens = cv2.morphologyEx(img, cv2.MORPH_OPEN, k1)# 开运算 先腐蚀后膨胀,去噪
closes = cv2.morphologyEx(img, cv2.MORPH_CLOSE, k1)# 闭运算 先膨胀后腐蚀,填充
grad = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, k1)# 边缘梯度
q1 = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, k1)# 原图和开运算之差
q2 = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, k1)# 原图和闭运算之差
res = np.hstack((img, img6))# 两幅图像同时显示
cv2.imshow('img', res)
cv2.waitKey(0)
文章图片
边缘检测(Sobel、Laplacian、Canny)
3个图像梯度滤波器,sobel是求一阶导数,scharr是对sobel的优化,laplacian求二阶导数
文章图片
看一眼卷积核就能懂sobel算子的原理了,就是经过一个[正,0,负]的卷积乘法,可以得到图像灰度偏差估计值,然后横着一刀竖着一刀,结合起来,就是图像中一个位置的梯度值了,然后给个阈值,大于阈值时就认为该点为边界点
这是基础边缘检测算子,效率高,抗噪声强,但细节不精确
文章图片
拉普拉斯是一种各向同性二阶微分算子。Laplace算子对孤立象素的响应要比对边缘或线的响应要更强烈,因此只适用于无噪声图象。存在噪声情况下,使用Laplacian算子检测边缘之前需要先进行低通滤波。在求二阶导数时,极值点通常为0,因此可以以此检测图像的边缘,但是在图像的噪声部分同样会出现二阶导为0的情况,这也就是为什么拉普拉斯只适用于无噪声的图像。
由于它使用的也是梯度,所以Laplace算子在opencv实现时是调用了Sobel算子
文章图片
canny算法也是调用sobel算子,不过它更完善更精细,抗噪,效率低些
canny计算过程
1、用高斯滤波器平滑图像
2、用一阶偏导的有限差分来计算梯度的幅值和方向(调用Sobel算子)
3、对梯度幅值进行非极大值抑制(只保留梯度变化中最锐利的)
4、用双阈值算法检测和连接边缘
文章图片
img=cv2.imread('cc.jpg')
img=cv2.resize(img,(550,737))
lap=cv2.Laplacian(img,cv2.CV_8U)
sx=cv2.Sobel(img,ddepth=cv2.CV_8U,dx=1,dy=0,ksize=-1)
sy=cv2.Sobel(img,ddepth=cv2.CV_8U,dx=0,dy=1,ksize=-1)
s=cv2.addWeighted(sx,0.5,sy,0.5,0) # 结合sobel的x和y方向的梯度
# canny边缘检测,边缘检测容易受噪声影响,所以先高斯滤波除噪声
# img1=cv2.GaussianBlur(img,(3,3),0)
edge=cv2.Canny(img,50,200) # 最小最大阈值
cv2.imshow('img',edge)
cv2.waitKey(0)
图像金字塔
有两类图像金字塔:高斯金字塔和拉普拉斯金字塔
高斯金字塔,对高分辨率图像先做高斯模糊,再将图像大小缩小到原来的一半,多个尺度就构成了高斯金字塔
拉普拉金字塔的图像就像边界图,其中很多像素都是0。称作图像的拉普拉斯分解,把高分辨率图像分解成小分辨率的子图,然后根据之前保留的残差图可以重构出原始高分辨率图
他们经常被用在图像压缩、图像拼接(比用mask模糊效率更高效果更好)中
img=cv2.imread('rabbit.jpg')
img=cv2.resize(img,(500,500))
lower=cv2.pyrDown(img) # 降低分辨率大小
hig=cv2.pyrUp(img) # 尺寸变大,分辨率不变
cv2.imshow('img',lower)
cv2.waitKey(0)
文章图片
轮廓
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或边缘检测
img=cv2.imread('133.png',0) # 灰度图
img1=cv2.imread('133.png')
img=cv2.resize(img,(960,540))
img1=cv2.resize(img1,(960,540))
ret,q1=cv2.threshold(img,130,255,0)# 二值化
contours, hierarchy=cv2.findContours(q1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # 输入图像,轮廓检索模式,轮廓近似方法
# print(contours)
img3=cv2.drawContours(img1,contours,-1,(0,255,0),3) # 绘制边缘轮廓M=cv2.moments(contours[0])# 图像的矩,可以帮助我们计算图像的质心,面积等
print(M)
area=cv2.contourArea(contours[0])
print(area,M['m00'])# 轮廓的面积
perimeter=cv2.arcLength(contours[0],True) # 周长。True闭合,False一条曲线
print(perimeter)
approx=cv2.approxPolyDP(contours[0],perimeter*0.1,True) # 近似轮廓
# img2=cv2.drawContours(img,approx,-1,(0,255,0),3) # 绘制边缘轮廓
# 将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定。使用的Douglas-Peucker算法hull=cv2.convexHull(contours[0]) # 凸包,获得凸性缺陷的角点
k=cv2.isContourConvex(contours[0])# 凸性检测,检测一个曲线是不是凸的
print(hull,k)x,y,w,h=cv2.boundingRect(contours[0])# 边界矩形
print(x,y,w,h)
img2=cv2.rectangle(img1,(x,y),(x+w,y+h),(22,255,22),2)(x,y),radius = cv2.minEnclosingCircle(contours[0]) # 最小外接圆
center = (int(x),int(y))
radius = int(radius)
print(center,radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
# ellipse = cv2.fitEllipse(contours[0])# 椭圆拟合,内切
# im = cv2.ellipse(img,ellipse,(0,255,0),2,2,10,'b')# 直线拟合
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(contours[0], cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)leftmost = tuple(contours[0][contours[0][:,:,0].argmin()][0])
rightmost = tuple(contours[0][contours[0][:,:,0].argmax()][0])
topmost = tuple(contours[0][contours[0][:,:,1].argmin()][0])
bottommost = tuple(contours[0][contours[0][:,:,1].argmax()][0])
print(leftmost,rightmost,topmost,bottommost) # 轮廓对象最上,最下,最左,最右的点ret1,q2=cv2.threshold(img,22,255,0)# 二值化
c1,h1=cv2.findContours(q1,2,1)
c2,h2=cv2.findContours(q2,2,1)
res=cv2.matchShapes(c1[0],c2[0],1,0) # 两个轮廓匹配相似度,值越小,匹配越好,和自己匹配值为0
print(res)cv2.imshow('img',c1)
cv2.waitKey(0)
文章图片
直方图
通过直方图你可以对整幅图像的灰度分布有一个整体的了解。直方图的 x 轴是灰度值(0 到 255),y 轴是图片中具有同一个灰度值的点的数目
文章图片
直方图均衡化:把它的直方图做一个横向拉伸,平均像素落点,防止太尖锐
CLAHE有限对比适应性直方图均衡化:整幅图像会被分成很多小块,在opencv中大小默认是8x8,然后再对每一个小块分别进行直方图均衡化
img=cv2.imread('rabbit.jpg',0) # 灰度图
img=cv2.resize(img,(500,500))
histd=cv2.calcHist([img],[0],None,[256],[0,256]) # [图],[0]是灰度图 [1][2][3]是RGB,mask,BIN数目,像素范围
# 统计像素直方图,得到256*1数组,比numpy的效率快几十倍,
# 代替np.bincount(),np.histgram ()
# 第三个参数可以加mask数组掩模,比如mask = np.zeros(img.shape[:2], np.uint8)plt.hist(img.ravel(),256,[0,256])# plot的直方图
plot(histd,color='b') # 曲线图
show()equ=cv2.equalizeHist(img)# 直方图均衡化,横向拉伸,会改变对比度丢失信息
# CLAHE(Contrast Limited Adaptive Histogram Equalization)限制对比度的自适应直方图均衡化,以小块进行均衡化,然后双线性插值缝合
# CLAHE是一个比较有意思的图像增强的方法,主要用在医学图像上面,和比赛中
# CLAHE起到的作用简单来说就是增强图像的对比度的同时可以抑制噪声
# 拉伸直方图,等同于让对比度更大,但最左右的对比度不受太大影响,黑还是黑 白还是白,还可以使用albumentations来做
clahe=cv2.createCLAHE(clipLimit=2,tileGridSize=(8,8)) # 默认(8,8)小块
cll=clahe.apply(img) # 不损失对比度
res=np.hstack((img,equ,cll))# 两个图片同时显示
cv2.imshow('img',res)
cv2.waitKey(0)# 2D直方图,需要考虑颜色Hue和饱和度Saturation
img=cv2.imread('g1.jpg')
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV) # img不能传0
histh=cv2.calcHist([hsv],[0,1],None,[180,256],[0,180,0,256])
imshow(histh,interpolation='nearest') # 图像和颜色直方图
show()
傅里叶变换(DFT、FFT)
# 图像变换离散傅里叶变换DFT快速傅里叶变换FFTLPF低通HPF高通滤波
img=cv2.imread('g1.jpg',0) # 灰度图
img=cv2.resize(img,(400,700))
dft=cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift=np.fft.fftshift(dft)
magnitude_spectrum=20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
print(magnitude_spectrum)# DFT的幅度频谱
# sobel、laplacian是高通滤波器
imshow(magnitude_spectrum)
show()
# 输出结果的中心部分更白(亮),这说明低频分量更多
模板匹配
模板匹配是用来在一副大图中搜寻查找模版图像位置的方法,是用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的输入图像的子区域进行比较,计算匹配度
# 模板匹配
img=cv2.imread('r4.jpg',0) # 灰度图
img=cv2.resize(img,(400,700))
img2=img.copy()
b=img[310:350,230:280]# right eye
w,h=b.shape[::-1]
print(w,h)
methods=[cv2.TM_CCOEFF,cv2.TM_CCOEFF_NORMED,cv2.TM_CCORR,
cv2.TM_CCORR_NORMED,cv2.TM_SQDIFF,cv2.TM_SQDIFF_NORMED]
# methods=[methods[5]]
for i in methods: #
img3=img2.copy()
res=cv2.matchTemplate(img,b,i)# 模板匹配
print('res',res)
min_val,max_val,min_loc,max_loc=cv2.minMaxLoc(res)# 找到其中的最小值和最大值的位置
print(min_val,max_val,min_loc,max_loc) # -0.4714 0.999 (320, 251) (230, 310)
if i in [cv2.TM_SQDIFF,cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left=max_loc
bottom_right=(top_left[0]+w,top_left[1]+h)
cv2.rectangle(img3, top_left, bottom_right, 255, 2)# 目标检测画框
# threshold=0.6# 阈值
# loc=np.where(res<=threshold)# 多对象检测,需要非极大值抑制
# for pt in zip(*loc[::-1]):
#cv2.rectangle(img3,pt, (pt[0] + w, pt[1] + h),255,2)
figure(0,(8,8))
subplot(121)
imshow(res,cmap='gray')
subplot(122)
imshow(img3,cmap='gray')
show()
# cv2.imshow('img',img3)
# cv2.waitKey(0)
文章图片
# 多模板匹配
imgg='jb.jpg'
img_rgb = cv2.imread(imgg)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread(imgg, 0)[150:176,135:165]
# cv2.rectangle(img_rgb, (135,150), (165,176), (0, 0, 255), 2)
# cv2.imshow('img',img_rgb)
# cv2.waitKey(0)
# exit()
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.6
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2)
cv2.imshow('img',img_rgb)
cv2.waitKey(0)
文章图片
霍夫变换
霍夫变换是一种特征提取,广泛应用在图像分析、计算机视觉以及数位影像处理
霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它
霍夫变换仅仅是一条直线都需要两个参数,这需要大量的计算。Probabilistic_Hough_Transform 是对霍夫变换的一种优化。它不会对每一个点都进行计算,而是从一幅图像中随机选取一个点集进行计算,对于直线检测来说这已经足够了。 cv2.HoughLinesP()
圆的表达式需要3个参数来确定。所以进行圆环霍夫变换的累加器必须是 3 维的,这样的话效率就会很低。所以 OpenCV 用来一个比较巧妙的办法,霍夫梯度法,它可以使用边界的梯度信息。 cv2.HoughCircles()
文章图片
# Hough直线变换25
img=cv2.imread('jgg.jpg') # 灰度图
# img=cv2.resize(img,(500,500))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edge=cv2.Canny(img1,100,200)
lines=cv2.HoughLines(edge,1,np.pi/180,200)# 需要先边缘检测
# print(lines)
for i in lines:
rho,theta=i[0][0:2]
print(rho,theta)
a=np.cos(theta)
b=np.sin(theta)
x0=a*rho
y0=b*rho
x1=int(x0+1000*(-b))
y1=int(y0+1000*a)
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
cv2.line(img,(x1,y2),(x2,y2),(0,0,255),2)# Probabilistic_Hough_Transform概率霍夫变换
imgz=cv2.imread('jgg.jpg') # 灰度图
imgz1=cv2.imread('jgg.jpg',0) # 灰度图
# imgz1=cv2.cvtColor(imgz,cv2.COLOR_BGR2GRAY)
edgez=cv2.Canny(imgz1,50,150)
minlinelength=100# 线的最短长度
maxlinegap=10# 线的最大间隔,小于它就当作一个直线
linesz=cv2.HoughLinesP(edgez,1,np.pi/180,100,minlinelength,maxlinegap)
print(linesz)
print(linesz.shape)# (217, 1, 4)
for i in linesz:
x1, y1, x2, y2=i[0]
cv2.line(imgz,(x1,y2),(x2,y2),(0,255,0),2)# 霍夫圆环变换
cir=cv2.HoughCircles(imgz1,cv2.HOUGH_GRADIENT,50,20,param1=50,param2=30,minRadius=0,maxRadius=0)
cirs=np.uint16(np.around(cir))
for i in cirs[0,:]:
cv2.circle(imgz,(i[0],i[1]),i[2],(0,255,0),2)
cv2.circle(imgz,(i[0],i[1]),2,(0,255,0),3)
cv2.imshow('img',imgz)
cv2.waitKey(0)
分水岭算法图像分割
分水岭算法图像分割:任何一副灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水。随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割
# 分水岭算法图像分割
img=cv2.imread('1333.png')
# img=cv2.resize(img,(400,700))
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_OTSU)# 二值化
edgeimage = cv2.Canny(gray, 10, 100)
thresh = np.uint8(thresh)
ret, q1 = cv2.connectedComponents(thresh)
mark=cv2.watershed(img,q1)
print(mark)
print(mark.shape)
contourf(mark[::-1])
show()
cv2.imshow('img',edgeimage)
cv2.waitKey(0)
文章图片
交互式前景提取
GrabCut算法是由微软剑桥研究院的人提出的,此算法在提取前景的操作过程中需要很少的人机交互,结果非常好,用于图像分割
开始需要用一个矩形将前景区域框住,然后算法进行迭代式分割直达达到最好结果
计算机会对我们的输入图像做一个初始化标记,它会标记前景和背景像素
使用一个高斯混合模型(GMM)对前景和背景建模
根据我们的输入,GMM 会学习并创建新的像素分布。对那些分类未知的像素(可能是前景也可能是背景),可以根据它们与已知分类(如背景)的像素的关系来进行分类(就像是在做聚类操作)
img = cv2.imread('5.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (10,10,999,999)
# 函数的返回值是更新的 mask, bgdModel, fgdModel
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,4,cv2.GC_INIT_WITH_RECT)
# 第二个参数 mask-掩模图像,用来确定那些区域是背景,前景。第三个包含前景的矩形(x,y,w,h),第六个 算法迭代次数
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
cv2.imshow('img',img)
cv2.waitKey(0)
plt.imshow(img),plt.colorbar(),plt.show()
文章图片
角点检测(Harris、Shi-Tomasi、SIFT、SURF、FAST、BRIEF、ORB)
角点是图像很重要的特征,对图像图形的理解和分析有很重要的作用
图像中的角点,无论向哪个方向移动,得到的结果都不同,这说明它是唯一的。所以,角点是一个好的图像特征
特征描述,当有了特征很它们的描述后,你就可以在所有的图像中找这个相同的特征了
特征检测,角点检测算法可归纳为3类:基于灰度图像的角点检测、基于二值图像的角点检测、基于轮廓曲线的角点检测。
Harris角点检测的原理和sobel差不多,就是滑动窗口计算每个点往四个方向移动后窗口的变化差异,进行打分,大于阈值的留下就是角点
文章图片
Shi-Tomasi角点检测原理和Harris一样,不过最后打分的函数不一样,直接用较小的那个特征值作为分数,不需要调整打分参数了。这个更精确
# # Harris角点检测
img=cv2.imread('jgg.jpg') # 灰度图
img=cv2.resize(img,(960,940))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img1=np.float32(img1)
dst=cv2.cornerHarris(img1,2,3,0.006)
# float32输入图像,领域大小,sobel球到窗口大小,角点检测方程自由度参数
dst=cv2.dilate(dst,None)
img[dst>0.1*dst.max()]=[0,255,0]# 大于阈值的标注
cv2.imshow('img',img)
cv2.waitKey(0)# Shi-Tomasi角点检测更精确
img=cv2.imread('p1.jpg') # 灰度图
img=cv2.resize(img,(800,700))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
corners=cv2.goodFeaturesToTrack(img1,1000,0.01,10)
# 想检测的角点数,质量水平0到1,角点间最短欧式距离,返回的结果是[[..]]
corners=np.int0(corners)
for i in corners:
x,y=i.ravel()
cv2.circle(img,(x,y),3,(0,255,0),-1)
cv2.imshow('img',img)
cv2.waitKey(0)
文章图片
文章图片
角点检测技术Harris等,它们具有旋转不变特性,即使图片发生了旋转,我们也能找到同样的角点。很明显即使图像发生旋转之后角点还是角点。那如果我们对图像进行缩放呢?角点可能就不再是角点了。
所以2004年提出了个新算法,尺度不变特征变换(SIFT)
Sift(尺度不变特征变换:Scale Invariant Feature Transform),其提取图像的局部特征,在尺度空间寻找极值点,并提取处其位置、尺度、方向信息。Sift的应用范围包括物体识别、机器人地图感知与导航、影像凭借、3D模型建立、手势识别、影像追踪等。
sift特征的特点
1、对旋转、尺度缩放、亮度变化保持不变,对视角变化、噪声等也存在一定程度的稳定性;
2、对特性,信息量丰富,使用与在海量特征数据中进行快速,精准的匹配;
3、多量性,即使少数几个物体也可以产生大量的Shift特征向量;
4、可扩展性,可以很方便的与其他形式的特征向量进行联合。
在不同的尺度空间不能使用相同的窗口检测极值点。对小的角点要用小的窗口,对大的角点只能使用大的窗口。为了达到这个目的我们要使用尺度空间滤波器。(尺度空间滤波器可以使用一些列具有不同方差σ的高斯卷积核构成)
使用小方差σ的高斯卷积核是可以很好的检测出小的角点,而使用大方差σ的高斯卷积核时可以很好的检测除大的角点
然后生成高斯差分金字塔后,就可以在不同的尺度空间和 2D 平面中搜索局部最大值了。
一旦找到关键点,我们就要对它们进行修正从而得到更准确的结果。作者使用尺度空间的泰勒级数展开来获得极值的准确位置,如果极值点的灰度值小于阈值(0.03)就会被忽略掉。
下一步采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取第一个图的某个关键点,通过遍历找到第二幅图像中的距离最近的那个关键点。此时要计算最近距离与第二近距离的比值。如果比值大于 0.8,就忽略掉。这会去除90%的错误匹配,同时只去除5%的正确匹配。
# SIFT关键点检测
img=cv2.imread('p1.jpg') # 灰度图
img=cv2.resize(img,(900,800))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift=cv2.SIFT_create()
print(sift)
kp=sift.detect(img1,None)
print(kp)
img=cv2.drawKeypoints(img,kp,cv2.DRAW_MATCHES_FLAGS_DEFAULT)
cv2.imshow('img',img)
cv2.waitKey(0)
文章图片
SURF(Speeded-Up Robust Features)加速稳健特征
SIFT算法执行慢,所以2006年提出了SURF,SURF不同的地方是使用盒子滤波器进行近似。在进行卷积计算时可以利用积分图像(积分图像的一大特点是:计算图像中某个窗口内所有像素和时,计算量的大小与窗口大小无关)
# 加速稳健特征算法SURF err maybe收费 没有,下一个
# img=cv2.imread('g1.jpg') # 灰度图
# img=cv2.resize(img,(500,800))
# img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# surf=cv2.SURF(400)# 没有,maybe收费
# kp=surf.detectAndCompute(img1,None)
# print(len(kp))
# img=cv2.drawKeypoints(img1,kp)
# cv2.imshow('img',img)
# cv2.waitKey(0)
上述几个特征检测器大多数效果很好。但是实时处理,这些算法都不够快。比如SLAM(同步定位与地图构建)、移动机器人,它们的计算资源非常有限
所以2006年提出了FAST算法
流程:
1、在图像中选取一个像素点p,来判断它是不是关键点
2、选择适当的阈值t
3、在像素点p的周围选择16个像素点进行测试,若在这16个像素点中存在n个连续像素点的灰度值超过p的像素±t,那么像素点p就被认为是一个角点
4、优化更快的话就是计算16个像素点的顺序了,1 9 5 13顺序测试
缺点是像素的选取不是最优的,并且检测到的很多特征点都是连在一起的(可以用NMS解决)
# 角点检测FAST算法比其他算法更快,但遇噪声不稳定
img=cv2.imread('sn.png') # 灰度图
img=cv2.resize(img,(800,800))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
fast=cv2.FastFeatureDetector_create(threshold=40)
kp=fast.detect(img1,None)
img=cv2.drawKeypoints(img,kp,cv2.DRAW_MATCHES_FLAGS_DEFAULT)
# img2=cv2.drawContours(img,kp,0,color=(0,255,0))
cv2.imshow('img',img)
cv2.waitKey(0)
文章图片
BRIEF(Binary Robust Independent Elementary Features)
SIFT算法使用的是 128 维的描述符。由于它是使用的浮点数,所以要使用512 个字节。同样 SURF 算法最少使用 256 个字节(64为描述符)。创建一个包含上千个特征的向量需要消耗大量的内存,在嵌入式等资源有限的设备上这样是合适的。匹配时还会消耗更多的内存和时间。
在实际的匹配过程中如此多的维度是没有必要的。我们可以使用 PCA,LDA 等方法来进行降维。但我们还是要先找到描述符才能使用哈希,这不能解决最初的内存消耗问题
所以BRIEF应运而生,它不去计算描述符而是直接找到一个二进制字符串。这
种算法使用的是已经平滑后的图像,它会按照一种特定的方式选取一组像素点
对,然后在这些像素点对之间进行灰度值对比。
BRIEF 是一种对特征点描述符计算和匹配的快速方法。这种算法可以实现很高的识别率,除非出现平面内的大旋转。
# BRIEF算法 (STAR检测器) err 没有
# img=cv2.imread('r3.jpg') # 灰度图
# img=cv2.resize(img,(500,800))
# img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# star=cv2.FeatureDetector_create('STAR')
# brief = cv2.DescriptorExtractor_create("BRIEF")
# cv2.imshow('img',img)
# cv2.waitKey(0)
ORB (Oriented FAST and Rotated BRIEF)
这个算法是在 2011 年提出的,在计算开支、匹配效率以及更主要的是专利问题方面 ORB 算法是是 SIFT 和 SURF 算法的一个很好的替代品。SIFT 和 SURF 算法是有专利保护的,如果你要使用它们,就可能要花钱。但是 ORB 不需要
ORB 基本是 FAST 关键点检测和 BRIEF 关键点描述器的结合体,并通过很多修改增强了性能。首先它使用 FAST 找到关键点,然后再使用 Harris角点检测对这些关键点进行排序找到其中的前 N 个点。它也使用金字塔从而产生尺度不变性特征
它使用灰度矩的算法计算出角点的方向。以角点到角点所在(小块)区域
质心的方向为向量的方向。为了进一步提高旋转不变性,要计算以角点为中心
半径为 r 的圆形区域的矩,再根据矩计算除方向。
# ORB算法,效率好于上面的大多角点检测算法
# SIFT、SURF是有专利保护,orb是fast和brief结合体
img=cv2.imread('g3.jpg') # 灰度图
img=cv2.resize(img,(1000,600))
img1=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
orb=cv2.ORB_create()
kp=orb.detect(img1,None)
kp,des=orb.compute(img1,kp)
print(kp[1])
img2 = cv2.drawKeypoints(img,kp,cv2.DRAW_MATCHES_FLAGS_DEFAULT) # 没有这个
cv2.imshow('img',img2)
cv2.waitKey(0)
文章图片
特征匹配(Brute-Force、FLANN)
openCV提供了两种匹配器Brute-Force 和 FLANN
Brute-Force匹配器很简单。首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点。
FLANN快速最近邻搜索包(Fast Library for Approximate Nearest Neighbors)。它包含一组优化算法,用于在大型数据集中快速搜索最近邻和高维特性。对于大型数据集,它比BFMatcher工作得更快。我们将看到基于FLANN的匹配器的第二个例子
# 特征匹配 蛮力Brute-Force匹配 FLANN匹配img=cv2.imread('jz1.jpg')
img1=cv2.imread('jz1-.jpg')
img=cv2.resize(img,(500,700))
img1=cv2.resize(img1,(500,700))# 用ORB 描述符来进行特征匹配
orb=cv2.ORB_create()
# find the keypoints and descriptors with SIFT
kp1,des1=orb.detectAndCompute(img,None)
kp2,des2=orb.detectAndCompute(img1,None)bf=cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=True)
matches = bf.match(des1,des2)
# Sort them in the order of their distance.
matches = sorted(matches, key = lambda x:x.distance)
print(matches)#cv2.DMatch float
img3 = cv2.drawMatches(img,kp1,img1,kp2,matches[:10], None, flags=2)
# cv2.imshow('img',img3)
# cv2.waitKey(0)# 用sift 特征匹配
sift = cv2.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img,None)
kp2, des2 = sift.detectAndCompute(img1,None)
# BFMatcher with default params
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)
# Apply ratio test
# 比值测试,首先获取与 A 距离最近的点 B(最近)和 C(次近),只有当 B/C
# 小于阈值时(0.75)才被认为是匹配,因为假设匹配是一一对应的,真正的匹配的理想距离为 0
good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append([m])
# cv2.drawMatchesKnn expects list of lists as matches.
img3 = cv2.drawMatchesKnn(img,kp1,img1,kp2,good[:10],None, flags=2)
# cv2.imshow('img',img3)
# cv2.waitKey(0)# FLANN快速最近邻搜索 两图像特征匹配
FLANN_index_kdtree=0
index_params=dict(algorithm=FLANN_index_kdtree,trees=5)
search_params=dict(checks=50)
flann=cv2.FlannBasedMatcher(index_params,search_params)
matches=flann.knnMatch(des1,des2,k=2) # 报错
matchesmask=[[0,0] for i in range(len(matches))]
for i,(m,n) in enumerate(matches):
if m.distance<0.7*n.distance:
matchesmask[i]=[1,0]
draw_params=dict(matchColor=(0,255,0),
singlePointColor=(255,0,0),
matchesMask=matchesmask,
flags=0)
img3=cv2.drawMatchesKnn(img,kp1,img1,kp2,matches,None,**draw_params)
cv2.imshow('img',img3)
cv2.waitKey(0)
文章图片
图像去噪
# 非局部平均值去噪算法
img = cv2.imread('nn.jpg')
print(img.shape)
img = cv2.resize(img, (500, 600))
dst = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21)
# h过滤器强度10,colorh,templatewindowsize推荐7,searchwindowsize推荐21
res = np.hstack((img, dst))
cv2.imshow('img', res)
cv2.waitKey(0)
# 可以手动noise加噪音,再去噪音看看算法有多强
文章图片
#图像修补
需要用画图软件画mask,都已经打开ps了我还要你何用
Haar级联检测
# opencv中有现成训练好的检测器xml,并且自带了训练器和检测器
a = cv2.data.haarcascades# 所有级联检测器所在的目录
print(a)
print([i for i in os.listdir(a) if i.startswith('haar')])
haarequ = ['haarcascade_frontalface_default.xml', 'haarcascade_eye.xml',
'haarcascade_smile.xml', 'haarcascade_profileface.xml', 'haarcascade_frontalcatface.xml',
'haarcascade_fullbody.xml', 'haarcascade_lowerbody.xml', 'haarcascade_upperbody.xml']
# 0123 4567
# 面 眼 笑 * * 全身 下肢 上身
cas = cv2.CascadeClassifier(a + haarequ[0])#
cas1 = cv2.CascadeClassifier(a + haarequ[1])
img = cv2.imread('16.jpg')
print(img.shape)
# img=img[100:600,240:600]
# img=cv2.resize(img,(1100,700))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 如果检测面部,它会返回所在矩形区域Rect(x,y,w,h),
# 如果获得,就可以创建一个ROI并进行眼部检测
faces = cas.detectMultiScale(gray, 1.1, 5)
# scaleFactor比例(标定)因子,minNeighbors最小临近
print(faces)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
roi = gray[y:y + h, x:x + w]
roic = img[y:y + h, x:x + w]
eyes = cas1.detectMultiScale(roi, 1.1, 2)
# 默认参数,毕竟眼睛太小,容易出问题
# 如果不用面部ROI来做,很可能出错,
# 毕竟眼睛的像素区域小,其他地方很容易阈值有大的
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roic, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
文章图片
文章图片
文章图片
二、视频分析 基本操作
# 使用摄像头捕获实时图像
cap = cv2.VideoCapture(0)
while 1:
# Capture frame-by-frame
ret, frame = cap.read()
print(ret,frame)# False None 是否有东西
# Our operations on the frame come here
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Display the resulting frame
cv2.imshow('frame',gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()# 从文件中播放视频,和保存视频
# 你只需要把设备索引号改成视频文件的名字。在播放每一帧时,使用 cv2.waiKey() 设置适当的持续时间
cap = cv2.VideoCapture('gg.mp4')
# fourcc = cv2.VideoWriter_fourcc(*'mp4v')
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('ggg.avi',fourcc, 20.0, (640,480))
while 1:
# while(cap.isOpened()):
ret, frame = cap.read()
print(ret,frame)
if ret==True:
frame = cv2.flip(frame,0)
# write the flipped frame
out.write(frame)
cv2.imshow('frame',frame[::-1])
cv2.waitKey(1)
else:
break
# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()
目标跟踪(Meanshift、Camshift)
视频追踪Meanshift和Camshift
meanshift原理:Meanshift 算法的基本原理是和很简单的。假设我们有一堆点(比如直方图反向投影得到的点),和一个小的圆形窗口,我们要完成的任务就是将这个窗口移动到最大灰度密度处(或者是点最多的地方)
一个迭代的步骤,即先算出当前点的偏移均值,移动该点到其偏移均值,然后以此为新的起始点,继续移动,直到满足一定的条件结束。
有点像kmeans聚类,一直移动到圆窗口包含像素值的和最大的地方
meanshift算法:简单,迭代次数少,但无法解决目标的遮挡问题并且不能适应运动目标的的形状和大小变化。
meanshift的一个问题,窗口的大小是固定的,而汽车由远及近(在视觉上)是一个逐渐变大的过程,固定的窗口是不合适的,所以后来就优化成了camshift
camshift算法全称“continuously adaptive mean-shift”(连续自适应meanshift算法),是对meanshift算法的改进算法,可随着跟踪目标的大小变化实时调整搜索窗口的大小,具有较好的跟踪效果。
Camshift算法首先应用meanshift,一旦meanshift收敛,它就会更新窗口的大小,还计算最佳拟合椭圆的方向,从而根据目标的位置和大小更新搜索窗口。
Camshift在opencv中实现时,只需要将上述的meanshift改为camshaft函数即可
在绘制矩形框时有所不同,meanshift是直接画出矩形框,而camshift则是根据求出的点来进行绘制的。
camshift算法:可适应运动目标的大小形状的改变,具有较好的跟踪效果,但当背景色和目标颜色接近时,容易使目标的区域变大,最终有可能导致目标跟踪丢失。
cap = cv2.VideoCapture('gg.mp4')
# take first frame of the video
ret,frame = cap.read()
# setup initial location of window
r,h,c,w = 460,140,620,165 # 需要提供窗口的起始位置
track_window = (c,r,w,h)
# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
# exit()
while 1:
ret ,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)# meanshift以下3行代码
# apply meanshift to get the new location
ret, track_window = cv2.meanShift(dst, track_window, term_crit)
# Draw it on image
x,y,w,h = track_window
img2 = cv2.rectangle(frame, (x,y), (x+w,y+h), 255,2)# # camshift以下3行代码
# # apply meanshift to get the new location
# ret, track_window = cv2.CamShift(dst, track_window, term_crit)
# # Draw it on image
# pts = cv2.boxPoints(ret)
# pts = np.int0(pts)
# img2 = cv2.polylines(frame, [pts], True, 255, 2)cv2.imshow('img2',img2)
k = cv2.waitKey(60) & 0xff
if k == 27:
break
else:
cv2.imwrite(chr(k)+".jpg",img2)
else:
break
cv2.destroyAllWindows()
cap.release()
文章图片
光流
文章图片
文章图片
文章图片
文章图片
# coding=utf-8
import numpy as np
import cv2
cap = cv2.VideoCapture('video.mp4')
#params for ShiTomasi corner detection角点检测的参数
feature_params = dict(maxCorners=100,
qualityLevel=0.3,
minDistance=7,
blockSize=7)# Parameters for lucas kanade optical flow
# maxLevel 为使用的图像金字塔层数
lk_params = dict(winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))# 创建随机颜色
color = np.random.randint(0, 255, (100, 3))# 拍摄第一帧并在其中找到角点
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)# 角点检测# 创建一个用于绘图的mask
mask = np.zeros_like(old_frame)while(1):
ret, frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 计算光流
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # 参数很多,详见源代码注释# 选择较好的点
good_new = p1[st == 1]
good_old = p0[st == 1]# 画曲线
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1)
img = cv2.add(frame, mask)cv2.imshow('frame', img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break# 更新上一帧和前一点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)cv2.destroyAllWindows()
cap.release()
文章图片
文章图片
# coding=utf-8
import cv2
import numpy as npcap = cv2.VideoCapture("video.mp4")
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255while(1):
ret, frame2 = cap.read()
next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])# 计算二维矢量的大小和角度
hsv[..., 0] = ang*180/np.pi/2
hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)cv2.imshow('frame2', bgr)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
cv2.imwrite('opticalfb.png', frame2)
cv2.imwrite('opticalhsv.png', bgr)
prvs = nextcap.release()
cv2.destroyAllWindows()
文章图片
# coding=utf-8
import numpy as np
import cv2# 对两个图片计算光流轨迹
f1='08120200_EVB0086.png'
f2='08120245_EVB0086.png'
ff1,ff2=cv2.imread(f1),cv2.imread(f2)prvs = cv2.cvtColor(ff1,cv2.COLOR_BGR2GRAY)
next = cv2.cvtColor(ff2,cv2.COLOR_BGR2GRAY)step=10
# 使用Gunnar Farneback算法计算密集光流
flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
print(flow[-1][-1])
# print(flow[...,0])# U
# print(flow[...,1][::10,::10].shape)# 等间隔取值 V# 绘制轨迹线
h, w = next.shape[:2]
y, x = np.mgrid[step / 2:h:step, step / 2:w:step].reshape(2, -1).astype(int)
fx, fy = flow[y, x].T
lines = np.vstack([x, y, x + fx, y + fy]).T.reshape(-1, 2, 2)
lines = np.int32(lines)line = []
for l in lines:
if l[0][0] - l[1][0] > 3 or l[0][1] - l[1][1] > 3:# 阈值为3
line.append(l)cv2.polylines(ff2, line, 0, (0, 255, 255))
cv2.imshow('flow', ff2)
cv2.waitKey()
背景剪除(BackgroundSubtractorMOG、MOG2、GMG)
在很多基础应用中背景检出都是一个非常重要的步骤。例如顾客统计,使用一个静态摄像头来记录进入和离开房间的人数,或者是交通摄像头,需要提取交通工具的信息等。在所有的这些例子中,首先要将人或车单独提取出来。技术上来说,我们需要从静止的背景中提取移动的前景。
如果你有一张背景(仅有背景不含前景)图像,比如没有顾客的房间,没有交通工具的道路等,那就好办了。我们只需要在新的图像中减去背景就可以得到前景对象了。但是在大多数情况下,我们没有这样的(背景)图像,所以我们需要从我们有的图像中提取背景。如果图像中的交通工具还有影子的话,那这个工作就更难了,因为影子也在移动,仅仅使用减法会把影子也当成前景。真是一件很复杂的事情。
为了实现这个目的科学家们已经提出了几种算法,OpenCV中已经包含了其中三种比较容易使用的方法
BackgroundSubtractorMOG是以混合高斯模型为基础的前景/背景分割算法
它使用 K(K=3 或 5)个高斯分布混合对背景像素进行建模。使用这些颜色(在整个视频中)存在时间的长短作为混合的权重。背景的颜色一般持续的时间最长,而且更加静止
因为背景建模是基于时间序列的,因此每一个像素点所在的位置在整个时间序列中就会有很多值,从而构成一个分布。
BackgroundSubtractorMOG2对上个的优化是它为每一个像素选择一个合适数目的高斯分布,这样就会对由于亮度等发生变化引起的场景变化产生更好的适应。
BackgroundSubtractorGMG此算法结合了静态背景图像估计和每个像素的贝叶斯分割。它使用前面很少的图像(默认为前 120 帧)进行背景建模。使用了概率前景估计算法(使用贝叶斯估计鉴定前景)。这是一种自适应的估计,新观察到的对象比旧的对象具有更高的权重,从而对光照变化产生适应。一些形态学操作如开运算闭运算等被用来除去不需要的噪音。在前几帧图像中你会得到一个黑色窗口。
对结果进行形态学开运算对与去除噪声很有帮助。
cap = cv2.VideoCapture('gg2.mp4')
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1):
ret, frame = cap.read()
fgmask = fgbg.apply(frame)
cv2.imshow('frame',fgmask)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()# cv4.0里没有MOG和GMG
# cap = cv2.VideoCapture('vtest.avi')
# kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
# fgbg = cv2.createBackgroundSubtractorGMG()
# while(1):
#ret, frame = cap.read()
#fgmask = fgbg.apply(frame)
#fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
#cv2.imshow('frame',fgmask)
#k = cv2.waitKey(30) & 0xff
#if k == 27:
#break
# cap.release()
# cv2.destroyAllWindows()
文章图片
三、摄像机标定和3D重构 摄像机标定
今天的低价单孔摄像机(照相机)会给图像带来很多畸变。畸变主要有两
种:径向畸变和切想畸变。
径向畸变:有凹进凸出的
切向畸变:这是由于透镜与成像平面不可能绝对平行造成的。这种畸变会造成图像中的某些点看上去的位置会比我们认为的位置要近一些
我们还需要找到一些信息,比如摄像机的内部和外部参数。内部参数是摄像机特异的。它包括的信息有焦距(fx, fy),光学中心(cx, cy)等。这也被称为摄像机矩阵。它完全取决于摄像机自身,只需要计算一次,以后就可以已知使用了
# 摄像机标定
import glob
# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (7,6),None)
# If found, add object points, image points (after refining them)
if ret == True:
objpoints.append(objp)
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
imgpoints.append(corners2)
# Draw and display the corners
img = cv2.drawChessboardCorners(img, (7,6), corners2,ret)
cv2.imshow('img',img)
cv2.waitKey(500)
cv2.destroyAllWindows()
文章图片
姿势估计
摄像机标定中,我们已经得到了摄像机矩阵,畸变系数等。有
了这些信息我们就可以估计图像中图案的姿势,比如目标对象是如何摆放,如
何旋转等。
# 姿势估计
# 在先前校准结果中加载相机矩阵和失真系数
with np.load('B.npz') as X:
mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]# 创建一个函数,绘制,该函数将棋盘上的角(使用cv.findChessboardCorners()获得)和轴点绘制为3D轴。
def draw(img, corners, imgpts):
corner = tuple(corners[0].ravel())
img = cv2.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5)
img = cv2.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5)
img = cv2.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5)
return img# 创建终止条件,对象点(棋盘上角的3D点)和轴点。 轴点是3D空间中用于绘制轴的点。 绘制长度为3的轴。
# X轴从(0,0,0)绘制为(3,0,0),对于Y轴。 对于Z轴,从(0,0,0)绘制为(0,0,-3)。 负号表示它被拉向相机。criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)# 加载每个图像。搜索7x6网格。如果找到,就使用子角像素对其进行优化。然后使用函数cv.solvePnPRansac()计算旋转和平移。
# 一旦有了这些变换矩阵,就可以使用它们将轴点投影到图像平面上。
# 简而言之,在图像平面上找到与3D空间中(3,0,0),(0,3,0),(0,0,3)中的每一个相对应的点。一旦获得它们,就可以使用draw()函数从第一个角到这些点中的每个点绘制线条for fname in glob.glob('left*.jpg'):
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (7,6),None)
if ret == True:
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 找到旋转和平移矢量。
ret,rvecs, tvecs = cv2.solvePnP(objp, corners2, mtx, dist)
# 将3D点投影到图像平面
imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
img = draw(img,corners2,imgpts)
cv2.imshow('img',img)
k = cv2.waitKey(0) & 0xFF
if k == ord('s'):
cv2.imwrite(fname[:6]+'.png', img)
cv2.destroyAllWindows()
文章图片
对极几何
对极几何(Epipolar Geometry)
在我们使用针孔相机时,我们会丢失大量重要的信心,比如说图像的深度,或者说图像上的点和摄像机的距离,因这是一个从 3D 到 2D 的转换。因此一个重要的问题就产生了,使用这样的摄像机我们能否计算除深度信息呢?答案就是使用多个相机。我们的眼睛就是这样工作的,使用两个摄像机(两个眼睛),这被称为立体视觉。
# 对极几何
# 首先需要在两个图像之间找到尽可能多的匹配项以找到基础矩阵。为此,将SIFT描述符与基于FLANN的匹配器和比率测试结合使用
img1 = cv2.imread('left.jpg', 0)
img2 = cv2.imread('right.jpg', 0)sift = cv2.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)good = []
pts1 = []
pts2 = []
# ratio test as per low;
s paper
for i, (m, n) in enumerate(matches):
if m.distance < 0.8 * n.distance:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)# 现在,获得了两张图片的最佳匹配列表,基于此找到基础矩阵
pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_LMEDS)# we select only inlier points
pts1 = pts1[mask.ravel() == 1]
pts2 = pts2[mask.ravel() == 1]# 接下来,找到对应的Epilines。在第二张图像上绘制与第一张图像中的点相对应的Epilines。因此,定义了一个新函数来在图像上绘制这些线条。
def drawlines(img1, img2, lines, pts1, pts2):
'''img1 - image on which we draw the epilines for the points in img2
lines - corresponding epilines '''
r, c = img1.shape
img1 = cv2.cvtColor(img1, cv2.COLOR_GRAY2BGR)
img2 = cv2.cvtColor(img2, cv2.COLOR_GRAY2BGR)for r, pt1, pt2 in zip(lines, pts1, pts2):
color = tuple(np.random.randint(0, 255, 3).tolist())
x0, y0 = map(int, [0, -r[2] / r[1]])
x1, y1 = map(int, [c, -(r[2] + r[0] * c) / r[1]])
img1 = cv2.line(img1, (x0, y0), (x1, y1), color, 1)
img1 = cv2.circle(img1, tuple(pt1), 5, color, -1)
img2 = cv2.circle(img2, tuple(pt2), 5, color, -1)
return img1, img2# 现在,我们在两个图像中都找到了Epiline并将其绘制。
# Find epilines corresponding to points in right image (second image) and
# drawing its lines on left image
lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F)
lines1 = lines1.reshape(-1, 3)
img5, img6 = drawlines(img1, img2, lines1, pts1, pts2)
# Find epilines corresponding to points in left image (first image) and
# drawing its lines on right image
lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1, 1, 2), 1, F)
lines2 = lines2.reshape(-1, 3)
img3, img4 = drawlines(img2, img1, lines2, pts2, pts1)
plt.subplot(121)
plt.imshow(img5)
plt.subplot(122)
plt.imshow(img3)
plt.show()
# 结果点都收敛在的那个汇合点就是极点
文章图片
深度地图
如果同一场景有两幅图像的话我们在直觉上就可以获得图像的深度信息
文章图片
# 深度地图
img1 = cv2.imread('2.jpg',0)
img2 = cv2.imread('1.jpg',0)
img2=cv2.resize(img2,img1.shape[::-1])
print(img1.shape,img2.shape)
stereo = cv2.StereoBM_create(numDisparities=16, blockSize=17)
disparity = stereo.compute(img1,img2)
plt.imshow(disparity,'gray')
plt.show()
【算法|opencvcv4.0】
文章图片
推荐阅读
- 数据分析|Python数据分析29——seaborn可视化(五)之琴形图
- 「Python」面向对象封装案例1——小夏爱跑步、案例扩展(多个对象之间属性互不干扰)
- python|安装pytorch
- 深度学习|【深度学习-吴恩达】L1-4 深层神经网络
- 深度学习|2. 刘二大人《PyTorch深度学习实践》作业--梯度下降
- 连续学习|图解连续学习中的蓄水池抽样算法(The Illustrated Reservoir sampling)
- 我的教程|Python 快速规范代码
- 数据结构和算法|数据结构和算法基础(6)——常用十种算法
- 数据结构和算法|数据结构和算法基础(2)——递归