在ci中查找轮廓

2024-05-20 10:26:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试编写一个程序,可以检测圆形透镜上的直线切割,如图左侧所示:

enter image description here

现在,我已经尝试使用Canny边缘检测、Hough线变换和findContour来单独分离线,但是我没有成功。在

我也尝试过通过首先检测镜头的外圆并在ROI内执行轮廓搜索(detected circle)来检测线条,但我得到的是穿过镜头的随机线条,而不是我想要的输出。在


Tags: 程序圆形边缘直线线条镜头轮廓透镜
1条回答
网友
1楼 · 发布于 2024-05-20 10:26:37

所以,首先我要指出,你的形象是非常嘈杂的。这意味着仅仅通过寻找轮廓、边缘或线条,可能会因为噪音而无法工作。这使任务非常困难。如果你正在寻找一种自动化的方法来完成这样的任务,我建议你花点力气去寻找合适的照明(我认为一个经典的顶灯就足够了),因为它会使图像上的噪音更小(反射更少……),因此这样的算法更容易实现。在

我是这么说的。我举了一个例子来说明我将如何努力完成这项任务。请注意,这个解决方案可能不适用于其他图像,但在这个示例中,结果非常好。它可能会给你一个新的观点,如何解决这个问题。在

首先,在用OTSU阈值将图像转换为二进制之前,我将尝试执行直方图均衡化。之后,我将对图像执行打开操作(腐蚀后膨胀):

enter image description here

之后我会在最大的轮廓上做一个包围盒。通过x,y,h,w,我可以计算出边界框的中心,它将作为我要创建的ROI的中心。在图像的副本上画一个半径略小于w/2的圆,在半径等于w/2的新遮罩上画一个圆。然后执行位操作:

enter image description here

现在您有了ROI,必须再次设置阈值,以使边界无噪音并搜索轮廓:

enter image description here

现在可以看到有两个轮廓(内部和外部)。现在你可以提取镜头被切割的区域。可以通过计算内轮廓和外轮廓的每个点之间的距离来完成此操作。两点之间的距离公式是sqrt((x2-x1)^2 + (y2-y2)^2)。阈值这个距离,以便如果距离小于某个整数,并在图像上这两个点之间画一条线。我用蓝线画了距离。之后,将图像转换为HSV颜色空间,并再次使用按位操作对其进行遮罩,因此只剩下蓝色线条:

enter image description here

再次执行OTSU阈值,然后选择最大的轮廓(那些蓝色的线)并通过轮廓拟合一条线。在原图上画一条线,你就会得到结果:

enter image description here

示例代码:

import cv2
import numpy as np

### Perform histogram equalization and threshold with OTSU.
img = cv2.imread('lens.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
equ = cv2.equalizeHist(gray)
_, thresh = cv2.threshold(equ,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

### Perform opening (erosion followed by dilation) and search for contours.
kernel = np.ones((2,2),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
_, contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)

### Select the biggest one and create a bounding box.
### This will be used to calculate the center of your ROI.
cnt = max(contours, key=cv2.contourArea)

### Calculate x and y of the center.
x,y,w2,h2 = cv2.boundingRect(cnt)
center_x = int(x+(w2/2))
center_y = int(y+(h2/2))

### Create the radius of your inner circle ROI and draw it on a copy of the image.
img2 = img.copy()
radius = int((w2/2)-20)
cv2.circle(img2,(center_x,center_y), radius, (0,0,0), -1)

### Create the radius of your inner circle ROI and draw it on a blank mask.
radius_2 = int(w2/2)
h,w = img.shape[:2]
mask = np.zeros((h, w), np.uint8)
cv2.circle(mask,(center_x,center_y), radius_2, (255,255,255), -1)

### Perform a bitwise operation so that you will get your ROI
res = cv2.bitwise_and(img2, img2, mask=mask)

### Modify the image a bit to eliminate noise with thresholding and closing.
_, thresh = cv2.threshold(res,190,255,cv2.THRESH_BINARY)
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel, iterations = 2)

### Search for contours again and select two biggest one.
gray = cv2.cvtColor(closing,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
area = sorted(contours, key=cv2.contourArea, reverse=True)
contour1 = area[0]
contour2 = area[1]

### Iterate through both contours and calculate the minimum distance.
### If it is less than the threshold you provide, draw the lines on the image.
### Forumula is sqrt((x2-x1)^2 + (y2-y2)^2).
for i in contour1:
    x = i[0][0]
    y = i[0][1]
    for j in contour2:
        x2 = j[0][0]
        y2 = j[0][1]
        dist = np.sqrt((x2-x)**2 + (y2-y)**2)
        if dist < 12:
            xy = (x,y)
            x2y2 = (x2,y2)
            line = (xy,x2y2)
            cv2.line(img2,xy,x2y2,(255,0,0),2)
        else:
            pass

### Transform the image to HSV colorspace and mask the result.
hsv = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
res = cv2.bitwise_and(img2,img2, mask= mask)

### Search fot contours again.
gray = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)

### Fit a line through the contour and draw it on the original image.
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
left = int((-x*vy/vx) + y)
right = int(((w-x)*vy/vx)+y)
cv2.line(img,(w-1,right),(0,left),(0,0,255),2)

### Display the result.
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

相关问题 更多 >