如何在OpenCV中找到图像形状的角点?

15

我需要在一张图片中找到形状的角落。 我已经使用了Harris角点检测算法来查找角点,但它只能给出图片中所有的角点,而对于在该图片中找到特定形状的角点是不可行的。 请建议其他方法。

输入图像描述

你应该尝试使用findcontours函数。 - EdChum
我需要计算形状的角坐标。但是findcontours只提供边缘信息。你能分享一些代码或链接吗? - r_ranjan
首先找到轮廓并找到边缘,然后在该图像上找到角落,这就是EdChum试图表达的意思。 - Jeru Luke
2个回答

25

你可以使用哈里斯角检测算法。角是两条边的交汇处,其中一条边是图像亮度的突然变化。该算法直接参考方向差分来考虑角得分(维基百科)。cornerSubPix()函数用于精确确定角点或径向鞍点的亚像素位置,它会进行迭代以找到更精确的位置(OpenCV文档)。

代码示例:

import cv2
import numpy as np


img = cv2.imread('edges.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,5,3,0.04)
ret, dst = cv2.threshold(dst,0.1*dst.max(),255,0)
dst = np.uint8(dst)
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
for i in range(1, len(corners)):
    print(corners[i])
img[dst>0.1*dst.max()]=[0,0,255]
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows

输入图像描述

输入图像描述

如果要检查它们是否为真实值,您可以添加:

for i in range(1, len(corners)):
    print(corners[i,0])
    cv2.circle(img, (int(corners[i,0]), int(corners[i,1])), 7, (0,255,0), 2)

结果:

在此输入图片描述

编辑:

如果您想为每个形状单独提取角落,可以首先搜索轮廓,然后对每个轮廓应用Harris角点检测(可以使用cv2.fillPolly()在掩膜上绘制出来)。您甚至可以根据它们的特征定义它们的形状(例如旋转角度,角数等)。我已经编写了一个示例代码以帮助理解,但请注意,还有其他符合我所想出的条件的形状,您需要制定其他标准(梯形,圆形等)。这只是一个简单的示例:

import cv2
import numpy as np


img = cv2.imread('edges.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,150,255,cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)

for i in contours:
    img = cv2.imread('edges.png')
    size = cv2.contourArea(i)
    rect = cv2.minAreaRect(i)
    if size <10000:
        gray = np.float32(gray)
        mask = np.zeros(gray.shape, dtype="uint8")
        cv2.fillPoly(mask, [i], (255,255,255))
        dst = cv2.cornerHarris(mask,5,3,0.04)
        ret, dst = cv2.threshold(dst,0.1*dst.max(),255,0)
        dst = np.uint8(dst)
        ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
        corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
        if rect[2] == 0 and len(corners) == 5:
            x,y,w,h = cv2.boundingRect(i)
            if w == h or w == h +3: #Just for the sake of example
                print('Square corners: ')
                for i in range(1, len(corners)):
                    print(corners[i])
            else:
                print('Rectangle corners: ')
                for i in range(1, len(corners)):
                    print(corners[i])
        if len(corners) == 5 and rect[2] != 0:
            print('Rombus corners: ')
            for i in range(1, len(corners)):
                print(corners[i])
        if len(corners) == 4:
            print('Triangle corners: ')
            for i in range(1, len(corners)):
                print(corners[i])
        if len(corners) == 6:
            print('Pentagon corners: ')
            for i in range(1, len(corners)):
                print(corners[i])
        img[dst>0.1*dst.max()]=[0,0,255]
        cv2.imshow('image', img)
        cv2.waitKey(0)
        cv2.destroyAllWindows

输入图像描述

输出(在检测到所有形状后):

输入图像描述


3
如果您是一个外行人,希望您能简要介绍一下cv2.cornerSubPix()cv2.cornerHarris()的作用,那么这个答案将会更加完美。不过回答非常好! +1! - Jeru Luke
6
谢谢您的反馈。我尽量不写太多,因为我的英语不太好。已经努力简要介绍了这些功能。谢谢。 - kavko
3
不要害羞,勇于尝试。如果出现错误,会有人帮忙修改!做得好! - Jeru Luke
我必须为每个形状单独找到角落。图像中有6个形状。因此,我必须找到6组角落来代表所有形状。在您的情况下,它会给出图像中所有角落的集合。那么如何分离这些角落呢? - r_ranjan
您可以为每个轮廓制作一个掩模并在掩模上绘制它,然后搜索角点(对于轮廓中的c,绘制,角点检测)。 - kavko
显示剩余2条评论

0
对于每个形状,跟踪轮廓,并对于轮廓的每个像素,检查是否找到了附近的角落(例如在3x3或5x5邻域内)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接