消除末端未连接的线路

时间:2018-08-07 22:35:01

标签: python opencv image-processing image-segmentation scikit-image

我有以下骨架:

Skeleton of Image

从这张图片中,我想消除不属于循环的线条。

我想象这是一个过程,其中找到线的末端(标有红色点),然后将线咬入直到有分支的点(标有蓝色点)。

我尚未在OpenCV或Scikit-Image中找到用于此目的的操作。

这种转换是否有名称?有没有一种方法可以有效地在Python中实现它?

我还上传了图片here,以防上述图片无法正确加载。

2 个答案:

答案 0 :(得分:2)

我还没有找到一种使用现有库在Python中执行此操作的好方法(尽管我希望有人能够为我指出一个),也没有找到它的名称。

所以我决定将其称为保险丝变换,因为算法的作用类似于像保险丝一样将线烧掉,直到它们分开。

为了实现效率,我将下面的保险丝变换实现为Cython函数。

该算法需要在矩阵大小中执行一次 O(N)次,以进行扫描以识别种子细胞(位于保险丝起点的那些细胞),然后确定 O (N)时间,以消除熔丝的长度。

保险丝变换算法

%%cython -a --cplus
import numpy as np
import cv2
import skimage.morphology as skm
import cython
from libcpp.queue cimport queue
cimport numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True) 
#Richard's Fuse Transform
#https://stackoverflow.com/a/51738867/752843
cpdef void FuseTransform(unsigned char [:, :] image):
    # set the variable extension types
    cdef int c, x, y, nx, ny, width, height, neighbours
    cdef queue[int] q

    # grab the image dimensions
    height = image.shape[0]
    width  = image.shape[1]

    cdef int dx[8]
    cdef int dy[8]

    #Offsets to neighbouring cells
    dx[:] = [-1,-1,0,1,1,1,0,-1]
    dy[:] = [0,-1,-1,-1,0,1,1,1]

    #Find seed cells: those with only one neighbour
    for y in range(1, height-1):
        for x in range(1, width-1):
            if image[y,x]==0: #Seed cells cannot be blank cells
                continue
            neighbours = 0
            for n in range(0,8):   #Looks at all neighbours
                nx = x+dx[n]
                ny = y+dy[n]
                if image[ny,nx]>0: #This neighbour has a value
                    neighbours += 1
            if neighbours==1:      #Was there only one neighbour?
                q.push(y*width+x)  #If so, this is a seed cell

    #Starting with the seed cells, gobble up the lines
    while not q.empty():
        c = q.front()
        q.pop()
        y = c//width         #Convert flat index into 2D x-y index
        x = c%width
        image[y,x] = 0       #Gobble up this part of the fuse
        neighbour  = -1      #No neighbours yet
        for n in range(0,8): #Look at all neighbours
            nx = x+dx[n]     #Find coordinates of neighbour cells
            ny = y+dy[n]
            #If the neighbour would be off the side of the matrix, ignore it
            if nx<0 or ny<0 or nx==width or ny==height:
                continue
            if image[ny,nx]>0:      #Is the neighbouring cell active?
                if neighbour!=-1:   #If we've already found an active neighbour
                    neighbour=-1    #Then pretend we found no neighbours
                    break           #And stop looking. This is the end of the fuse.
                else:               #Otherwise, make a note of the neighbour's index.
                    neighbour = ny*width+nx
        if neighbour!=-1:           #If there was only one neighbour
            q.push(neighbour)       #Continue burning the fuse

#Read in image
img         = cv2.imread('part.jpg')
ShowImage('Original',img,'bgr')

#Convert image to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#Apply Otsu's method to eliminate pixels of intermediate colour
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)

#Apply the Fuse Transform
skh_dilated = skelhuman.copy()
FuseTransform(skh_dilated)

输入

Skeleton

结果

Fuse Transform

答案 1 :(得分:1)

在下面的算法中,我首先将图像像素标准化为零和一。然后,我通过应用3x3非标准化盒式滤波器来检查非零像素的8个相连的邻居。如果我们将输入图像的滤波器输出乘以像素(像素),则会得到所有非零像素,这一次,它们的值告诉我们他们有8个相邻的8个邻居加1。因此,此处中心像素会将其自身计为它的邻居。

红色是中心像素。黄色是它的8个邻域。

8-neighbors

我们应该消除小于3的结果像素值。

代码将使事情变得更清楚。它可能不是很有效。我没有尝试深入研究Richard的代码。可能是他在有效地做类似的事情。

import cv2
import numpy as np

im = cv2.imread('USqDW.png', 0)

# set max pixel value to 1
s = np.uint8(im > 0)

count = 0
i = 0
while count != np.sum(s):
    # non-zero pixel count
    count = np.sum(s)
    # examine 3x3 neighborhood of each pixel
    filt = cv2.boxFilter(s, -1, (3, 3), normalize=False)
    # if the center pixel of 3x3 neighborhood is zero, we are not interested in it
    s = s*filt
    # now we have pixels where the center pixel of 3x3 neighborhood is non-zero
    # if a pixels' 8-connectivity is less than 2 we can remove it
    # threshold is 3 here because the boxfilter also counted the center pixel
    s[s < 3] = 0
    # set max pixel value to 1
    s[s > 0] = 1
    i = i + 1

修剪后:

prune