OpenCV多边形的凸和凹角点

5

问题

我正在处理一个项目,需要获取哑铃形状的边界框。然而,我需要尽可能少的点,并且盒子需要适合所有角落的形状。这是我制作的测试图片:模糊、破裂的哑铃形状

我不关心进入形状的间隙,我只想清理它,并使边缘变直,以便我可以获得像这样的形状轮廓:清理后的

我一直在试图用threshold()来解决它,使用findContours()获取其轮廓,然后使用approxPolyDP()简化轮廓最终成为的大量点数。所以,经过三天的摸索,如何简单地获得以下内容:

  • 指定哑铃两端和中间的矩形的两个框,或者
  • 一个包含所有角落十二个点的轮廓

第二个选项更好,因为那真的是我的最终目标:获取那些角落的点。

需要注意的几件事:

  • 我正在使用Python的OpenCV
  • 通常会有许多这些大小不一的形状分布在输入图像的各个位置
  • 它们只会水平或垂直定位。没有奇怪的27度角...

我需要什么:

我真的不需要别人为我编写代码,我只需要一些方法或算法来完成这项工作,最好附带一些简单的示例。

我的代码

这是我的过于干净的代码,其中包含我甚至不使用但认为我最终会使用它们的函数:

import cv2
import numpy as np

class traceImage():

    def __init__(self, imageLocation):
        self.threshNum = 127
        self.im = cv2.imread(imageLocation)
        self.imOrig = self.im
        self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)
        self.ret, self.imThresh = cv2.threshold(self.imGray, self.threshNum, 255, 0)
        self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    def createGray(self):
        self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)

    def adjustThresh(self, threshNum):
        self.ret, self.imThresh = cv2.threshold(self.imGray, threshNum, 255, 0)

    def getContours(self):
        self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    def approximatePoly(self, percent):
        i=0
        for shape in self.contours:
            shape = cv2.approxPolyDP(shape, percent*cv2.arcLength(shape, True), True)
            self.contours[i] = shape
            i+=1

    def drawContours(self, blobWidth, color=(255,255,255)):
        cv2.drawContours(self.im, self.contours, -1, color, blobWidth)

    def newWindow(self, name):
        cv2.namedWindow(name)

    def showImage(self, window):
        cv2.imshow(window, self.im)

    def display(self):
        while True:
            cv2.waitKey()

    def displayUntil(self, key):
        while True:
            pressed = cv2.waitKey()
            if pressed == key:
                break

if __name__ == "__main__":
    blobWidth = 30
    ti = traceImage("dumbell.png")
    ti.approximatePoly(0.01)
    for thresh in range(127,256):
        ti.adjustThresh(thresh)
        ti.getContours()
        ti.drawContours(blobWidth)
    ti.showImage("Image")
    ti.displayUntil(10)
    ti.createGray()
    ti.adjustThresh(127)
    ti.getContours()
    ti.approximatePoly(0.0099)
    ti.drawContours(2, (0,255,0))
    ti.showImage("Image")
    ti.display()

代码解释

我知道我的做法可能不完美,但是我为此感到自豪 :)

我的想法是在这些哑铃中经常会有空洞和缝隙,所以我想如果我迭代从127到255的所有阈值并使用足够大的粗细将轮廓绘制到图像上,绘制轮廓的厚度会填补任何足够小的空洞,然后我可以使用新的、肥胖的图像来获取边缘,然后将边缘缩小到适当的大小。这就是我的想法。当然还有其他更好的方法...

总结

我希望最终得到12个点,每个角落一个。

编辑:

尝试了一些腐蚀和膨胀后,最好的选择似乎是在某些点处切割轮廓,然后在切割形状周围使用边界框来获得正确的角落,然后进行一些计算将边界框重新组合成一个形状。这是一个相当有趣的挑战...

编辑 2:

