我会尝试用cv2.Canny() 找到线的两条边并搜索轮廓。如果您按边界框的宽度对轮廓进行排序,则前两个轮廓将是您的线条边缘。之后,您可以计算一个边缘中每个点到另一边缘的最小距离。然后您可以计算距离的中位数,并说如果一个点的距离大于或小于中位数(+- 容差),那么该点就是线的膨胀或腐蚀,并将其附加到列表中。如果需要,您可以通过遍历列表来整理噪音,如果它们不连续(在 x 轴上),则删除它们。
这是一个简单的例子:
import cv2
import numpy as np
from scipy import spatial
def detect_dilation(median, mindist, tolerance):
count = 0
for i in mindist:
if i > median + tolerance:
dilate.append((reshape_e1[count][0], reshape_e1[count][1]))
elif i < median - tolerance:
erode.append((reshape_e1[count][0], reshape_e1[count][1]))
else:
pass
count+=1
def other_axis(dilate, cnt):
temp = []
for i in dilate:
temp.append(i[0])
for i in cnt:
if i[0] in temp:
dilate.append((i[0],i[1]))
img = cv2.imread('1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,100,200)
_, contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
contours.sort(key= lambda cnt :cv2.boundingRect(cnt)[3])
edge_1 = contours[0]
edge_2 = contours[1]
reshape_e1 = np.reshape(edge_1, (-1,2))
reshape_e2 =np.reshape(edge_2, (-1,2))
tree = spatial.cKDTree(reshape_e2)
mindist, minid = tree.query(reshape_e1)
median = np.median(mindist)
dilate = []
erode = []
detect_dilation(median,mindist,5)
other_axis(dilate, reshape_e2)
other_axis(erode, reshape_e2)
dilate = np.array(dilate).reshape((-1,1,2)).astype(np.int32)
erode = np.array(erode).reshape((-1,1,2)).astype(np.int32)
x,y,w,h = cv2.boundingRect(dilate)
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
x,y,w,h = cv2.boundingRect(erode)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
编辑:
如果图片有一条断线(这意味着更多的轮廓),您必须将每个轮廓视为单独的线。您可以通过在cv2.boundingRect() 的帮助下创建感兴趣的区域来实现此目的。但是当我尝试使用新上传的图片时,该过程不是很稳健,因为您必须更改容差才能获得所需的结果。由于我不知道其他图像是什么样的,您可能需要一种更好的方法来获取平均距离和容差因子。这里的任何方式都是我所描述的示例(15 表示容差):
import cv2
import numpy as np
from scipy import spatial
def detect_dilation(median, mindist, tolerance):
count = 0
for i in mindist:
if i > median + tolerance:
dilate.append((reshape_e1[count][0], reshape_e1[count][1]))
elif i < median - tolerance:
erode.append((reshape_e1[count][0], reshape_e1[count][1]))
else:
pass
count+=1
def other_axis(dilate, cnt):
temp = []
for i in dilate:
temp.append(i[0])
for i in cnt:
if i[0] in temp:
dilate.append((i[0],i[1]))
img = cv2.imread('2.jpg')
gray_original = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh_original = cv2.threshold(gray_original, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Filling holes
_, contours, hierarchy = cv2.findContours(thresh_original,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
cv2.drawContours(thresh_original,[cnt],0,255,-1)
_, contours, hierarchy = cv2.findContours(thresh_original,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in contours:
x2,y,w2,h = cv2.boundingRect(cnt)
thresh = thresh_original[0:img.shape[:2][1], x2+20:x2+w2-20] # Region of interest for every "line"
edges = cv2.Canny(thresh,100,200)
_, contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
contours.sort(key= lambda cnt: cv2.boundingRect(cnt)[3])
edge_1 = contours[0]
edge_2 = contours[1]
reshape_e1 = np.reshape(edge_1, (-1,2))
reshape_e2 =np.reshape(edge_2, (-1,2))
tree = spatial.cKDTree(reshape_e2)
mindist, minid = tree.query(reshape_e1)
median = np.median(mindist)
dilate = []
erode = []
detect_dilation(median,mindist,15)
other_axis(dilate, reshape_e2)
other_axis(erode, reshape_e2)
dilate = np.array(dilate).reshape((-1,1,2)).astype(np.int32)
erode = np.array(erode).reshape((-1,1,2)).astype(np.int32)
x,y,w,h = cv2.boundingRect(dilate)
if len(dilate) > 0:
cv2.rectangle(img[0:img.shape[:2][1], x2+20:x2+w2-20],(x,y),(x+w,y+h),(255,0,0),2)
x,y,w,h = cv2.boundingRect(erode)
if len(erode) > 0:
cv2.rectangle(img[0:img.shape[:2][1], x2+20:x2+w2-20],(x,y),(x+w,y+h),(0,0,255),2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果: