简体   繁体   English

特定点的图像卷积

[英]Image convolution at specific points

Is there a way in scipy (or other similar library) to get the convolution of an image with a given kernel only at some desired points? 在scipy(或其他类似的库)中是否有一种方法只能在某些所需的点获得给定内核的图像卷积?

I'm looking for something like: 我正在寻找类似的东西:

ndimage.convolve(image, kernel, mask=mask)

Where mask contains True (or 1 ) whenever the kernel needs to be applied, False (or 0 ) otherwise. 其中mask需要应用内核时包含True (或1 ),否则为False (或0 )。

EDIT: Example python code that does what I'm trying to do (but not faster than a whole image convolution using scipy): 编辑:示例python代码执行我正在尝试做的事情(但不比使用scipy的整个图像卷积更快):

def kernel_responses(im, kernel, mask=None, flatten=True):
    if mask is None:
        mask = np.ones(im.shape[:2], dtype=np.bool)

    ks = kernel.shape[0]//2

    data = np.pad(im, ks, mode='reflect')
    y, x = np.where(mask)

    responses = np.empty(y.shape[0], float)

    for k, (i, j) in enumerate(zip(y, x)):
        responses[k] = (data[i:i+ks*2+1, j:j+ks*2+1] * kernel).sum()

    if flatten:
        return responses

    result = np.zeros(im.shape[:2], dtype=float)
    result[y, x] = responses
    return result

The above code does the job with a wrap boundary conditions, but the inner loop is in python, and thus, slow. 上面的代码使用wrap边界条件来完成工作,但是内部循环是在python中,因此很慢。 I was wondering if there is something faster already implemented in scipy / opencv / skimage . 我想知道scipy / opencv / skimage是否有更快的实现。

I know that I'm responding to my own answer, I hope the code bellow carries further improvements, or it might be useful for other users. 我知道我正在回答我自己的答案,我希望下面的代码可以进一步改进,或者它可能对其他用户有用。

The code bellow is a cython/python implementation: 下面的代码是一个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)

CYTHON: 用Cython:

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)

Comparision with other methods 与其他方法比较

The following tables shows evaluation of 4 methods: 下表显示了4种方法的评估:

  1. My python method in the question 我在问题中的python方法
  2. The method from @Vighnesh Birodkar 来自@Vighnesh Birodkar的方法
  3. Complete image convolution with scipy 用scipy完成图像卷积
  4. My python/cython implementation in this post 我在这篇文章中的python / cython实现

Each rows, in order, correspond to those methods for 3 different images ( coins , camera and lena from skimage.data respectively) and each of the columns corresponds to a different ammount of points to calculate the kernel responses (is in percentages as meaning "calculate response in x% of the points of the image"). 每个行按顺序对应于3个不同图像(分别来自skimage.data coinscameralena )的那些方法,并且每个列对应于不同的点数以计算内核响应(以百分比表示“以x%的图像点计算响应“)。

For calculating the kernel response in less than 50% of the points, my implementation is faster than convolving the whole image, but is not faster otherwise.. 为了在不到50%的点数内计算内核响应,我的实现比卷积整个图像更快,但不会更快。

EDIT: kernel windows for the tests are 5x5 uniform windows ( np.ones((5,5)) ). 编辑:测试的内核窗口是5x5统一窗口( np.ones((5,5)) )。

['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

NOTE: times are in ms . 注意:时间以ms

I don't know of any function that does exactly what you're asking. 我不知道任何功能完全符合您的要求。 If instead of providing a mask of points to be convolved you provided a list of points ex. 如果不是提供要进行卷积的点掩码,而是提供了一个点列表。 [(7, 7), (100, 100)] then it might be as simple as getting the appropriate image patch (say the same size as your provided kernel), convolve the image patch and kernel, and insert back into the original image. [(7, 7), (100, 100)]然后它可能就像获取适当的图像补丁(比如提供的内核大小相同)一样简单,卷积图像补丁和内核,然后插回原始图像。

Here's a coded example, hopefully it's close enough for you to modify lightly: 这是一个编码示例,希望它足够接近您轻松修改:

[ EDIT : I noticed a couple errors I had in my padding and patch arithmetic. [ 编辑 :我注意到我的填充和补丁算法中有一些错误。 Previously, you could not convolve with a point right on the boarder (say (0, 0)), I doubled the padding, fixed some arithmetic, and now all is well.] 以前,你无法在边界上对一个点进行卷积(比如说(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]

Now to try it out on an image: 现在尝试一下图像:

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()

I applied a 5x5 box smoother and can't really see any change around pixel (7, 7), but I chose the other two points to be the tips of the two left-most penguin's beaks. 我应用了一个5x5的盒子更顺畅,并且不能真正看到像素周围的任何变化(7,7),但我选择了另外两个点作为两个最左侧企鹅的喙的尖端。 So you can see the smoothed patches. 所以你可以看到平滑的补丁。 在此输入图像描述在此输入图像描述

Here is a lena512 image with 21 convolution points (time:0.006177 sec). 这是一个带有21个卷积点的lena512图像(时间:0.006177秒)。 在此输入图像描述

[ EDIT 2 : An example of using a mask to generate a list of row, col tuples to feed in to the function.] [ 编辑2 :使用掩码生成行列表的示例,使用col元组来输入函数。]

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

在此输入图像描述

You can use the following code snippet. 您可以使用以下代码段。 If you mask is sufficiently dense, it might not be that inefficient. 如果你的面具足够密集,那可能效率不高。

def mask_conv(img, kernel, mask):
    out = filters.convolve(img, kernel)
    return np.where(mask, out, img)

Some sample code 一些示例代码

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()

用面具卷积

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM