实验二 基元检测
计算机视觉OpenCV基础实验合辑(实验1234+扩展)
资源下载地址: https://download.csdn.net/download/weixin_53403301
合辑:(加在下载地址后面)
/87113581
讲义(包括理论、图例、PPT、实验、代码、手册):(加在下载地址后面)
/87113633
matplotlib中载入中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
实验目的:
1、 熟练掌握OpenCV使用方法;
2、 编程实现基元检测算法,加深对基元检测技术的理解,初步掌握基元检测运用方法。
实验内容:
1、边缘检测
(1)边缘检测的原理
基于搜索:利用一阶导数的最大值获取边界
基于零穿越:利用二阶导数为0获取边界
(2)Sobel算子
基于搜索的方法获取边界
cv.sobel()
cv.convertScaleAbs()
cv.addweights()
(3)Laplacian算子
基于零穿越获取边界
(4)Canny算法
噪声去除:高斯滤波
计算图像梯度:sobel算子,计算梯度大小和方向
非极大值抑制:利用梯度方向像素来判断当前像素是否为边界点
滞后阈值:设置两个阈值,确定最终的边界
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 14:44:28 2021
@author: ZHOU
"""
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1 读取图像
img = cv.imread('./image/horse.jpg',0)
# 2 计算Sobel卷积结果
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
# 3 将数据进行转换
Scale_absX = cv.convertScaleAbs(x) # convert 转换 scale 缩放
Scale_absY = cv.convertScaleAbs(y)
# 4 结果合成
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# 5 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel滤波后结果')
plt.xticks([]), plt.yticks([])
plt.show()
x = cv.Sobel(img, cv.CV_16S, 1, 0, ksize = -1) # 利用Scharr进行边缘检测。
y = cv.Sobel(img, cv.CV_16S, 0, 1, ksize = -1)
# 3 将数据进行转换
Scale_absX = cv.convertScaleAbs(x) # convert 转换 scale 缩放
Scale_absY = cv.convertScaleAbs(y)
# 4 结果合成
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# 5 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Scharr滤波后结果')
plt.xticks([]), plt.yticks([])
plt.show()
# 1 读取图像
img = cv.imread('./image/horse.jpg',0)
# 2 laplacian转换
result = cv.Laplacian(img,cv.CV_16S)
Scale_abs = cv.convertScaleAbs(result)
# 3 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(Scale_abs,cmap = plt.cm.gray),plt.title('Laplacian检测后结果')
plt.xticks([]), plt.yticks([])
plt.show()
# 1 图像读取
img = cv.imread('./image/horse.jpg',0)
# 2 Canny边缘检测
lowThreshold = 0
max_lowThreshold = 100
canny = cv.Canny(img, lowThreshold, max_lowThreshold)
# 3 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(canny,cmap = plt.cm.gray),plt.title('Canny检测后结果')
plt.xticks([]), plt.yticks([])
plt.show()
2、模板匹配
原理:在给定的图片中查找和模板最相似的区域
API:利用cv.matchTemplate()进行模板匹配,然后使用cv.minMaxLoc()搜索最匹配的位置。
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 15:04:31 2021
@author: ZHOU
"""
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1 图像和模板读取
img = cv.imread('./image/wulin.jpeg')
template = cv.imread('./image/bai.jpeg')
h,w,l = template.shape
# 2 模板匹配
# 2.1 模板匹配
res = cv.matchTemplate(img, template, cv.TM_CCORR)
# 2.2 返回图像中最匹配的位置,确定左上角的坐标,并将匹配位置绘制在图像上
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
# 使用平方差时最小值为最佳匹配位置
# top_left = min_loc
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv.rectangle(img, top_left, bottom_right, (0,255,0), 2)
# 3 图像显示
plt.imshow(img[:,:,::-1])
plt.title('匹配结果'), plt.xticks([]), plt.yticks([])
plt.show()
3、霍夫变换
(1)霍夫线检测
原理:将要检测的内容转换到霍夫空间中,利用累加器统计最优解,将检测结果表示处理
API:cv2.HoughLines()
注意:该方法输入是的二值化图像,在进行检测前要将图像进行二值化处理
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 15:06:51 2021
@author: ZHOU
"""
import numpy as np
import random
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1.加载图片,转为二值图
img = cv.imread('./image/rili.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150)
# 2.霍夫直线变换
lines = cv.HoughLines(edges, 0.8, np.pi / 180, 150)
# 3.将检测的线绘制在图像上(注意是极坐标噢)
for line in lines:
rho, theta = line[0]
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))
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0))
# 4. 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('霍夫变换线检测')
plt.xticks([]), plt.yticks([])
plt.show()
(2)霍夫圆检测
方法:霍夫梯度法
API:cv.HoughCircles()
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 15:06:51 2021
@author: ZHOU
"""
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 1 读取图像,并转换为灰度图
planets = cv.imread("./image/star.jpeg")
gay_img = cv.cvtColor(planets, cv.COLOR_BGRA2GRAY)
# 2 进行中值模糊,去噪点
img = cv.medianBlur(gay_img, 7)
# 3 霍夫圆检测
circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 200, param1=100, param2=30, minRadius=0, maxRadius=100)
# 4 将检测结果绘制在图像上
for i in circles[0, :]: # 遍历矩阵每一行的数据
# 绘制圆形
cv.circle(planets, (i[0], i[1]), i[2], (0, 255, 0), 2)
# 绘制圆心
cv.circle(planets, (i[0], i[1]), 2, (0, 0, 255), 3)
# 5 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(planets[:,:,::-1]),plt.title('霍夫变换圆检测')
plt.xticks([]), plt.yticks([])
plt.show()
4、角点检测
(1)Harris算法
思想:通过图像的局部的小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致图像灰度的明显变化。
API: cv.cornerHarris()
(2)Shi-Tomasi算法
对Harris算法的改进,能够更好地检测角点
API: cv2.goodFeatureToTrack()
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 15:24:03 2021
@author: ZHOU
"""
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1 读取图像,并转换成灰度图像
img = cv.imread('./image/chessboard.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2 角点检测
# 2.1 输入图像必须是 float32
gray = np.float32(gray)
# 2.2 最后一个参数在 0.04 到 0.05 之间
dst = cv.cornerHarris(gray,2,3,0.04)
# 3 设置阈值,将角点绘制出来,阈值根据图像进行选择
img[dst>0.001*dst.max()] = [0,0,255]
# 4 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('Harris角点检测')
plt.xticks([]), plt.yticks([])
plt.show()
# 1 读取图像
img = cv.imread('./image/tv.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2 角点检测
corners = cv.goodFeaturesToTrack(gray,1000,0.01,10)
# 3 绘制角点
for i in corners:
x,y = i.ravel()
cv.circle(img,(x,y),2,(0,0,255),-1)
# 4 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('shi-tomasi角点检测')
plt.xticks([]), plt.yticks([])
plt.show()
5、轮廓检测
(1)轮廓搜索:
cv2.findContours(img,mode,method)
mode:轮廓检索模式
• RETR_EXTERNAL :只检索最外面的轮廓;
• RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
• RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
• RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法
• CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
• CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
(2)轮廓绘制
cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
(3)轮廓特征计算:面积、周长、边界矩形、外接圆、饱和度(轮廓面积与边界矩形面积之比)。
cv2.contourArea(cnt) #面积
cv2.arcLength(cnt,True) #周长,True表示闭合的
x,y,w,h = cv2.boundingRect(cnt) #获得边界矩形
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) #绘制边界矩形
area = cv2.contourArea(cnt) #轮廓面积
x, y, w, h = cv2.boundingRect(cnt) #获得轮廓的边界矩形
rect_area = w * h#边界矩形面积
extent = float(area) / rect_area#面积比
print (‘轮廓面积与边界矩形比’,extent)
(x,y),radius = cv2.minEnclosingCircle(cnt)#外接圆
(4)轮廓近似(多边形逼近)
epsilon = 0.15*cv2.arcLength(cnt,True) #以周长的0.15作为误差标准
approx = cv2.approxPolyDP(cnt,epsilon,True) #对轮廓cnt以误差标准epsilon进行近似
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 15:24:03 2021
@author: ZHOU
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1 读取图像,并转换成灰度图像
img = cv2.imread('./image1/contours.png')
plt.imshow(img[:,:,::-1])
plt.title('原图'), plt.xticks([]), plt.yticks([])
plt.show()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold (gray, 127, 255, cv2.THRESH_BINARY)
binary=thresh
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 绘制轮廓 传入图像 -1
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
plt.imshow(res[:,:,::-1])
plt.title('绘制轮廓 -1'), plt.xticks([]), plt.yticks([])
plt.show()
# 绘制轮廓 传入图像 0
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)
plt.imshow(res[:,:,::-1])
plt.title('绘制轮廓 0'), plt.xticks([]), plt.yticks([])
plt.show()
# 计算面积
cnt = contours[0]
print('面积',cv2.contourArea(cnt))
print('周长',cv2.arcLength(cnt,True))
# 边界矩形
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
plt.imshow(img[:,:,::-1])
plt.title('边界矩形'), plt.xticks([]), plt.yticks([])
plt.show()
area = cv2.contourArea(cnt)
rect_area = w*h
extent = float(area) / rect_area
print('轮廓面积与边界矩形之比',extent)
# 外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0,255,0), 2)
plt.imshow(img[:,:,::-1])
plt.title('外接圆'), plt.xticks([]), plt.yticks([])
plt.show()
# 1 读取图像2,并转换成灰度图像
img = cv2.imread('./image1/contours2.png')
plt.imshow(img[:,:,::-1])
plt.title('原图'), plt.xticks([]), plt.yticks([])
plt.show()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold (gray, 127, 255, cv2.THRESH_BINARY)
binary=thresh
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
plt.imshow(res[:,:,::-1])
plt.title('绘制轮廓'), plt.xticks([]), plt.yticks([])
plt.show()
epsilon = 0.15*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
print('0.15倍周长',epsilon)
print('对轮廓cnt以误差标准epsilon进行近似',approx)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
plt.imshow(res[:,:,::-1])
plt.title('轮廓近似'), plt.xticks([]), plt.yticks([])
plt.show()
# 边界矩形
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
plt.imshow(img[:,:,::-1])
plt.title('边界矩形'), plt.xticks([]), plt.yticks([])
plt.show()
area = cv2.contourArea(cnt)
rect_area = w*h
extent = float(area) / rect_area
print('轮廓面积与边界矩形之比',extent)
# 外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0,255,0), 2)
plt.imshow(img[:,:,::-1])
plt.title('外接圆'), plt.xticks([]), plt.yticks([])
plt.show()
面积 8500.5
周长 437.9482651948929
轮廓面积与边界矩形之比 0.5154317244724715
0.15倍周长 189.8943818628788
对轮廓cnt以误差标准epsilon进行近似 [[[291 30]]
[[ 40 263]]
[[335 257]]]
轮廓面积与边界矩形之比 0.7800798598378357
6、形态学图像处理
• 腐蚀和膨胀:
腐蚀:求局部最大值,缩小或细化,能将小于结构体的细节从图像中滤除。
膨胀:求局部最小值,增长或粗化
• 开闭运算:
开:先腐蚀后膨胀。平滑物体轮廓、断开较窄的连接、消除细的突出物。
闭:先膨胀后腐蚀。平滑轮廓、弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂。
• 礼帽和黑帽:
礼帽:原图像与开运算之差。
开运算放大了裂缝或者局部低亮度的区域,所以,从原图中减去开运算后的图,得到的结果突出了比原图轮廓周围的区域更明亮的区域,
这个操作与选择的核的大小有关。TopHat运算一般用来分离比邻近点亮一些的斑块,可以使用这个运算提取背景。
黑帽:闭运算与原图像之差
黑帽运算的结果突出了比原图轮廓周围区域更暗的区域,所以黑帽运算用来分离比邻近点暗一些的斑块。
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 21 16:48:59 2021
@author: ZHOU
"""
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# 1 读取图像
img = cv.imread("./image/letter.png")
# 2 创建核结构
kernel = np.ones((5, 5), np.uint8)
# 3 图像腐蚀和膨胀
erosion = cv.erode(img, kernel) # 腐蚀
dilate = cv.dilate(img,kernel) # 膨胀
# 4 图像展示
fig,axes=plt.subplots(nrows=1,ncols=3,figsize=(10,8),dpi=100)
axes[0].imshow(img)
axes[0].set_title("原图")
axes[1].imshow(erosion)
axes[1].set_title("腐蚀后结果")
axes[2].imshow(dilate)
axes[2].set_title("膨胀后结果")
plt.show()
img1 = cv.imread("./image/letteropen.png")
img2 = cv.imread("./image/letterclose.png")
# 2 创建核结构
kernel = np.ones((10, 10), np.uint8)
# 3 图像的开闭运算
cvOpen = cv.morphologyEx(img1,cv.MORPH_OPEN,kernel) # 开运算
cvClose = cv.morphologyEx(img2,cv.MORPH_CLOSE,kernel)# 闭运算
# 4 图像展示
fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(img1)
axes[0,0].set_title("原图")
axes[0,1].imshow(cvOpen)
axes[0,1].set_title("开运算结果")
axes[1,0].imshow(img2)
axes[1,0].set_title("原图")
axes[1,1].imshow(cvClose)
axes[1,1].set_title("闭运算结果")
plt.show()
# 2 创建核结构
kernel = np.ones((10, 10), np.uint8)
# 3 图像的礼帽和黑帽运算
cvOpen = cv.morphologyEx(img1,cv.MORPH_TOPHAT,kernel) # 礼帽运算
cvClose = cv.morphologyEx(img2,cv.MORPH_BLACKHAT,kernel)# 黑帽运算
# 4 图像显示
fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(img1)
axes[0,0].set_title("原图")
axes[0,1].imshow(cvOpen)
axes[0,1].set_title("礼帽运算结果")
axes[1,0].imshow(img2)
axes[1,0].set_title("原图")
axes[1,1].imshow(cvClose)
axes[1,1].set_title("黑帽运算结果")
plt.show()
实验收获:
编程实现基元检测算法,加深对基元检测技术的理解,初步掌握基元检测运用方法。 学会了利用OpenCV进行边缘检测、角点检测、霍夫变换、形态学处理、基元检测以及模板匹配。