近几日使用opencv-python做了气泡轮廓识别相关的工作,需要实现的功能是查找气泡图像外轮廓。
分为三部分:
1.轮廓识别并按长度排序;2.特定轮廓的最高点、最低点、最左点、最右点查找;3.内外轮廓判断。
原图像:
读取图片、预处理、轮廓识别:
# 定义显示函数
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
# 读取图片 灰度和彩色都要读取
img = cv2.imread('bub.png',cv2.IMREAD_GRAYSCALE)
img_rgb = cv2.imread('bub.png')
# 二值化
ret, image_threshold = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cv2.imshow(image_threshold, image_threshold )
cv2.waitKey()
# 轮廓识别
contours, hierarchy = cv2.findContours(image_threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
二值化后图像:
轮廓识别:使用cv2.findContours返回contours轮廓信息
轮廓排序:
# 创建带有轮廓编号和长度的二维数组cnt_size
cnt_size = np.zeros((len(contours), 2), dtype = int)
for i in range(len(contours)):
len_cnt = np.size(contours[i])
if len_cnt > 6:
cnt_size[i][0] = i
cnt_size[i][1] = len_cnt
cnt_size = cnt_size[np.lexsort(cnt_size.T)]
sort_cnt = cv2.drawContours(img_rgb, contours, cnt_size[-2][0], (215, 19, 225), 2)
cv_show(sort_cnt,'sort_cnt')
识别效果:最长的轮廓为最外圈,第二长的轮廓才是需要定位的气泡外轮廓。
# 确定需要查找的轮廓
contour = np.array(contours[cnt_size[-2][0]])
# 寻找上下左右四最值点
left_most = contour[contour[:, :, 0].argmin()][0]
right_most = contour[contour[:, :, 0].argmax()][0]
top_most = contour[contour[:, :, 1].argmin()][0]
bottom_most = contour[contour[:, :, 1].argmax()][0]
# 画线检验
cv2.line(img_rgb, (0, 0), left_most, (255, 25, 25), 2)
cv2.line(img_rgb, (0, 0), right_most, (25, 255, 255), 2)
cv2.line(img_rgb, (0, 0), top_most, (255, 255, 25), 2)
cv2.line(img_rgb, (0, 0), bottom_most, (55, 25, 255), 2)
cv_show(img_rgb,'img_line')
画线效果:
基于sobel算子,可以有效识别左黑右白、左白右黑、上黑下白、上白下黑。
# 使用sobel算子计算x、y方向上的梯度
sobelx = cv2.Sobel(image_threshold, cv2.CV_64F, 1, 0, ksize = 5)
sobely = cv2.Sobel(image_threshold, cv2.CV_64F, 0, 1, ksize = 5)
cv_show(sobelx,'sobelx')
cv_show(sobely,'sobely')
# 输出四个最值点的梯度信息
print(sobelx[left_most[1], left_most[0]], sobelx[right_most[1], right_most[0]])
print(sobely[top_most[1], top_most[0]], sobely[bottom_most[1], bottom_most[0]])
梯度图像:
输出结果:这里注意,sobelx和返回的最值点坐标使用的x、y坐标位置是相反的!!!!
-9690.0 9690.0
-9690.0 9690.0
进而根据正负值判断是否为外轮廓:
left = sobelx[left_most[1], left_most[0]]
right = sobelx[right_most[1], right_most[0]]
top = sobely[top_most[1], top_most[0]]
bottom = sobely[bottom_most[1], bottom_most[0]]
if left < 0 and right > 0 and top < 0 and bottom > 0:
print('True')
else:
print('False')
全部代码如下:
import cv2 #opencv读取的格式是BGR
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
# 一、轮廓识别并按长度排序
# 读取图片 灰度和彩色都要读取
img = cv2.imread('bub1.png',cv2.IMREAD_GRAYSCALE)
img_rgb = cv2.imread('bub1.png')
cv_show(img_rgb,'img_rgb')
# 二值化
ret, image_threshold = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cv_show(image_threshold,'image')
# 二、特定轮廓的最高点、最低点、最左点、最右点查找
# 轮廓识别
contours, hierarchy = cv2.findContours(image_threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 创建带有轮廓编号和长度的二维数组cnt_size
cnt_size = np.zeros((len(contours), 2), dtype = int)
for i in range(len(contours)):
len_cnt = np.size(contours[i])
if len_cnt > 6:
cnt_size[i][0] = i
cnt_size[i][1] = len_cnt
cnt_size = cnt_size[np.lexsort(cnt_size.T)]
sort_cnt = cv2.drawContours(img_rgb, contours, cnt_size[-2][0], (215, 19, 225), 2)
cv_show(sort_cnt,'sort_cnt')
# 开始画线
contour = np.array(contours[cnt_size[-2][0]])
# 寻找上下左右四最值点
left_most = contour[contour[:, :, 0].argmin()][0]
right_most = contour[contour[:, :, 0].argmax()][0]
top_most = contour[contour[:, :, 1].argmin()][0]
bottom_most = contour[contour[:, :, 1].argmax()][0]
# 画线检验
cv2.line(img_rgb, (0, 0), left_most, (255, 25, 25), 2)
cv2.line(img_rgb, (0, 0), right_most, (25, 255, 255), 2)
cv2.line(img_rgb, (0, 0), top_most, (255, 255, 25), 2)
cv2.line(img_rgb, (0, 0), bottom_most, (55, 25, 255), 2)
cv_show(img_rgb,'img_line')
# 三、内外轮廓判断
# 使用sobel算子计算x、y方向上的梯度
sobelx = cv2.Sobel(image_threshold, cv2.CV_64F, 1, 0, ksize = 5)
sobely = cv2.Sobel(image_threshold, cv2.CV_64F, 0, 1, ksize = 5)
cv_show(sobelx,'sobelx')
cv_show(sobely,'sobely')
# 输出四个最值点的梯度信息
# 是反着的!!!!!!!!!!!!!!!!!!!!!
print(sobelx[left_most[1], left_most[0]], sobelx[right_most[1], right_most[0]])
print(sobely[top_most[1], top_most[0]], sobely[bottom_most[1], bottom_most[0]])
# 输出
left = sobelx[left_most[1], left_most[0]]
right = sobelx[right_most[1], right_most[0]]
top = sobely[top_most[1], top_most[0]]
bottom = sobely[bottom_most[1], bottom_most[0]]
if left < 0 and right > 0 and top < 0 and bottom > 0:
print('True')
else:
print('False')