消除末端未连接的线
Eliminate lines with an unconnected end
我有以下骨架:
我想从此图像中删除不属于循环的线。
我把这想象成一个过程,在这个过程中找到线的末端(用红点标记),然后这些线被吞噬直到有一个分支点(用蓝点标记)。
我还没有在 OpenCV 或 Scikit-Image 中找到针对此的操作。
这种变换有名称吗?有没有一种在 Python 中有效实施的方法?
我还上传了图片 here 以防上面的图片加载不正确。
在 Python 中,我还没有找到使用现有库执行此操作的好方法(尽管我希望有人能够为我指出一个),也没有找到它的名称。
所以我决定将此称为熔断变换,因为该算法的作用类似于像熔断器一样烧掉线条,直到它们分裂。
为了提高效率,我已经将下面的 Fuse Transform 实现为 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
#
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)
输入
结果
在下面的算法中,我首先将图像像素归一化为具有值 0 和 1。然后,我通过应用 3x3 非归一化框过滤器检查非零像素的 8 个连接的邻居。如果我们(按像素)乘以输入图像的滤波器输出,我们得到所有非零像素,这一次,它们的值告诉我们它们有多少个 8 连接的邻居加 1。因此,这里的中心像素将自己算作它的邻居。
红色是中心像素。黄色是它的 8-connected 社区。
我们应该剔除小于 3 的结果像素值。
代码会让事情更清楚。它可能不是很有效。我没有尝试深入研究 的代码。也许他正在高效地做类似的事情。
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
修剪后:
我有以下骨架:
我想从此图像中删除不属于循环的线。
我把这想象成一个过程,在这个过程中找到线的末端(用红点标记),然后这些线被吞噬直到有一个分支点(用蓝点标记)。
我还没有在 OpenCV 或 Scikit-Image 中找到针对此的操作。
这种变换有名称吗?有没有一种在 Python 中有效实施的方法?
我还上传了图片 here 以防上面的图片加载不正确。
在 Python 中,我还没有找到使用现有库执行此操作的好方法(尽管我希望有人能够为我指出一个),也没有找到它的名称。
所以我决定将此称为熔断变换,因为该算法的作用类似于像熔断器一样烧掉线条,直到它们分裂。
为了提高效率,我已经将下面的 Fuse Transform 实现为 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
#
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)
输入
结果
在下面的算法中,我首先将图像像素归一化为具有值 0 和 1。然后,我通过应用 3x3 非归一化框过滤器检查非零像素的 8 个连接的邻居。如果我们(按像素)乘以输入图像的滤波器输出,我们得到所有非零像素,这一次,它们的值告诉我们它们有多少个 8 连接的邻居加 1。因此,这里的中心像素将自己算作它的邻居。
红色是中心像素。黄色是它的 8-connected 社区。
我们应该剔除小于 3 的结果像素值。
代码会让事情更清楚。它可能不是很有效。我没有尝试深入研究
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
修剪后: