【问题标题】:Using Houghlines on a binary image, to identify the horizontal and vertical components and then "removing" them by drawing them in black在二值图像上使用 Houghlines 来识别水平和垂直分量,然后通过将它们绘制为黑色来“移除”它们
【发布时间】:2017-07-25 03:22:19
【问题描述】:

在这个original image 上,我正在尝试创建一个具有黑色背景和白色点的二进制图像,以便我可以拟合它们周围的曲线。 here is the image after thresholding, dilating, corroding and blurring
我打算通过在二进制图像上使用 Houghlines 来识别水平和垂直分量,然后通过将它们绘制为黑色来“移除”它们,但是我的代码仅以灰度返回原始图像,而不是一堆白点在黑色背景上准备用作坐标以拟合它们周围的曲线

    erosion = cv2.erode(img,kernel,iterations = 500)
    edges = cv2.Canny(img,0,255)
    lines = cv2.HoughLines(edges, 1, np.pi/180, 0, 0, 0)
    for rho,theta in lines[0]:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a*rho
        y0 = b*rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))   
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))

        line = cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)

    cv2.imshow("blackwave.PNG", line)
    cv2.imwrite("blackwave.PNG", line)
    cv2.waitKey(0) 
else:
    print 'Image could not be read'

【问题讨论】:

  • 提示:为什么不尝试显示每个中间图像以确认您的处理步骤正在采用正确的输入并产生一些看起来合理的输出,即按您希望的方式工作(/expect/想象/猜测/...)。当您在图像上绘制线条时,包括对图像的更新。所以,为了清楚起见,在每一步显示dst,然后显示bg,然后显示新的bg,等等。
  • cv2.line() 修改您传入的图像。所以你传入img,它会在上面画黑线。相反,您可能希望在阈值图像上绘制,因此您将其传递到行中。并且没有返回值,它只是修改图像。此外,您的程序中有许多行没有做任何事情或没有被使用。我同意上面的@barny,通过显示图像来感受每一行。并确保您在使用每个函数时都在查找它的文档,并确保您了解输出!

标签: python-2.7 opencv


【解决方案1】:

作为我自己的学习练习,我花了一段时间试图解决这个问题的图像分析部分。在某些方面,我有点不愿意给你一个解决方案,因为我认为你已经展示了这种情况发生在你身上的影响——你还没有学会如何使用 cv,所以你必须提出更多问题来寻找解决方案而不是弄清楚如何为自己调整代码。 OTOH,不分享我所做的事情感觉很无礼。

不要要求我“请更改/改进/让它正常工作” - 这段代码可以完成它的工作,如果你想让它做一些不同的事情,那就开始编码吧:现在交给你了。

我将您的原始图像保存在 sineraw.png 文件中。

代码经过以下步骤:

1。读取原始图像,已经是灰度的

2。在第一步中均衡图像以获得二值(黑/白)图像

3。做一个自适应阈值以获得黑白图像,仍然有很多噪音

4。执行腐蚀以从阈值图像中去除任何非常小的噪声点

5。对阈值图像执行连通分量分析,然后仅将“大”斑点存储到掩码中

6。将蒙版骨架化为 skel

7.现在查找并用黑色覆盖接近水平和接近垂直的线

最终图像应该适合使用曲线拟合,因为只有曲线以白色像素显示。这对你来说是另一个练习。

顺便说一句,您真的应该得到更好的源图像。

我想还有其他可能更好的方法可以实现与最终图像中所示相同的效果,但这适用于您的源图像。如果它不适用于其他图像,那么你有源代码,开始编辑。

在此过程中,我探索了一些选项,例如不同的自适应阈值,高斯似乎更擅长不在图片边缘放置白色。我还探索了在图片周围绘制黑线以消除边缘噪声,并使用标签去除图片边缘的所有白色,但会去除上升到边缘的主曲线。我还尝试了更多的侵蚀/扩张/打开/关闭,但放弃并使用了骨架化,因为它保留了形状并且还很高兴地为曲线留下了中心线。

代码

import copy
import cv2 
import numpy as np
from skimage import measure

# seq is used to give the saved files a sequence so it is easier to understand the sequence
seq = 0

