图像基本操作
cv2.imread(‘待读入图像’)—读入图像
cv2.IMREAD_COLOR:彩色图像
cv2.IMREAD_GRAYSCALE:灰度图像
注:opencv读取的格式是BGR
cv2.cvtColor(img,方法) 转变图像
cv2.imshow(‘展示时图像显示的名称’,待展示图像)
cv2.waitKey(0) 等待时间,毫秒级,0表示任意键终止
cv2.destroyAllWindows()
cv2.imwrite(‘img.png’,img)
cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1。
如果是视频文件,直接指定好路径即可。
vc = cv2.VideoCapture('test.mp4')
# 检查是否打开正确
if vc.isOpened():
open, frame = vc.read()
else:
open = False
while open:
ret, frame = vc.read()
if frame is None:
break
if ret == True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('result', gray)
if cv2.waitKey(100) & 0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
注:视频是一帧一帧读取的,frame是一张张图片
截取部分图像数据
img=cv2.imread(‘img.jpg’)
cat=img[0:50,0:200]
cv_show(‘cat’,cat)
颜色通道的提取
b,g,r=cv2.split(img)
合并:img=cv2.merge((b,g,r))
只保留R
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,1] = 0
cv_show(‘R’,cur_img)
边界填充
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
BORDER_CONSTANT:常量法,常数值填充。
top_size,bottom_size,left_size,right_size = (50,50,50,50)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)
图像融合:res = cv2.addWeighted(img1, 0.4, img2, 0.6, 0)
图像阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)
src: 输入图,只能输入单通道图像,通常来说为灰度图
dst: 输出图
thresh: 阈值
maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY;
cv2.THRESH_BINARY_INV;
cv2.THRESH_TRUNC;
cv2.THRESH_TOZERO;
cv2.THRESH_TOZERO_INV
cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0
cv2.THRESH_BINARY_INV THRESH_BINARY的反转
cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
图像平滑
均值滤波
简单的平均卷积操作
blur = cv2.blur(img, (3, 3))
方框滤波
基本和均值一样,可以选择归一化,否则容易越界
box = cv2.boxFilter(img,-1,(3,3), normalize=True)
高斯滤波
#高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)
注:参数中,()里的是卷积大小
中值滤波
相当于用中值代替
median = cv2.medianBlur(img, 5)
多张图片展示方法
res = np.hstack((blur,aussian,median))
注:hstack水平展示,vstack竖直展示
形态学操作
形态学-腐蚀操作
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1) iteration:迭代次数 kernel:卷积核
作用:去毛刺
形态学-膨胀操作
kernel = np.ones((3,3),np.uint8)
dige_dilate = dilate(dige_erosion,kernel,iterations = 1)
开运算与闭运算
开:先腐蚀,再膨胀
闭:先膨胀,再腐蚀
梯度运算
梯度=膨胀-腐蚀
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
礼帽与黑帽
礼帽 = 原始输入-开运算结果
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
可用来找出毛刺
黑帽 = 闭运算-原始输入
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
图像梯度
dst = cv2.Sobel(img, ddepth, dx, dy, ksize)
ddepth:图像的深度
dx和dy分别表示水平和竖直方向
ksize是Sobel算子的大小
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) 注:cv2.CV_64F是使得结果能取负数,因为计算梯度时会出现负数
sobelx = cv2.convertScaleAbs(sobelx) 取梯度的绝对值
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) 进行梯度的求和
Canny边缘检测
- 使用高斯滤波器,以平滑图像,滤除噪声。
- 计算图像中每个像素点的梯度强度和方向。
- 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
- 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
- 通过抑制孤立的弱边缘最终完成边缘检测。
c1=cv2.Canny(img,80,150) 其中两个数字代表两个阈值
图像金字塔
up=cv2.pyrUp(img) 上采样
down=cv2.pyrDown(img) 下采样
down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
ll=img - down_up
图像轮廓
cv2.findContours(img,mode,method)
mode:轮廓检索模式
RETR_EXTERNAL :只检索最外面的轮廓;
RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法
CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
注:mode里常用RETR_TREE
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
#注意需要copy,要不原图会变。。。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
# 轮廓索引为-1表示显示所有轮廓,为一个正整数表示显示某一个轮廓
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)
轮廓近似
img = cv2.imread('contours2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
epsilon = 0.15*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
边界矩形
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
傅里叶变换
傅里叶变换的作用
高频:变化剧烈的灰度分量,例如边界
低频:变化缓慢的灰度分量,例如一片大海
因此,高频代表细节,低频代表主体
滤波
低通滤波器:只保留低频,会使得图像模糊
高通滤波器:只保留高频,会使得图像细节增强
opencv中主要就是cv2.dft()和cv2.idft(),输入图像需要先转换成np.float32 格式。
得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。
cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)。
直方图与模板匹配
cv2.calcHist(images,channels,mask,histSize,ranges)
images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一分的直方图的你就制作一个掩模图像并 使用它。
histSize:BIN 的数目。也应用中括号括来
ranges: 像素值范围常为 [0256]
plt.hist(img.ravel(),256); # 显示直方图
创建mask
mask = np.zeros(img.shape[:2], np.uint8)
print (mask.shape)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img, img, mask=mask)
equ = cv2.equalizeHist(img) # 直方图均衡
自适应直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
res_clahe = clahe.apply(img)
模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)
模板匹配方法
TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
TM_CCORR:计算相关性,计算出来的值越大,越相关
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
图像特征
harris
基本原理:设置一个窗口,在图上移动,如果是平坦的,像素值基本不变;如果是边界,一个方向变化大,另一个方向基本不变;如果是角点,则变化一定会大
函数调用:
cv2.cornerHarris()
img: 数据类型为 float32 的入图像
blockSize: 角点检测中指定区域的大小
ksize: Sobel求导中使用的窗口大小
k: 取值参数为 [0,04,0.06]
sift
高斯差分金字塔(DoG)
极值检测
需要比周围26个像素点都大才是极值点
之后再从这些极值点中找到真正的极值点,即关键点
使用Hessian矩阵消除边界响应
之后求出关键点周围点的梯度和,总共分为8个方向,取最大的为关键点的主方向,第二大的为辅方向,将坐标轴旋转为主方向的方向
作用:即使图片旋转,关键点的主方向不变,即旋转不变性
之后,为了找出两张图片上相同的关键点,需要有一个特征
关键点旋转后,以主方向为坐标轴方向,在关键周围取窗口并分为多个小块,每个小块为4*4大小,在每个小块上求8方向的梯度值,即为一个种子点
函数调用:
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(img_gray, None)
img = cv2.drawKeypoints(img_gray, kp, img)
注:sift常在灰度图上查找,先转为灰度图,再再原图上绘出
背景建模
帧差法
由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。
帧差法非常简单,但是会引入噪音和空洞问题
混合高斯模型
在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯的个数可以自适应。然后在测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。
背景的实际分布应当是多个高斯分布混合在一起,每个高斯模型也可以带有权重
混合高斯模型学习方法
1、首先初始化每个高斯模型矩阵参数。
2、取视频中T帧数据图像用来训练高斯混合模型。来了第一个像素之后用它来当做第一个高斯分布。
3、当后面来的像素值时,与前面已有的高斯的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则属于该分布,并对其进行参数更新。
4、如果下一次来的像素不满足当前高斯分布,用它来创建一个新的高斯分布。
混合高斯模型测试方法
在测试阶段,对新来像素点的值与混合高斯模型中的每一个均值进行比较,如果其差值在2倍的方差之间的话,则认为是背景,否则认为是前景。将前景赋值为255,背景赋值为0。这样就形成了一副前景二值图。
光流估计
光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。
想要使用光流估计,需要满足以下几个条件
1、亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
2、小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
3、空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
cv2.calcOpticalFlowPyrLK():
参数:1、prevImage 前一帧图像
2、nextImage 当前帧图像
3、prevPts 待跟踪的特征点向量
4、winSize 搜索窗口的大小
5、maxLevel 最大的金字塔层数
返回值:
1、nextPts 输出跟踪特征点向量
2、status 特征点是否找到,找到的状态为1,未找到的状态为0