我有以下骨架:
从这张图片中,我想消除不属于循环的线条。
我想象这是一个过程,其中找到线的末端(标有红色点),然后将线咬入直到有分支的点(标有蓝色点)。
我尚未在OpenCV或Scikit-Image中找到用于此目的的操作。
这种转换是否有名称?有没有一种方法可以有效地在Python中实现它?
我还上传了图片here,以防上述图片无法正确加载。
答案 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)
答案 1 :(得分:1)
在下面的算法中,我首先将图像像素标准化为零和一。然后,我通过应用3x3非标准化盒式滤波器来检查非零像素的8个相连的邻居。如果我们将输入图像的滤波器输出乘以像素(像素),则会得到所有非零像素,这一次,它们的值告诉我们他们有8个相邻的8个邻居加1。因此,此处中心像素会将其自身计为它的邻居。
红色是中心像素。黄色是它的8个邻域。
我们应该消除小于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
修剪后: