opencv图像操作
opencv库
import cv2
读取图像
-
cv2.imread(img_path) #cv2.IMREAD_COLOR:彩色图像 #cv2.IMREAD_GRAYSCALE:灰色图像
- OpenCV 中彩色图像使用 BGR 格式,而 PIL、PyQt、matplotlib 等库使用的是 RGB 格式。
展示图片
cv2.imshow('img_name',img)
- 可以创建多个窗口
- 读出来的图像是numpy.ndarray格式
- 一般展示图像函数
-
def cv_show(name,img): cv2.imshow(name,img) cv2.waitKey(0) #括号中为窗口展示的时间,单位是毫秒,0表示按下任意键关闭 cv2.destroyAllWindows() #括号中为窗口展示的时间
读取视频
cv2.VideoCapture(video_path)
-
检查是否正确打开
-
if vc.isOpened(): open,frame=vc.read() else: open=False
-
在正确打开时展示视频
-
while open: ret,frame=cv.read() gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) cv2.imshow('result',gray) if cv2.waitKey(10) & 0xFF==27: break cv.release() cv2.destroyAllWindows()
基本操作
截取图像的部分
-
test=img[0:200,0:200]
颜色通道提取
-
b,g,r=cv2.split(img)
边界填充
-
cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE) # BORDER_REPLICATE:复制法,复制最边缘像素 # BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制 # BORDER_REFLECT_101:反射法,以最边缘像素为轴 # BORDER_WRAP:外包装法 # BORDER_CONSTANT:常量法
数值计算
- 直接相加:超过255的部分为 num-255-1
- img=inmg+常数
- img=img1+img2
- 函数相加:超过255的部分取255
- cv2.add(img1,img2)
图像融合
-
res=cv2.addWeighted(img1,a,img2,b,c) #其中生成的图像为f=ax+by+c,a和b为0-1之间的数
形态学操作
腐蚀操作
-
作用:将图像的边界点消除,是图像沿着边界向内收缩,也可以将小于指定结构体的部分去除,可除倒刺
-
kernel=np.ones((5,5),np.uint8) erosion=cv2.erode(img,kernel,iterations=1) # kernel为腐蚀核 # iterations为腐蚀次数
膨胀操作
-
与腐蚀操作相反,对图像的边界进行扩张
-
kernel=np.ones((5,5),np.uint8) dilate=cv2.dilate(img,kernel,iterations=1) # kernel为腐蚀核 # iterations为腐蚀次数
开运算
-
先腐蚀,再膨胀
-
可用于去噪、计数
-
kernel=np.ones((5,5),np.uint8) opening=cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) # cv2.MORPH_OPEN表示开运算
闭运算
-
先膨胀,再腐蚀
-
有助于关闭前景物体内部的小孔,或去除物体上的小黑点,还可以将不同的前景进行连接
-
kernel=np.ones((5,5),np.uint8) closing=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel) # cv2.MORPH_CLOSE表示开运算
梯度计算
-
梯度=膨胀-腐蚀
-
可得到原始图的轮廓
-
kernel=np.ones((5,5),np.uint8) gradient=cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
礼帽
-
原始图-开运算结果
-
可以获取图像的噪声信息,或者得到比原始图边缘信息更亮的边缘信息
-
kernel=np.ones((5,5),np.uint8) tophat=cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
黑帽
-
闭运算结果-原始图
-
获取图像内部的小孔,或前景中的小黑点,或者得到比原始图像的边缘更暗的边缘部分
-
kernel=np.ones((5,5),np.uint8) blackhat=cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
图像梯度
Sobel算子
G x = − 1 0 1 − 2 0 2 − 1 0 − 1 ∗ A , G y = − 1 − 2 − 1 0 0 0 1 2 1 ∗ A G_x=\begin{matrix} -1&0&1\\ -2&0&2\\ -1&0&-1\\ \end{matrix} * A,G_y=\begin{matrix} -1&-2&-1\\ 0&0&0\\ 1&2&1\\ \end{matrix} * A Gx=−1−2−100012−1∗A,Gy=−101−202−101∗A
-
dst=cv2.Sobel(src,ddepth,dx,dy,ksize)
- ddepth:图像的深度
- dx,dy表示水平和垂直方向
- ksize为Sobel算子的大小
-
# 深度一般为cv2.CV_64F # ksize为核算子,大小为3 # x方向求导,计算y方向梯度,获取垂直方向的边缘信息 sobel_x=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 计算绝对值 sobelx=cv2.convertScaleAbs(sobel_x) # y方向求导,计算x方向梯度,获取水平方向边缘信息 sobel_y=cv2.Soble(img,cv2.CV_64F,0,1,ksize=3) sobely=cv2.convertScaleAbs(sobel_y) # 当dx、dy均为1时求两个方向的梯度,获取两个方向的边缘信息 sobelxy=cv2.addWeighted(soblex,0.5,sobley,0.5,0)
Scharr算子
G x = − 3 0 3 − 10 0 10 − 3 0 3 ∗ A , G y = − 3 − 10 − 3 0 0 0 3 10 3 ∗ A G_x=\begin{matrix} -3&0&3\\ -10&0&10\\ -3&0&3\\ \end{matrix} * A,G_y=\begin{matrix} -3&-10&-3\\ 0&0&0\\ 3&10&3\\ \end{matrix} * A Gx=−3−10−30003103∗A,Gy=−303−10010−303∗A
- Sobel中ksize=-1就是Scharr算子
-
dx=Scharr(src, ddpeth, dx=1, dy=0) dy=Scharr(src, ddpeth, dx=0, dy=1) Scharrxy=cv2.addWeighted(dx,0.5, dy,0.5,0)
Laplacian算子
G = 0 1 0 1 − 4 1 0 1 0 G=\begin{matrix} 0&1&0\\ 1&-4&1\\ 0&1&0\\ \end{matrix} G=0101−41010
-
lap=cv2.Laplacian(img,cv2.CV_64F) lap=cv2.convertScaleAbs(lap)
图像平滑
噪声类型
- 椒盐噪声:黑、白像素随机出现
- 脉冲噪声:白像素随机出现
- 高斯噪声:噪声强度服从高斯分布
如何去噪声
- 线性滤波器:高斯噪声
- 中值滤波:椒盐噪声、脉冲噪声,原卷积核无值,放在图像中后把图像对应值取出从小到大排序,取中间值为该点值
均值滤波
-
用卷积核框起来的区域的均值来代替中心点元素
-
优缺点
- 优点:算法简单,计算速度快
- 缺点:在除噪声的同时去除了很多细节部分,将图像变得模糊
-
blur=cv2.blur(img,(3,3))
方框滤波
-
box=cv2.boxFilter(img,-1,(3,3),normalize=True) # -1表示保持原图像尺寸 # normalize=True表示要进行归一化处理,要用邻域像素值的和除以面积 # normalize=0时表示不需要进行归一化处理,直接使用邻域像素值的和。
- 是均值滤波的一般形式,可以选择是均值代替中心点元素还是和代替中心点元素
高斯滤波
- 是线性滤波器
- 对整个图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他点加权平均后得到
- 卷积核必须是奇数的
-
aussian=cv2.GaussianBlur(img,(3,3),1) # 1表示卷积核在水平方向上的标准差
中值滤波
- 卷积核框起来的区域数值从小到大排序,选中间值作为中心点元素的值
-
median=cv2.medianBlur(img,5) # 5表示卷积核大小
图像阈值
-
ret,dst=cv2.threshold(src,thresh,maxval,type)
- scr:输入图,只能输入单通道图像,一般为灰度图
- dst:输出图
- thresh:阈值
- maxval:当像素超过阈值时赋予的值,根据type决定
- type:二值化操作的类型,包含:
- cv.THRESH_BINARY:超过阈值部分取maxval,否则0
- cv.THRESH_BINARY_INV:THRESH_BINARY反转
- cv.THRESH_TRUNC:大于阈值部分设置为阈值,否则0
- cv.THRESH_TOZERO:大于阈值部分不变,否则0
- cv.THRESH_TOZERO_INV:HRESH_TOZERO反转
-
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV) ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO) ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV) # ret为使用的阈值 # thresh为使用阈值后的图像
边缘检测
Canny边缘检测
-
v1=cv2.Canny(img,minVal,maxVal) # minVal、maxVal为大小阈值
-
1、利用高斯一阶偏导核卷积
-
2、计算每个点的梯度幅值和方向
-
3、非极大值抑制
-
4、连续与阈值
- 定义高、低阈值
- 高开始边缘曲线,低连续曲线
非极大值抑制
- IOU–交并比
- 表示了bounding box 与 ground truth 的重叠度IOU=Area(A∩B)/Area(A∪B)
- 算法流程
- (1)将所有框的得分排序,选中最高分及其对应的框 ;
- (2)遍历其余的框,如果和当前最高分框的重叠面积(IOU)大于一定阈值,我们就将框删除。(为什么要删除,是因为超过设定阈值,认为两个框的里面的物体属于同一个类别,比如都属于狗这个类别。我们只需要留下一个类别的可能性框图即可;
- (3)从未处理的框中继续选一个得分最高的,重复上述过程。
双阈值检测
- 梯度值>maxval:处理为边界
- minval<梯度值<maxval:连有边界则保留,否则舍弃
- 梯度值<minval:舍弃
图像轮廓
查找轮廓
-
cv2.findContours(img,mode,method) # 为了轮廓的准确性,需要先对图像进行二值化处理 gray=cv2.cvtColor(img,cv2.COLOR_BGRA2GRAY) ret,thresh=cv2.threshold(gray,127,255,cv2.THRESH_BINARY) contours,hierarchy=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) # contours,hierarchy为返回图像的轮廓和层级 ''' - mode:轮廓检索模式 - RETR_EXTERNAL:只检索最外面的轮廓 - RETR_LIST:检索所有的轮廓,并将其保存到一条链表中 - RETR_CCOMP:检索所有的轮廓,并将它们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界 - RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次 - method:轮廓逼近方法 - CHAIN_APPROX_NONE:以freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列) - CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是函数只保留他们的终点部分 '''
绘制轮廓
-
#传入绘制图像、轮廓、轮廓索引、颜色模式、线条厚度 res=cv2.drawContours(draw_img,contours,-1,(0,0,255),2) #-1这个位置为轮廓顺序标号,-1代表绘制所有轮廓,2表示线条宽度
轮廓特征–面积和周长
-
# 选定最外层的轮廓 cnt=contours[0] # 面积计算 area=cv2.contourArea(cnt) # 周长计算 length=cv2.arcLength(cnt,True) # 第二个参数指定形状是闭合轮廓(True)还是曲线。
轮廓近似
-
epsilon = 0.1*cv.arcLength(cnt,True) approx = cv.approxPolyDP(cnt,epsilon,True) res=cv2.drawContours(draw_img,[approx],-1,(0,0,255),2)
模版匹配
-
从左到右,从下到上
-
计算相似度的方法:
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关,对应数值为0
- TM_CCORR:计算相关性,计算出来的值越大,越相关,对应数值为2
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关,对应数值4
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关,对应数值为1
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关,对应数值为3
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关,对应数值为5
-
如果原图形大小是A✖️B,模版大小是a✖️b,则输出结果的矩阵是(A-a+1)✖️(B-b+1)
-
result=cv2.matchTemplate(img,templ(模版图),method) # 返回的是一个结果集,类型是单通道32浮点型,由每个位置的比较结果组成
查找最值
-
minVal,maxVal,minLoc,maxLoc=cv2.minMaxLoc(result) ''' minVal:最小值,没有最小值返回NULL maxVal:最大值,没有最大值返回NULL minLoc:最小值位置,没有最小值返回NULL maxLoc:最大值位置,没有最大值返回NULL ''' w,h=img[:2]#w,h是模版图的宽度和高度 # 如果方法是TM_SQDIFF或TM_SQDIFF_NORMED,则取最小值 if method 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)
多模版匹配
-
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img_rgb = cv.imread('mario.png') img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY) template = cv.imread('mario_coin.png',0) w, h = template.shape[::-1] res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED) threshold = 0.8 loc = np.where( res >= threshold) for pt in zip(*loc[::-1]): cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2) cv.imwrite('res.png',img_rgb)
图像金字塔
-
高斯金字塔
- 向下采样方法:缩小,从金字塔底部到最高处
- 通过高斯核卷积
- 将所有偶数行列去除
- 向上采样方法:放大,从金字塔顶部到最低处
- 将图像在每个方向上扩大为原来的2倍,新增行列填充0
- 通过之前✖️4的内核卷积
- 向下采样方法:缩小,从金字塔底部到最高处
-
拉普拉斯金字塔
- 最终图=原始图-向上采样(向下采样)
- 低通滤波
- 缩小尺寸
- 放大尺寸
- 图像相减
- 最终图=原始图-向上采样(向下采样)
-
up=cv2.pyrUp(img) down=cv2.pyrDown(img) lap=img-up/down