【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程(附带课程课件资料+课件笔记)图像处理|深度学习人工智能计算机视觉python+AI_哔哩哔哩_bilibili
1、图像的读取与显示
img = cv2.imread(img, type)
* img:图片的路径
* type(可选):cv2.IMREAD_COLOR 彩色图像
cv2.IMREAD_GRAYSCALE 灰度图像
* 返回值
* 图像数据
ref = cv2.cvtColor(img, type)
* img:图片的路径
* type:cv2.COLOR_BGR2GRAY 灰度图像
* 返回值
* 图像数据
-
图像的读取
img = cv2.imread('01_Picture/01_cat.jpg') print(type(img)) # img 的类型为 numpy.ndarray 类型 print(img) # uint8 的取值范围在 0-255 之间 -
图像的显示
# opencv 读取并用 opencv 自带的展示函数不需要进行通道转换,但 opencv 读取后用其他库展示图片需要通道转换 # 图像显示时,可以创建多个窗口 # 第一个入口参数为展示图像窗口的名字 # 第二个入口参数为展示图像窗口中所展示的图像 img = cv2.imread('01_Picture/01_cat.jpg') cv2.imshow('image_cat',img) # 等待时间,毫秒级,0表示任意键终止,5000ms表示5s cv2.waitKey(5000) # 销毁图像窗口 cv2.destroyAllWindows() -
视频的读取并逐帧转成灰度图显示
vc = cv2.VideoCapture('02_Video/00_Scenery.mp4') if vc.isOpened(): # 检查是否打开正确 open, frame = vc.read() # 这里的 vc.read() 相当于读取图像的第一帧 # 若循环不断的执行 vc.read,则不断的读取第二帧、第三帧.... print(open) # 正常打开时,open会返回 True cv_show('image_scenery',frame) else: open = False while open: # 如果正常打开,则不停循环读取,这里可替换成 i 值,来确定读取 i 帧 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(10) & 0xFF == 27: # cv2.waitKey(10)为等多少时间执行下一帧,0xFF为退出键ESC break vc.release() # release()完成与 open() 相反的工作.释放 open() 向内核申请的所有资源 cv2.destroyAllWindows() # 销毁所有窗口
2、灰度图的处理与保存
-
读取灰度图
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE) # 读取灰度图 img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR) # 读取BGR图 print('type(img_gray):',type(img_gray)) print('img_gray.size: ',img_gray.size) # 414 × 500 = 20700 print('img_gray.dtype:',img_gray.dtype) print('img_gray.shape:',img_gray.shape) # 只有一个通道 -
保存图片
cv2.imwrite('01_Picture/02_cat_gray.jpg',img_gray) # 保存图片
3、ROI区域
-
位置提取ROI
img = cv2.imread('01_Picture/01_cat.jpg') cat = img[0:200,0:200] # 选择图片感兴趣的区域 cv_show('cat',cat) -
分离BGR通道
img = cv2.imread('01_Picture/01_cat.jpg') b,g,r = cv2.split(img) cv_show('cat_b',b) # 显示B通道灰度图 print('b.shape:',b.shape) # B通道,单通道,灰度图 cv_show('cat_g',g) print('g.shape:',g.shape) # G通道,单通道,灰度图 cv_show('cat_r',r) print('r.shape:',r.shape) # R通道,单通道,灰度图 img = cv2.merge((b,g,r)) print('img.shape:',img.shape) # 3 通道,彩色图 -
只展示R通道
img = cv2.imread('01_Picture/01_cat.jpg') b,g,r = cv2.split(img) img = cv2.merge((b,g,r)) cur_img = img.copy() cur_img[:,:,0] = 0 cur_img[:,:,1] = 0 cv_show('R',cur_img) -
只展示G通道
img = cv2.imread('01_Picture/01_cat.jpg') cur_img = img.copy() cur_img[:,:,0] = 0 cur_img[:,:,2] = 0 cv_show('G',cur_img) -
只展示B通道
img = cv2.imread('01_Picture/01_cat.jpg') cur_img = img.copy() cur_img[:,:,1] = 0 cur_img[:,:,2] = 0 cv_show('B',cur_img)
4、边界填充
① 边界填充就是对图像进行一些变换,让原始图像进行扩大。
② 边界填充的入口参数:
- BORDER_REPLICATE:复制法,也就是复制最边缘像素。
- BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
- BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
- BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
- BORDER_CONSTANT:常量法,常数值填充。
img = cv2.imread('01_Picture/01_cat.jpg')
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,borderType=cv2.BORDER_WRAP)
# 方式五:常量法
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)
5、数值越界处理
img_cat = cv2.imread('01_Picture/01_cat.jpg')
img_dog = cv2.imread('01_Picture/03_dog.jpg')
img_cat2 = img_cat + 10 # 将 img_cat 矩阵中每一个值都加 10
print(img_cat[:5,:,0])
print(img_cat2[:5,:,0])
print((img_cat+img_cat2)[:5,:,0]) # 0-255 若相加越界后 294 用 294%256 获得余数 38
cv2.add(img_cat,img_cat2)[:5,0] # cv2.add 是越界后取最大值 255
6、图像融合与缩放
-
图像融合
-
不同长宽的图像不能进行融合操作
img_cat = cv2.imread('01_Picture/01_cat.jpg') img_dog = cv2.imread('01_Picture/03_dog.jpg') print(img_cat.shape) print(img_dog.shape) img_cat+img_dog # 不同数据大小不能执行数值计算操作 -
调整图像大小
print(img_cat.shape) print(img_dog.shape) img_dog = cv2.resize(img_dog,(500,414)) img_dog.shape -
图像融合
res = cv2.addWeighted(img_cat,0.4,img_dog,0.6,0) # img_cat 的权重为 0.4,img_dog 的权重为 0.6, 0 代表整个图像添加的系数 print(img_dog.shape) plt.imshow(res)
-
-
图像的缩放
-
倍数缩放
img = cv2.imread('01_Picture/01_cat.jpg') res = cv2.resize(img,(0,0),fx=3,fy=1) # (0,0)表示不确定具体值,fx=3 相当于行像素 x 乘 3,fy=1 相当于 y 乘 1 plt.imshow(res) -
等比例缩放
res = cv2.resize(img,(0,0),fx=1.5,fy=1.5) # 同比例放缩 plt.imshow(res)
-
7、图像的阈值处理
ret, dst = cv2.threshold(src, thresh, maxval, type)
* src:输入图,只能输入单通道图像,通常来说为灰度图
* thresh:阈值
* maxval:当像素值超过了阈值 ( 或者小于阈值,根据 type 来决定 ),所赋予的值
* type:二值化操作的类型,包含以下5种类型:
* cv2.THRESH_BINARY 超过阈值部分取maxval ( 最大值 ),否则取0 即亮 一点的全为白,黑一点的全为黑(二值化处理)
* cv2.THRESH_BINARY_INV THRESH_BINARY方法的反转
* cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
* cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
* cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
* 返回值:
* dst:转换后的输出图
* ret:阈值
img = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR)
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE)
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
print(ret)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # THRESH_BINARY_INV 相对THRESH_BINARY 黑的变成白的,白的变成黑的
print(ret)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
print(ret)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
print(ret)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
print(ret)
8、图像平滑处理
-
均值滤波
# 均值滤波 # 简单的平均卷积操作,方框中的值相加,取平均,替换掉中心204的值 blur = cv2.blur(img,(3,3)) # (3,3) 为卷积核的大小,通常情况核都是奇数 3、5、7 cv2.imshow('blur',blur) cv2.waitKey(0) cv2.destroyAllWindows() -
方框滤波
# 在 Python 中 -1 表示自适应填充对应的值,这里的 -1 表示与颜色通道数自适应一样 box = cv2.boxFilter(img,-1,(3,3),normalize=True) # 方框滤波如果做归一化,得到的结果和均值滤波一模一样 cv2.imshow('box',box) cv2.waitKey(0) cv2.destroyAllWindows() box = cv2.boxFilter(img,-1,(3,3),normalize=False) # 越界的值取 255 cv2.imshow('box',box) cv2.waitKey(0) cv2.destroyAllWindows() -
高斯滤波
# 高斯函数,越接近均值时,它的概率越大。 # 离中心值越近的,它的权重越大,离中心值越远的,它的权重越小。 aussian = cv2.GaussianBlur(img,(5,5),1) cv2.imshow('aussian',aussian) cv2.waitKey(0) cv2.destroyAllWindows() -
中值滤波
# 中值滤波 # 排序后拿中值替代中间元素值的大小 median = cv2.medianBlur(img,5) cv2.imshow('median',median) cv2.waitKey(0) cv2.destroyAllWindows() -
展示所有滤波后的图像
# 展示所有的 res = np.hstack((blur,aussian,median)) # 矩阵横着拼接 #res = np.vstack((blur,aussian,median)) # 矩阵竖着拼接 print(res) cv2.imshow('median vs average', res) cv2.waitKey(0) cv2.destroyAllWindows()
9、腐蚀和膨胀与开运算、闭运算、梯度运算与礼帽和黑帽
-
腐蚀操作
# 只要框里有黑色,中心点的值就变为黑色,即原来的白色被黑色腐蚀掉 kernel = np.ones((5,5),np.uint8) # 选取一个都是1的卷积核 erosion = cv2.erode(img,kernel,iterations=1) # 递归运行的次数 cv2.imshow('erosion',erosion) cv2.waitKey(0) cv2.destroyAllWindows() -
膨胀操作
# 先腐蚀 后膨胀,抵消腐蚀造成的损害 kernel = np.ones((3,3),np.uint8) dige_erosion = cv2.erode(img,kernel,iterations=1) cv2.imshow('erosion',dige_erosion) cv2.waitKey(0) cv2.destroyAllWindows() kernel = np.ones((3,3),np.uint8) dige_dilate = cv2.dilate(dige_erosion,kernel,iterations=1) cv2.imshow('dilate',dige_dilate) cv2.waitKey(0) cv2.destroyAllWindows() -
开运算
# 开:先腐蚀,再膨胀 img = cv2.imread('01_Picture/05_Dige.png') kernel = np.ones((5,5),np.uint8) opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) cv2.imshow('opening',opening) cv2.waitKey(0) cv2.destroyAllWindows() -
闭运算
# 闭:先膨胀,再腐蚀 img = cv2.imread('01_Picture/05_Dige.png') kernel = np.ones((5,5),np.uint8) closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel) cv2.imshow('closing',closing) cv2.waitKey(0) cv2.destroyAllWindows() -
梯度运算
# 梯度 = 腐蚀-膨胀 可以获取边界信息 pie = cv2.imread('01_Picture/06_pie.png') kernel = np.ones((7,7),np.uint8) dilate = cv2.dilate(pie,kernel,iterations=5) erosion = cv2.erode(pie,kernel,iterations=5) res = np.hstack((dilate,erosion)) cv2.imshow('res',res) cv2.waitKey(0) cv2.destroyAllWindows() gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel) # 进行梯度运算 cv2.imshow('gradient',gradient) cv2.waitKey(0) cv2.destroyAllWindows() -
礼帽
礼帽 = 原始输入-开运算
# 原始带刺,开运算不带刺,原始输入-开运算 = 刺 img = cv2.imread('01_Picture/05_Dige.png') kernel = np.ones((5,5),np.uint8) tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel) cv2.imshow('tophat',tophat) cv2.waitKey(0) cv2.destroyAllWindows() -
黑帽
黑帽 = 闭运算-原始输入
# 原始带刺,闭运算带刺并且比原始边界胖一点,闭运算-原始输入 = 原始整体 img = img = cv2.imread('01_Picture/05_Dige.png') kernel = np.ones((5,5),np.uint8) blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel) cv2.imshow('blackhat',blackhat) cv2.waitKey(0) cv2.destroyAllWindows()
10、Sobel算子、Scharr算子与Laplacian算子
-
Sobel算子

