OpenCV多边形的凹凸角点

nat*_*t13 5 python opencv image-processing contour python-2.7

问题

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

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

我一直在尝试解决threshold()这个问题,使用它来获取它的轮廓findContours(),然后使用它approxPolyDP()来简化轮廓最终的疯狂数量的点。因此,在摆弄了大约三天之后,我怎样才能简单地得到:

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

第二种选择是首选,因为这确实是我的最终目标:获得这些角落的分数。

有几点需要注意:

  • 我正在使用 OpenCV for Python
  • 输入图像中通常会有许多各种大小的形状
  • 它们只有水平或垂直定位。没有奇怪的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()
Run Code Online (Sandbox Code Playgroud)

代码说明

我知道我可能不会在这里做一些事情,但是嘿,我为此感到自豪:)

所以,我的想法是,这些哑铃中经常有孔和间隙,所以我想,如果我迭代从 127 到 255 的所有阈值,并将轮廓绘制到具有足够大厚度的图像上,则绘制的厚度轮廓将填充任何足够小的孔,我可以使用新的斑点图像来获取边缘,然后将侧面缩小到尺寸。这就是我的想法。但必须有另一种更好的方法......

概括

我想最终得到12分;形状的每个角都有一个。

编辑:

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

编辑2:

我发现了一些效果很好的东西!我制作了自己的线检测系统,仅检测水平线或垂直线,然后在检测到的线/轮廓边缘上,程序绘制一条延伸穿过整个图像的黑线,从而有效地在图像的直线处切片轮廓。一旦完成,它就会获得切片框的新轮廓,在各个块周围绘制边界框,然后使用膨胀来缩小间隙。到目前为止,它在大形状上效果很好,但当形状很小时,它往往会失去一些形状。

nat*_*t13 1

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

方法

该程序迭代每个轮廓以及轮廓中的每对点,查找位于同一轴上的点对并计算它们之间的距离。如果距离大于可调整阈值,程序会决定将这些点视为形状上的边缘。然后程序使用该边缘,并沿着整个轮廓绘制一条黑线,从而在该边缘处切割轮廓。然后程序重新确定轮廓,因为形状已被切割。这些被切掉的碎片知道它们自己的轮廓,然后由边界框界定。最后,所有形状都被膨胀和腐蚀(闭合)以重新连接被切断的盒子。

这种方法可以进行多次,但每次都会有一点精度损失。但它可以满足我的需要,而且当然是一个有趣的挑战!谢谢你们的帮助!

内特机器人13