Python 特定点的图像卷积
在scipy(或其他类似的库)中,是否有一种方法可以仅在某些需要的点上获得图像与给定内核的卷积 我要找的东西是:Python 特定点的图像卷积,python,numpy,scipy,convolution,scikit-image,Python,Numpy,Scipy,Convolution,Scikit Image,在scipy(或其他类似的库)中,是否有一种方法可以仅在某些需要的点上获得图像与给定内核的卷积 我要找的东西是: ndimage.convolve(image, kernel, mask=mask) 当需要应用内核时,mask包含True(或1),否则为False(或0) 编辑:示例python代码,它完成了我正在尝试做的事情(但不会比使用scipy的整个图像卷积更快): 上面的代码使用wrap边界条件来完成这项工作,但是内部循环是用python编写的,因此速度很慢。我想知道在scipy/op
ndimage.convolve(image, kernel, mask=mask)
当需要应用内核时,mask
包含True
(或1
),否则为False
(或0
)
编辑:示例python代码,它完成了我正在尝试做的事情(但不会比使用scipy的整个图像卷积更快):
上面的代码使用
wrap
边界条件来完成这项工作,但是内部循环是用python编写的,因此速度很慢。我想知道在scipy
/opencv
/skimage
中是否已经实现了一些更快的功能,我不知道有任何函数能够完全满足您的要求。如果您没有提供要卷积的点的掩码,而是提供了一个点列表,例如[(7,7),(100,100)]
,那么它可能很简单,只需获得适当的图像修补程序(例如与您提供的内核大小相同),卷积图像修补程序和内核,然后插入到原始图像中
下面是一个编码示例,希望它足够近,您可以轻松修改:
[编辑:我注意到填充和修补算法中有几个错误。以前,你不能用边界上的一个点(比如(0,0))进行卷积,我将填充加倍,修正了一些算法,现在一切正常。]
import cv2
import numpy as np
from scipy import ndimage
from matplotlib import pyplot as plt
def image_convolve_mask(image, list_points, kernel):
# list_points ex. [(7, 7), (100, 100)]
# assuming kernels of dims 2n+1 x 2n+1
rows, cols = image.shape
k_rows, k_cols = kernel.shape
r_pad = int(k_rows/2)
c_pad = int(k_cols/2)
# zero-pad the image in case desired point is close to border
padded_image = np.zeros((rows + 2*k_rows, cols + 2*k_cols))
# set the original image in the center
padded_image[k_rows: rows + k_rows, k_cols: cols + k_cols] = image
# should you prefer to use np.pad:
# padded_image = np.pad(image, (k_rows, k_cols), 'constant', constant_values=(0, 0))
for p in list_points:
# extract pertinent patch from image
# arbitrarily choosing the patch as same size as the kernel; change as needed
patch = padded_image[p[0] + k_rows - r_pad: p[0] + 2*k_rows - r_pad, p[1] + k_cols - c_pad: p[1] + 2*k_cols - c_pad]
# here use whatever function for convolution; I prefer cv2filter2D()
# commented out is another option
# conv = ndimage.convolve(patch, kernel, mode='constant', cval=0.0)
conv = cv2.filter2D(patch, -1, kernel)
# set the convolved patch back in to the image
padded_image[p[0] + k_rows - r_pad: p[0] + 2*k_rows - r_pad, p[1] + k_cols - c_pad: p[1] + 2*k_cols - c_pad] = conv
return padded_image[k_rows: rows + k_rows, k_cols: cols + k_cols]
现在要在图像上试用它:
penguins = cv2.imread('penguins.png', 0)
kernel = np.ones((5,5),np.float32)/25
# kernel = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], np.float32)
conv_image = image_convolve_mask(penguins, [(7, 7), (36, 192), (48, 207)], kernel)
plt.imshow(conv_image, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([])
plt.show()
我使用了一个5x5盒平滑器,在像素(7,7)周围看不到任何变化,但我选择了另外两个点作为最左边的两个企鹅喙的尖端。因此,您可以看到平滑的面片。
这是一个lena512图像,有21个卷积点(时间:0.006177秒)。
[编辑2:使用掩码生成行、列元组列表以输入函数的示例。]
mask = np.eye(512)
k = np.ones((25, 25), np.float32)/625
list_mask = zip(np.where(mask==1)[0], np.where(mask==1)[1])
tic = time.time()
conv_image = image_convolve_mask(lena, list_mask, k)
print 'time: ', time.time()-tic # 0.08136 sec
您可以使用以下代码段。如果遮罩足够密集,它可能不会那么低效
def mask_conv(img, kernel, mask):
out = filters.convolve(img, kernel)
return np.where(mask, out, img)
一些示例代码
from skimage import data, draw, io, color
from scipy.ndimage import filters
import numpy as np
def mask_conv(img, kernel, mask):
out = filters.convolve(img, kernel)
return np.where(mask, out, img)
img = data.camera()
mask = np.zeros_like(img, dtype=np.bool)
kernel = np.ones((9,9))/100
circle = draw.circle(300, 350, 100)
mask[circle] = True
out = mask_conv(img, kernel, mask)
io.imshow(out)
io.show()
我知道我是在回应我自己的答案,我希望下面的代码能够进一步改进,或者对其他用户有用 下面的代码是cython/python实现: PYTHON:
def py_convolve(im, kernel, points):
ks = kernel.shape[0]//2
data = np.pad(im, ks, mode='constant', constant_values=0)
return cy_convolve(data, kernel, points)
赛顿:
import numpy as np
cimport cython
@cython.boundscheck(False)
def cy_convolve(unsigned char[:, ::1] im, double[:, ::1] kernel, Py_ssize_t[:, ::1] points):
cdef Py_ssize_t i, j, y, x, n, ks = kernel.shape[0]
cdef Py_ssize_t npoints = points.shape[0]
cdef double[::1] responses = np.zeros(npoints, dtype='f8')
for n in range(npoints):
y = points[n, 0]
x = points[n, 1]
for i in range(ks):
for j in range(ks):
responses[n] += im[y+i, x+j] * kernel[i, j]
return np.asarray(responses)
与其他方法的比较
下表显示了对4种方法的评估:
coins
、camera
和lena
分别来自skimage.data
),每列对应于计算内核响应的不同点数(以百分比表示)“计算图像点的x%
响应”)
对于在小于50%
的点中计算内核响应,我的实现比卷积整个映像更快,但在其他方面则不快
编辑:测试的内核窗口是5x5统一窗口(np.ones((5,5))
)
注意:时间在
ms
@tom10不是关于忽略评估中的点。其想法是忽略某些点的评估。我不想卷积整个映像,我只想在给定(比如20)点上获得内核响应。我想通过计算给定点上的内核响应”。谢谢!这与我想要的非常接近(+1),但我仍然需要检查它的性能。python循环可能比使用scipy或opencv卷积整个图像要慢。不客气。如果你真的想使用数组掩码,显然你可以取下掩码,退出(行,列),并将其转换为元组列表。就性能而言,这一切都取决于您拥有多少个点。我在512x512图像上使用20个点运行了此操作,运行时间分别为0.00286秒。如果您的点最终是一个连续的面片(如一个大矩形),您可以修改上面的代码以仅卷积矩形。再次非常感谢。但是,该方法比使用scipy(我认为是fortran/c++语言)卷积整个图像要慢。我添加了一个带有一些分析功能的答案。由于没有其他实现,而您的实现帮助我制作了cython版本,我刚刚给了您答案:P谢谢您的帮助!还要注意np.pad
功能这需要对整个图像进行卷积,这正是我试图避免的。我的掩码
是一些稀疏像素我要在其中评估内核的图像。使用我正在查找的python代码示例编辑了答案。对于您的配置文件表,您应该添加内核形状(因为不同的内核大小显然会改变您的结果)。此外,还有一个打字建议,escribiste percentajes en espanol:(在percentajes中表示含义…)
import numpy as np
cimport cython
@cython.boundscheck(False)
def cy_convolve(unsigned char[:, ::1] im, double[:, ::1] kernel, Py_ssize_t[:, ::1] points):
cdef Py_ssize_t i, j, y, x, n, ks = kernel.shape[0]
cdef Py_ssize_t npoints = points.shape[0]
cdef double[::1] responses = np.zeros(npoints, dtype='f8')
for n in range(npoints):
y = points[n, 0]
x = points[n, 1]
for i in range(ks):
for j in range(ks):
responses[n] += im[y+i, x+j] * kernel[i, j]
return np.asarray(responses)
['303x384'] 1% 2% 5% 10% 20% 50%
1 4.97 9.58 24.32 48.28 100.39 245.77
2 7.60 15.09 37.42 75.17 150.09 375.60
3 3.05 2.99 3.04 2.88 2.96 2.98
4 0.17 0.22 0.38 0.60 1.10 2.49
['512x512'] 1% 2% 5% 10% 20% 50%
1 10.68 21.87 55.47 109.16 223.58 543.73
2 17.90 34.59 86.02 171.20 345.46 858.24
3 6.52 6.53 6.74 6.63 6.43 6.60
4 0.31 0.43 0.78 1.34 2.73 6.82
['512x512'] 1% 2% 5% 10% 20% 50%
1 13.21 21.45 54.98 110.80 217.11 554.96
2 19.55 34.78 87.09 172.33 344.58 893.02
3 6.87 6.82 7.00 6.60 6.64 7.71
4 0.35 0.47 0.87 1.57 2.47 6.07