① Sobel算子函数:cv2.Sobel(src, ddepth, dx, dy, ksize),返回值为Sobel算子处理后的图像。
- ddepth:图像的深度
- dx 和 dy 分别表示水平和竖直方向
- ksize 是 Sobel 算子的大小
② 靠近最近点的左右和上下的权重最高,所以为±2。
img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE) cv_show(img,'img') img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE) sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 计算x方向的边界 sobelx = cv2.convertScaleAbs(sobelx) # 取绝对值 sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) # 计算y方向的边界 sobely = cv2.convertScaleAbs(sobely) # 取绝对值 sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) # 合并xy方向的边界图像 cv_show(sobelxy,'sobelxy') -
Scharr算子与Laplacian算子

Scharr算子对结果的差异更敏感一些。
① Laplacian算子用的是二阶导,对噪音点更敏感一些。
② 如果中心点是边界,它与周围像素点差异的幅度会较大,Laplacian算子根据此特点可以把边界识别出来。
# 不同算子的差异 img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE) sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) sobelx = cv2.convertScaleAbs(sobelx) sobely = cv2.convertScaleAbs(sobely) sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) scharrx = cv2.Scharr(img,cv2.CV_64F,1,0) scharry = cv2.Scharr(img,cv2.CV_64F,0,1) scharrx = cv2.convertScaleAbs(scharrx) scharry = cv2.convertScaleAbs(scharry) scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0) laplacian = cv2.Laplacian(img,cv2.CV_64F) # 没有 x、y,因为是求周围点的比较 laplacian = cv2.convertScaleAbs(laplacian) res = np.hstack((sobelxy,scharrxy,laplacian)) cv_show(res,'res')
11、Canny边缘检测
-
流程介绍
- 使用高斯滤波器,以平滑图像,滤除噪声。
- 计算图像中每个像素点的梯度强度和方向。
- 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
- 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
- 通过抑制孤立的弱边缘最终完成边缘检测。
-
梯度和方向

