【问题标题】:OpenCV concave and convex corner points of polygons多边形的OpenCV凹凸角点
【发布时间】:2013-07-11 14:24:02
【问题描述】:

问题

我正在做一个项目,我需要获得类似哑铃形状的边界框。但是,我需要尽可能少的点,并且盒子需要适合所有角落的形状。这是我用来测试的图片:Blurry, cracked, dumbell shape

我不关心形状中的间隙,我只想清理它,拉直边缘,这样我就可以得到这样的形状的轮廓:Cleaned up

我一直在尝试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,这样人们可以更快地为您提供帮助。
  • "输入图像中通常会有很多各种大小的这些形状" 如果这些形状不相交,则将它们提取并一一处理。 “它们将只有水平或垂直定位。没有奇怪的 27 度角......”如果你的轮廓只是水平或垂直的,只需提取轮廓,消除倾斜的轮廓,然后稍微拉长剩余的轮廓以缩小潜在的差距。跨度>
  • 感谢您的意见@Aurelius!对此,我真的非常感激!我一定会尝试用更简洁的声明来更新我的代码。
  • @a.lasram 这不是一个坏主意,但有时轮廓由数百个点组成,为了知道轮廓的角度,您不需要两个点来获得角度吗?考虑到图像可能是巨大的,我对遍历许多点有点警惕。但是在考虑了您的方法之后,似乎它可能会起作用。让我思考一些事情。

标签: python opencv image-processing python-2.7 contour


【解决方案1】:

因此,在处理了腐蚀、膨胀、闭合、打开和查看直线轮廓之后,我找到了一个可行的解决方案。谢谢@Ante 和@a.alsram!您的两个想法结合在一起帮助我找到了解决方案。下面是它的工作原理。

方法

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

这种方法可以做多次,但每次都会有一点点精度损失。但它适用于我需要的东西,当然是一个有趣的挑战!谢谢你们的帮助!

natebot13

【讨论】:

    【解决方案2】:

    也许简单的解决方案会有所帮助。如果有一个阈值长度来弥补差距, 可以在单元格长度 >= 阈值的网格中分割图像,并使用 里面有东西的细胞。这样,将只有水平和 垂直线,并注意网格以遵循原始水平 和垂直线,它将覆盖主线特征。

    更新

    看看mathematical morphology。我认为使用结构元素 (2*k+1)x(2*k+1) 像素的关闭操作可以满足您的需求。

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

    对边界像素进行操作就足够了。

    【讨论】:

    • 我一定会调查并尝试一下!谢谢。现在尝试将我的头脑包裹在所有的方程式和符号上......要学习的东西很多!
    • 好的,所以经过一些使用膨胀和腐蚀的测试和实验,最终的图像看起来像this. 差不多了......但不完全是。
    • 有圆角。您将圆形用于相邻区域,而不是使用长度为 2k+1 的正方形。右下的“洞”是因为参数k,尝试增加它。
    • 我使用的是方形结构元素,具有 (2k+1),但圆角仍然存在。另外,右下角的“洞”可以修复,只要结构元素真的很大,而在最终程序中,结构元素必须保持小。事实上,它必须是最小的,(3,3)。但是,我已经想出了如何很好地完成这项工作,我将发布我自己的问题的答案。
    猜你喜欢
    • 2020-11-02
    • 2012-11-05
    • 2011-05-09
    • 2011-01-28
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 2013-04-25
    • 2018-05-08
    相关资源
    最近更新 更多