OpenCV 霍夫变换与轮廓提取
- 3. 霍夫变换
- 3.1 霍夫直线
- 3.2 霍夫圆
- 4. 轮廓提取
- 4.1 查找轮廓
- 4.2 绘制轮廓
3. 霍夫变换
首先放上霍夫变换官方文档:[霍夫直线变换官网文档]
3.1 霍夫直线
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
# 1. 将图片以灰度的方式读取进来
img = cv.imread("../img/weiqi.jpg", cv.IMREAD_COLOR)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2. 将图片转成二值图
_, thresh_img = cv.threshold(gray_img, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
# 3. 霍夫变换
# 线段以像素为单位的距离精度,double类型的,推荐用1.0
rho = 1
# 线段以弧度为单位的角度精度,推荐用numpy.pi/180(弧度变换的步长)
theta = np.pi / 180
# 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。(一条直线至少包含十个像素点)
threshold = 10
# 线段以像素为单位的最小长度
min_line_length = 25
# 同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段
max_line_gap = 3
lines = cv.HoughLinesP(thresh_img, rho, theta, threshold, minLineLength=min_line_length, maxLineGap=max_line_gap)
dst_img = img.copy()
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(dst_img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("src", img)
cv.imshow("gray_img", gray_img)
cv.imshow("thresh_img", thresh_img)
cv.imshow("dst_img", dst_img)
cv.waitKey(0)
3.2 霍夫圆
import cv2 as cv
import numpy as np
# 1. 将图片以灰度的方式读取进来
img = cv.imread("../img/weiqi.jpg", cv.IMREAD_COLOR)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2. 霍夫圆形检测
def hough_circle(gray_img):
# 定义检测图像中圆的方法。目前唯一实现的方法是cv2.HOUGH_GRADIENT
method = cv.HOUGH_GRADIENT
# 累加器分辨率与图像分辨率的反比。例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。如果dp = 2,则累加器的宽度和高度都是一半。
dp = 1
# 检测到的圆的圆心之间最小距离。如果minDist太小,则可能导致检测到多个相邻的圆。如果minDist太大,则可能导致很多圆检测不到。
minDist = 20
# param1 Canny算法阈值上线
# param2 cv2.HOUGH_GRADIENT方法的累加器阈值。阈值越小,检测到的圈子越多。
# minRadius : 最小的半径,如果不确定,则不指定
# maxRadius : 最大的半径,若不确定,则不指定
circles = cv.HoughCircles(gray_img, method, dp, minDist=minDist, param1=70, param2=30, minRadius=0, maxRadius=20)
dst_img = img.copy()
for circle in circles[0, :]:
# 圆心坐标,半径
x, y, r = circle
# 绘制圆心
cv.circle(dst_img, (x, y), 2, (0, 255, 0), 1)
# 绘制圆形
cv.circle(dst_img, (x, y), r, (0, 0, 255), 2)
cv.imshow("src", img)
cv.imshow("result", dst_img)
# 3. 调用函数,寻找霍夫圆
hough_circle(gray_img)
cv.waitKey(0)
cv.destroyAllWindows()
4. 轮廓提取
- 基于图像边缘提取或二值化的基础寻找对象轮廓
- 边缘提取的阈值会最终影响轮廓发现的结果
- 主要API有以下两个
-
findContours
发现轮廓 -
drawContours
绘制轮廓
4.1 查找轮廓
处理的图像, 轮廓列表, 继承关系 = cv.findContours(图像, 轮廓检索模式, 轮廓检索算法)
# hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号
轮廓检索模式:
RETR_EXTERNAL
只检测最外层轮廓RETR_LIST
提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系RETR_CCOMP
提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界RETR_TREE
提取所有轮廓并重新建立网状轮廓结构
轮廓检索算法:
CHAIN_APPROX_NONE
获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1CHAIN_APPROX_SIMPLE
压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息CHAIN_APPROX_TC89_L1
Teh-Chinl链逼近算法CHAIN_APPROX_TC89_KCOS
Teh-Chinl链逼近算法
4.2 绘制轮廓
cv.drawContours(图像, 轮廓列表, 轮廓索引 如-1则绘制所有, 轮廓颜色, 轮廓的宽度)
((x,y),radius) = cv.minEnclosingCircle(contour) # 绘制外切圆
实现步骤:
- 读取图片
- 将图片转成一张灰色图片
- 对图片进行二值化处理
- 使用findContours查找轮廓
- 对轮廓进行处理
import cv2 as cv
# 1. 读取图片
def read_rgb_img(img_name):
rgb_img = cv.imread(img_name, cv.IMREAD_COLOR)
cv.imshow("rgb img", rgb_img)
return rgb_img
# 2. 将图片转成一张灰色图片
def convert_rgb2gray(img):
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
return gray_img
# 3. 对图片进行二值化处理
def convert_gray2binary(img):
# binary_img = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2)
_, binary_img = cv.threshold(img, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
return binary_img
# 4. 使用findContours查找轮廓
def getContours(img):
_, contours, hierarchy = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
print(contours, hierarchy)
return contours
# 5. 对轮廓进行处理
def draw_contours(img, contours):
index = -1 # 所有的轮廓
thickness = 2 # 轮廓的宽度
color = (255, 125, 125) # 轮廓的颜色
imgg = cv.drawContours(img, contours, index, color, thickness)
cv.imshow('draw contours', imgg) # 画出轮廓图
for i, c in enumerate(contours):
circle = cv.minEnclosingCircle(c)
((x, y), radius) = circle
cv.circle(imgg, (int(x), int(y)), int(radius), (0, 0, 255), 2)
if __name__ == '__main__':
img_name = "../img/shape.jpg"
rgb_img = read_rgb_img(img_name)
gray_img = convert_rgb2gray(rgb_img)
binary_imgage = convert_gray2binary(gray_img)
contours = getContours(binary_imgage)
draw_contours(rgb_img, contours)
cv.imshow("gray_img", gray_img) # 画出灰度图
cv.imshow("binary_img", binary_imgage) # 画出二值图
cv.imshow('draw_contours_circle', rgb_img) # 画出有外接圆的轮廓图
cv.waitKey(0)
cv.destroyAllWindows()