-
非极大值抑制
-
方法一:
① C 点的梯度和方向可以通过前一步算出来。
② C 点的梯度是不是一个极大值点,应该是去跟它的临近点去比较。
③ 利用 C 点梯度的方向,可以得到上面有一个交点 Q,下面有一个交点 Z,如果 C 点的梯度比 Q 和 Z 都大,那么 C 就是极大值点,其中 Q 和 Z 的梯度值通过线性差值法来计算。
④ 如果 C 的梯度是极大值点,那么 C 就是边缘点。否则 C 不是极大值点,就会被抑制。

-
方法二:
① 简单计算将像素点周围固定为八个像素,当梯度角度相交的点与哪个方向近,就哪个方向的两个点。
② 例如,梯度方向是 43° 就取上下两个像素来做极大值判断,如果梯度方向是 46°,就取左下、右上两个像素来做极大值判断。
③ 如下图所示,如果 A 的梯度值比 B 和 C 都要大,那么 A 就是边界,由于边界与梯度方向垂直,所以如下图所示黑色为边界。

-
-
双阈值检测
① C 在 minVal 与 maxVal 之间,是候选边界,若 C 的左右或上下两边连有 A,而 A 是边界,那么定义 C 也是边界。
② B 在 minVal 与 maxVal 之间,是候选边界,若B的左右或上下像素都不是边界,那么 B 就被舍弃,不定义为边界。

