在二值图像上使用 Houghlines,识别水平和垂直分量,然后通过将它们绘制成黑色来“去除”它们

1 opencv python-2.7

在这个原始图像上,我试图创建一个带有黑色背景和白点的二值图像,以便我可以拟合它们周围的曲线。这是经过阈值处理、扩张、腐蚀和模糊处理后的图像
我打算通过在二值图像上使用 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'
Run Code Online (Sandbox Code Playgroud)

bar*_*rny 5

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

不要让我“请改变/改进/让它工作” - 这段代码做了它所做的,如果你想让它做一些不同的事情,那就开始编码:现在就交给你了。

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

代码经过以下步骤:

1.读取原始图像,已经灰度

在此处输入图片说明

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

在此处输入图片说明

3.做一个自适应阈值来得到一个黑白图像,仍然有很多噪音

在此处输入图片说明

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

在此处输入图片说明

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

在此处输入图片说明

6.将面具骨架化为骨架

在此处输入图片说明

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)
Run Code Online (Sandbox Code Playgroud)