我正在开展一个项目,我需要获得像形状一样的dumbell边界框。但是,我需要尽可能少的点,并且盒子需要适合所有角落的形状。这是我要测试的图片:Blurry, cracked, dumbell shape
我不关心形状的间隙,我只是想清理它,并将边缘拉直,这样我就可以得到这样的形状轮廓:Cleaned up
我一直试图threshold()
使用findContours()
获取轮廓,然后使用approxPolyDP()
来简化轮廓最终的疯狂点数。所以,在摆弄了这个约三天之后,我怎么能简单地得到:
第二种选择是首选,因为这确实是我的最终目标:获得那些角落的点数。
我真的不需要有人为我编写代码,我只需要一些方法或算法来完成这项工作,最好用一些简单的例子。
这是我过于干净的代码,其中包含我甚至不使用的函数,但我想最终会使用它们:
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并将轮廓绘制到具有足够厚度的图像上,厚度绘制轮廓将填充任何足够小的孔,我可以使用新的blobby图像来获得边缘,然后将边缩回到尺寸。这是我的想法。尽管如此,还有另一种更好的方式......
我想得到12分;一个形状的每个角落。
在尝试了一些侵蚀和扩张之后,似乎最好的选择是在某些点处切割轮廓,然后在切片形状周围使用边界框来获得正确的四四方角,然后进行一些计算以重新加入盒子成一个形状。一个相当有趣的挑战......
我发现了一些效果很好的东西!我制作了自己的线检测系统,只检测水平或垂直线,然后在检测到的线/轮廓边缘,程序绘制一条延伸到整个图像的黑线,从而有效地切割图像的直线。轮廓。一旦它这样做,它会获得切片盒的新轮廓,在片段周围绘制边界框,然后使用扩张来封闭间隙。到目前为止,它在大型形状上效果很好,但是当形状很小时,它往往会失去一些形状。
答案 0 :(得分:1)
所以,在摆弄了侵蚀,扩张,关闭,打开和看直线轮廓之后,我找到了一个有效的解决方案。谢谢@Ante和@ a.alsram!你的两个想法结合起来帮助我找到了解决方案。所以这就是它的工作原理。
程序迭代每个轮廓,并遍历轮廓中的每对点,寻找位于同一轴上的点对并计算它们之间的距离。如果距离大于可调阈值,则程序确定这些点被视为形状上的边缘。然后程序使用该边缘,沿整个轮廓绘制一条黑线,从而切割该边缘的轮廓。然后程序重新确定轮廓,因为形状被切断。被切断的这些碎片知道它们自己的轮廓,然后由边界框界定。最后,所有形状都被扩张和侵蚀(接近)以重新加入被切断的盒子。
这种方法可以多次完成,但每次都有一点精度损失。但它适用于我需要的东西,当然是一个有趣的挑战!谢谢你的帮助!
natebot13
答案 1 :(得分:0)
也许简单的解决方案可以提供帮助。如果有一个阈值长度来填补空白,
可以在具有单元长度> =阈值的网格中分割图像,并使用
内有东西的细胞。有了这个,只有横向和
垂直线条,并注意网格跟随原始水平线
它将覆盖主线特征。
<强>更新强>
看看mathematical morphology。我认为使用结构元素(2 * k + 1)x(2 * k + 1)像素的结束操作可以做你想要的。
算法应采用阈值参数k,并执行扩张和侵蚀。这意味着更改图像,以便为每个白色像素设置距离k((2 * k + 1)x(2 * k + 1)框)上的所有邻居到白色,然后改变图像,以便为每个黑色像素设置邻居距离k到黑色。
对边界像素进行操作就足够了。