-
使用方式
def cv_show(img,name): cv2.imshow(name,img) cv2.waitKey() cv2.destroyAllWindows() img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE) # 第二个参数为minVal,第三个参数为maxVal 值越大代表越严格,梯度信息越少 v1 = cv2.Canny(img,80,150) v2 = cv2.Canny(img,50,100) res = np.hstack((v1,v2)) cv_show(res,'res')
12、图像金字塔
① 金字塔的底层是比较大,越往上越小,图像金字塔就是把图像组合成金字塔的形状。
② 图像金字塔可以做图像特征提取,做特征提取时有时可能不光对原始输入做特征提取,可能还会对好几层图像金字塔做特征提取。可能每一层特征提取的结果是不一样的,再把特征提取的结果总结在一起。
③ 常用的两种图像金字塔形式:
- 高斯金字塔
- 拉普拉斯金字塔
-
高斯金字塔
-
向下采样方法 ( 缩小 )

-
向上采样方法 ( 放大 )

-
使用方式
img = cv2.imread('01_Picture/09_AM.png') up = cv2.pyrUp(img) #上采样 up_down = cv2.pyrDown(up) #下采样 cv_show(np.hstack((img,up_down)),'up_down')
-
-
拉普拉斯金字塔
① 拉普拉斯金字塔的每一层图像尺寸不变。
② 拉普拉斯金字塔的每一层操作都是上一层处理后作为输入,该输入减去该输入缩小放大后的图像,获得该层的输出。