# utility to save/show an image and optionally pause
def show(name,im, pause=False, save=False):
    global seq
    seq += 1
    if save:
        cv2.imwrite(str(seq)+"-"+name+".PNG", im)
    cv2.imshow(str(seq)+"-"+name+".PNG",im)
    if pause:
        cv2.waitKey(0) 

# utility to return True if theta is approximately horizontal        
def near_horizontal(theta):
    a = np.sin(theta)
    if a > -0.1 and a < 0.1:
        return True
    return False

# utility to return True if theta is approximately vertical
def near_vertical(theta):
    return near_horizontal(theta-np.pi/2.0)

################################################    
# 1. read raw image, already grayscale
src = cv2.imread('sineraw.PNG',0)
show("src",src, save=True)

################################################    
# 2. equalize the image in the first step to getting a binary (black/white) image
gray = cv2.equalizeHist(src)
show("gray",gray, save=True)

################################################    
# 3. do an adaptive threshold to get a black/white image, still got lots of noise
# I tried a range of parameters for the 41,10 - may vary by image, not sure
dst = cv2.adaptiveThreshold(gray, 255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY_INV,41,10)  
show("dst",dst, save=True)

################################################    
# 4. perform an erosion to remove any very small dots of noise from the thresholded image
erode1 = cv2.erode(dst, None, iterations=1)
show( "erode1",erode1, save=True)

################################################    
# 5. perform a connected component analysis on the thresholded image, then store only the "large" blobs into mask
labels = measure.label(erode1, neighbors=8, background=0)
# mask is initially all black
mask = np.zeros(erode1.shape, dtype="uint8")
# loop over the unique components
for label in np.unique(labels):
    # if this is the background label, ignore it
    if label == 0:
        continue

    # otherwise, construct the mask for this label and count the
    # number of pixels
    labelMask = np.zeros(erode1.shape, dtype="uint8")
    labelMask[labels == label] = 255
    numPixels = cv2.countNonZero(labelMask)

    # if the number of pixels in the component is sufficiently
    # large, then add it to our mask of "large blobs"
    if numPixels > 50:
        # add the blob into mask
        mask = cv2.add(mask, labelMask)
show( "mask", mask, save=True )

################################################    
# 6. skeletonize mask into skel
img = copy.copy(mask)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
done = False
size = np.size(img)
# the skeleton is initially all black
skel = np.zeros(img.shape,np.uint8)     
while( not done):
    eroded = cv2.erode(img,element)
    temp = cv2.dilate(eroded,element)
    temp = cv2.subtract(img,temp)
    skel = cv2.bitwise_or(skel,temp)
    img = eroded.copy()
#        show( "tempimg",img)
    zeros = size - cv2.countNonZero(img)
    if zeros==size:
        done = True
show( "skel",skel, save=True )

################################################    
# 7. Now look for and overwrite near-horizontal and near-vertical lines with black
lines = cv2.HoughLines(skel, 1, np.pi/180, 100)
for val in lines:
    (rho,theta)=val[0]    
    a = np.cos(theta)
    b = np.sin(theta)
    if not near_horizontal(theta) and not near_vertical(theta):
        print "ignored line",rho,theta
        continue
    print "line",rho, theta, 180.0*theta/np.pi
    x0 = a*rho
    y0 = b*rho
    # this is pretty kulgey, should be able to use actual image dimensions, but this works as long as image isn't too big
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))   
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    print "line",rho, theta, 180.0*theta/np.pi,x0,y0,x1,y1,x2,y2
    cv2.line(skel,(x1,y1),(x2,y2),0,3)
################################################
# the final image is now in skel    
show("final",skel, pause=True,save=True)

【讨论】:

  • 不错的解决方案。特别是均衡和骨架化是个好主意。我创建了a solution 用于在 OP 中的一个关于此主题的其他问题中的二值化步骤 - 请随时查看以比较这两种方法。
  • 顺便说一句,我还创建了一个删除线的解决方案(使用展示的不同方法 here)并执行了曲线拟合,但决定不将这些解决方案用勺子喂给 OP。
  • @WhoIsJack 是的,我对用勺子喂食有两种看法,而且可能过于乐观,但你永远不知道也许更容易看到处理步骤以及如何查看正在发生的事情,OP 可以在理解如何使用自己可用的一些图像处理选项方面取得一些进展。
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 2021-07-06
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多