環境:
Pycharm
python3.9
opencv4.5.4
此文章只描述技術思路,不講解相關原理
問題描述
從如下圖像中提取激光交點的坐標:
解決
我們要提取的是激光線交點的坐標,首先要找到激光線,通過直線的方程即可得到激光線的交點。
提取直線之前的預處理
- 根據激光線的顏色來提取激光線,激光線都是單色的,可以從RGB或者HSV模型中提取出單一顏色(一般來說,是在HSV模型中提取),即可得到只含有中心線的圖片, 這種方法每次都需要調整要查找的顏色,顏色范圍選取的好壞直接決定了處理效果的好壞(這種做法,我沒有做出來)
- 通過傳統的,濾波、二值化、腐蝕、膨脹操作,來獲得較為干凈的圖片。這種做法可以處理第一章圖像,但,在第二張圖像轉換為灰度圖后,由於文字的灰度和激光線的灰度差不多,在二值化時會無法消除文字的影響,進而后續的hough變換中會影響直線的識別,效果不是很好
- 使用Canny邊緣檢測作為二值化的方法,需要調整的參數少,二值化的效果也比較好,但仍然無法消除文字對圖像的影響
以上是尋找直線之前的預處理,在預處理中,還是沒有克服文字對激光線的影響,在文字影響小的情況下,使用Canny檢測可以有效的獲得二值化的圖像,對於文字對識別影響較大的圖片,需要對文字另做處理
文字消除
對圖二做邊緣檢測后,得到如下圖像
可以看到文字會對直線的識別造成影響,分析文字和激光線,文字是彎曲的,激光線大致只有兩個方向,沿x軸方向或者沿y軸方向,有傾斜,但沒有彎曲,這時可以考慮使用Sobel算子對圖像進行處理,但實際操作中,Sobel也會提取文字的一部分,使文字產生缺失,后續的膨脹效果不好。
對文字的處理,最后是在濾波中處理的,使用高斯濾波,選取較大的核即將直線濾除掉得到只有文字的圖片
通過對只含有文字的圖片進行膨脹操作(注意:opencv中膨脹操作默認是對黑色部分進行膨脹,這里我們希望對白色部分進行膨脹,所以使用的是腐蝕的函數),得到文字區域
得到腐蝕后的圖片后我們將兩長圖片進行比較,在原圖像中, 將文字區域的像素全部設置為0(黑色)即可,消除文字的影響
提取直線
一般采用Hough變換(霍夫變換)來提取直線
提取交點坐標
這里用兩種提取交點坐標的方法
-
通過直線方程獲得,檢測到的直線可能會有多根,可以將同一方向上的直線進行合並,最后只剩下兩個方向上的兩條直線計算交點
-
通過角點檢測來獲得坐標,在一張空白的圖像上,根據直線方程獲得只含有直線的圖像,然后檢測圖像的角點,選取前4個角點坐標,進行平均,即可得到中心點坐標
通過四個角點(紅色),來逼近真正的交點(綠色)
相關代碼
import cv2.cv2 as cv
import numpy as np
'''
圖片2的文字太清晰了,所以先提取文字,然后多次膨脹,找到文字所在的區域,在Canny邊緣檢測得到的圖像上
將文字所在區域全置為0(黑色), 這樣就有效的消除了文字對圖像的影響
'''
def hough2les(rho, theta, w):
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + w * (-b))
y1 = int(y0 + w * a)
x2 = int(x0 - w * (-b))
y2 = int(y0 - w * a)
k = float(y2 - y1) / float(x2 - x1)
b = -1*x1*k + y1
return k, b
src = cv.imread('./Photo/5.jpg', cv.IMREAD_COLOR)
high, width, channel = src.shape
hs_img = cv.cvtColor(src, cv.COLOR_RGB2GRAY)
# cv.imshow('hs', hs_img)
# cv.waitKey(0)
# 高斯濾波
Gaus_img = cv.GaussianBlur(hs_img, (7, 7), 1.5)
# cv.imshow('gauss', hs_img)
# cv.waitKey(0)
# Canny邊緣檢測
mask_img = cv.Canny(Gaus_img, 130, 142)
cv.imshow('edge', mask_img)
cv.waitKey(0)
elememt_1 = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
dilation_img = cv.dilate(mask_img, elememt_1, iterations=10)
cv.imshow('di_img', dilation_img)
cv.waitKey(0)
edge_img = cv.Canny(hs_img, 130, 140)
cv.imshow('edge_img', edge_img)
cv.waitKey(0)
for i in range(high):
for j in range(width):
if dilation_img[i][j] == 255:
edge_img[i][j] = 0
cv.imshow('edge_1_img', edge_img)
cv.waitKey(0)
# 霍夫變換
# p 的間隔為0.45 角度間隔為 1度, 長度大於100才被認為是直線
lines = cv.HoughLines(edge_img, 0.45, np.pi / 180, 100)
# print(lines)
show_img = np.zeros((high, width), np.uint8)
# result_line_array = []
for i in range(len(lines)):
for rho, theta in lines[i]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + width * (-b))
y1 = int(y0 + width * a)
x2 = int(x0 - width * (-b))
y2 = int(y0 - width * a)
cv.line(show_img, (x1, y1), (x2, y2), (255, 255, 255), 1)
# 角點檢測
# 檢測四個點,0.01是品質因數0.1-0.01之間, 10是兩個點之間的最小距離
corners = cv.goodFeaturesToTrack(show_img, 4, 0.01, 1)
x_value = 0.0
y_value = 0.0
for i in corners:
x, y = i.ravel()
x_value += x
y_value += y
# print('交點:{},{}\n'.format(x, y))
x_value /= len(corners)
y_value /= len(corners)
cv.circle(src, (int(x_value), int(y_value)), 3, 255, -1)
print('交點:{},{}\n'.format(x_value, y_value))
cv.imshow('result', src)
cv.waitKey(0)