使用方式
img = cv2.imread('01_Picture/09_AM.png') domn = cv2.pyrDown(img) down_up = cv2.pyrUp(down) L_1 = img - down_up cv_show(L_1,'L_1') print(L_1.shape)
13、图像轮廓
-
轮廓绘制
① 边缘有一些零零散散的线段也可以当做边缘,反正梯度上下左右发生差异,就把它当做边缘了。
② 图像的轮廓必须是一个整体,不是零零散散的,而是连在一块的。
③ 图像轮廓函数:cv2.findContours(img,mode,method)
mode:轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓。
- RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中。
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界。
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。( 最常用 )
method:轮廓逼近方法
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,如下图左所示。所有其他方法输出多边形 ( 顶点的序列 ),如下图右所示。
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分,如下图右所示。
④ 为了更高的准确率,轮廓检测使用二值图像。
img = cv2.imread('01_Picture/10_contours.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 图像二值化 大于17的取255,小于127的取0 binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) # 轮廓检测 draw_img = img.copy() # 若不用拷贝后的,而是用原图画轮廓,则画轮廓图绘把原始的输入图像重写,覆盖掉 res = cv2.drawContours(draw_img,contours,3,(0,0,255),2) # 轮廓绘制 原始图像 轮廓层次 第几个轮廓-1为显示全部 绘制的轮廓的颜色 线条的宽度 cv_show(res,'res') -
轮廓近似
① 正常轮廓展示是最右边的图,但是当我们需要轮廓没有那么不规则,而是想要轮廓近似成规则的形状,这就叫轮廓近似,近似成下图中中间图像的轮廓。
② 一条呈抛物线的曲线的端点为 A、B 两点,取曲线上到直线 AB 距离最大的点,该点为 C 点,若 C 点到直线的距离小于设置的阈值,则可以把直线 AB 当做曲线的近似,若 C 点到直线的距离大于设置的阈值,那么曲线不能用直线 AB 来近似,而 AC 曲线可能用 AC 直线来代替、BC曲线可能用 BC 直线来代替。再通过阈值来判断是否可以代替。

-
轮廓近似展示
img = cv2.imread('01_Picture/11_contours2.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0 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.1 * cv2.arcLength(cnt,True) # 周长的百分比,这里用 0.1 的周长作阈值 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') -
外界矩形
img = cv2.imread('01_Picture/10_contours.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0 binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[6] x,y,w,h = cv2.boundingRect(cnt) # 可以得到矩形四个坐标点的相关信息 img = cv2(img,(x,y),(x+w,y+h),(0,255),2) cv_show(img,'img') -
外接圆
img = cv2.imread('01_Picture/10_contours.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0 binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[0] draw_img = img.copy() (x,y),redius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) redius = int(redius) img = cv2.circle(draw_img,center,redius,(0,255,0),2) cv_show(img,'img')
-
14、模板匹配
① 模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度(例如值127与值190的区别),这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。
② 假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)。
③ 模板匹配计算方式6种方式 ( 用归一化后的方式更好一些 ):
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
- TM_CCORR:计算相关性,计算出来的值越大,越相关。
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关。
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关。
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关。
模板匹配单个对象
import cv2 #opencv的缩写为cv2
import matplotlib.pyplot as plt # matplotlib库用于绘图展示
import numpy as np # numpy数值计算工具包
template = cv2.imread('01_Picture/12_Face.jpg',0) # 0 表示以灰度图方式读取
img = cv2.imread('01_Picture/13_Lena.jpg',0)
h, w = template.shape[:2] # 获得模板的宽和高
print(img.shape)
print(template.shape)
methods = ['cv2.TM_CCOEFF','cv2.TM_CCOEFF_NORMED','cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
print(res.shape) # 返回的矩阵大小 (A-a+1)x(B-b+1)
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res) # 返回模板匹配后最小值、最大值的位置
print(min_val) # cv2.TM_SQDIFF方法中,越小的值表示像素点的差异越小
print(max_val)
print(min_loc) # 当获得最小值对应的模板左上角的位置,加上模板自身的长、宽,可以在原图像中画出最匹配的区域
print(max_loc)
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth) # 提取字符串中的内容,不能用字符串的形式
print(method)
res = cv2.matchTemplate(img,template,method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果是平方差匹配 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)
# 画矩形
cv2.rectangle(img2,top_left,bottom_right,255,2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
plt.subplot(122),plt.imshow(img2,cmap='gray')
plt.xticks([]),plt.yticks([])
plt.suptitle(meth)
plt.show()
模板匹配多个对象
img_rgb = cv2.imread('01_Picture/14_Mario.jpg')
img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)
print('img_gray.shape:',img_gray.shape)
template = cv2.imread('01_Picture/15_Mario_coin.jpg',0)
print('template.shape:',template.shape)
h, w = template.shape[:2]
# res 是 result 的简称
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) # res 是返回每一个小块窗口得到的结果值
print('res.shape:',res.shape)
threshold = 0.8
# 取匹配程度大于 80% 的坐标
loc = np.where(res >= threshold) # np.where 使得返回 res 矩阵中值大于 0.8 的索引,即坐标
print('type(loc):',type(loc)) # loc 为元组类型
print('len(loc):',len(loc)) # loc 元组有两个值
print('len(loc[0]):',len(loc[0]),'len(loc[1]):',len(loc[1])) # loc 元组每个值 120 个元素
print('type(loc[0]):',type(loc[0]),'type(loc[1]):',type(loc[1])) # loc 元组每个值的类型为 numpy.array
print("loc[::-1]:",loc[::-1]) # loc[::-1] 表示顺序取反,即第二个 numpy.array 放在第一个 numpy.array 前面
i = 0
# zip函数为打包为元组的列表,例 a = [1,2,3] b = [4,5,6] zip(a,b) 为 [(1, 4), (2, 5), (3, 6)]
for pt in zip(*loc[::-1]): # 当用 *b 作为传入参数时, b 可以为列表、元组、集合,zip使得元组中两个 numpy.array 进行配对
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0,0,255),2)
i = i + 1
print('i:',i)
cv2.imshow('img_rgb',img_rgb)
cv2.waitKey(0)
15、图像直方图
16、傅里叶变换