我发现了一种有效的方法!我制作了自己的线检测系统,只检测水平或垂直线条,然后在检测到的线/轮廓边缘上,程序绘制一条黑色的线条,该线条横跨整个图像,从而有效地在轮廓的直线处切割图像。一旦它这样做了,就会得到被切碎的盒子的新轮廓,绘制出盒子的边界框,然后使用膨胀来填补空隙。到目前为止,它对大型形状效果良好,但当形状很小时,它倾向于失去一些形状。


欢迎来到StackOverflow!您已经很好地定义了您的问题,但是您发布的代码包含许多与实际问题无关的元素。在所有声明中很难看出您实际正在做什么。考虑发布一个SSCCE,这将使人们更快地帮助您。 - Aurelius
输入图像中通常会有许多这些形状,大小不一。如果这些形状没有相交,就将它们提取出来并逐个处理。它们只会水平或垂直定位。没有奇怪的27度角度...如果您的轮廓只是水平或垂直的,请提取轮廓,消除倾斜的轮廓,然后稍微拉伸剩余的轮廓以关闭潜在的间隙。 - a.lasram
感谢您的输入@Aurelius!我非常感激!我一定会尝试使用更简洁的声明来更新我的代码。 - natebot13
@a.lasram 这不是个坏主意,但有时轮廓由数百个点组成,想要知道轮廓的角度,难道不需要两个点才能得到角度吗?我有点担心迭代许多点,因为图像可能会很巨大。但在考虑了你的方法后,似乎它可能有效。给了我一些思考的东西。 - natebot13
2个回答

1

所以,在尝试腐蚀、膨胀、闭合、开放和查看直线轮廓之后,我已经找到了一个可行的解决方案。谢谢@Ante和@a.alsram!你们两个的想法结合起来帮助我找到了解决方案。那么它是如何工作的呢?

方法

程序遍历每个轮廓,并在轮廓中的每对点上迭代,寻找位于同一轴上的点对并计算它们之间的距离。如果距离大于可调节的阈值,则程序认为这些点被视为形状的边缘。然后程序使用该边缘,沿整个轮廓画一条黑线,从而在该边缘处切割轮廓。然后程序重新确定轮廓,因为形状被切割了。这些被切割的部分知道自己的轮廓,然后由边界框限定。最后,所有形状都被膨胀和侵蚀(闭合),以重新连接被切断的盒子。

这种方法可以多次执行,但每次都会有一点精度损失。但它对我需要的东西有效,肯定是一个有趣的挑战!感谢你们的帮助!

natebot13


0

也许简单的解决方案可以帮助。如果有一个阈值长度来关闭间隙,那么可以将图像分成网格,其中单元格长度>=阈值,并使用具有内部内容的单元格。这样只会有水平和垂直线,并通过注意网格以遵循原始水平和垂直线来覆盖主要线条特征。

更新

看一下数学形态学。我认为使用结构元素(2*k+1)x(2*k+1)像素的闭合操作可以实现您要寻找的内容。

算法应该采用阈值参数k,并执行膨胀,然后进行腐蚀。这意味着更改图像,使得对于每个白色像素,将所有距离k ((2*k+1)x(2*k+1)框)的邻居设置为白色,然后更改图像,使得对于每个黑色像素,将距离k的邻居设置为黑色。

仅在边界像素上执行操作即可。


我一定会研究并尝试的!谢谢。现在要试着理解所有的方程式和符号...还有很多要学习! - natebot13
好的,经过一些膨胀和腐蚀的测试和实验后,最终图像看起来像这样。 它已经接近了...但还不够。 - natebot13
有圆角。你正在使用圆形来表示相邻区域,而应该使用边长为2k+1的正方形。右下角的“洞”是由于参数k造成的,请尝试增加它。 - Ante
我正在使用一个方形结构元素,大小为(2k+1),但是圆角仍然存在。此外,右下角的“孔”只有在结构元素非常大的情况下才能修复,而在最终程序中,结构元素必须保持小巧,即(3,3)。不过,我已经想出了如何很好地解决这个问题,并将在我的问题下发布答案。 - natebot13

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