问题
我正在处理一个项目,需要获取哑铃形状的边界框。然而,我需要尽可能少的点,并且盒子需要适合所有角落的形状。这是我制作的测试图片:模糊、破裂的哑铃形状
我不关心进入形状的间隙,我只想清理它,并使边缘变直,以便我可以获得像这样的形状轮廓:清理后的
我一直在试图用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:
我发现了一种有效的方法!我制作了自己的线检测系统,只检测水平或垂直线条,然后在检测到的线/轮廓边缘上,程序绘制一条黑色的线条,该线条横跨整个图像,从而有效地在轮廓的直线处切割图像。一旦它这样做了,就会得到被切碎的盒子的新轮廓,绘制出盒子的边界框,然后使用膨胀来填补空隙。到目前为止,它对大型形状效果良好,但当形状很小时,它倾向于失去一些形状。