作用:
① 高频:变化剧烈的灰度分量,例如边界礁石。
② 低频:变化缓慢的灰度分量,例如一片大海。
③ 高通滤波器:只保留高频,会使得图像细节增强。高频边界锐化了,增强了,细节更明显了。
④ 低通滤波器:只保留低频,会使得图像模糊。低频信息保留下来了,高频信息没了,图像边界会变得模糊了。
① opencv 中主要就是 cv2.dft() 执行傅里叶变换到频域中 和 cv2.idft() 执行逆傅里叶变换,输入图像需要先转换成 np.float32 格式。
② 得到的结果中频率为 0 的部分会在左上角,通常要转换到中心位置,可以通过 shift 变换来实现。
③ cv2.dft() 返回的结果是双通道的 ( 实部,虚部 ),通常还需要转换成图像格式才能展示(0,255)像素值。
-
高通滤波器
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('01_Picture/13_Lena.jpg',0) img_float32 = np.float32(img) # DFT ( 傅里叶变换 ) dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape crow, ccol = int(rows/2), int(cols/2) # 中心位置 # 低通滤波 mask = np.zeros((rows,cols,2),np.uint8) mask[crow-30:crow+30,ccol-30:ccol+30] = 1 # 只保留中心点周围的区域,中心点为最低频的 # IDPT (傅里叶逆变换) fshift = dft_shift * mask # 用掩码提取 dft_shift 中相应区域,是 1 就保留,不是 1 就过滤了 f_ishift = np.fft.ifftshift(fshift) # 把拉到中心位置的频谱区域给还原回去,依旧回到左上角 img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) # 将实部和虚部结合起来,才能将傅里叶变换的结果显示出来 plt.subplot(121),plt.imshow(img,cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back,cmap='gray') plt.title('Result'), plt.xticks([]), plt.yticks([])
-
低通滤波器
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('01_Picture/13_Lena.jpg',0) img_float32 = np.float32(img) dft = cv2.dft(img_float32,flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape crow, ccol = int(rows/2), int(cols/2) # 中心位置 # 高通滤波 mask = np.ones((rows,cols,2),np.uint8) mask[crow-30:crow+30,ccol-30:ccol+30] = 0 # 中间区域置 0,外面的区域置 1 # IDFT fshift = dft_shift * mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) plt.subplot(121),plt.imshow(img,cmap = 'gray') plt.title('Input Image'), plt.xticks([]),plt.yticks([]) plt.subplot(122),plt.imshow(img_back,cmap='gray') plt.title('Result'),plt.xticks([]),plt.yticks([])
17、
评论区