Image convolution at specific points
Question: 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?

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.

EDIT: Example python code that does what I'm trying to do (but not faster than
a whole image convolution using 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. I was wondering if there is something
faster already implemented in `scipy`/`opencv`/`skimage`.


Answer: 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.

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.]

    
    
    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. So you can see the smoothed patches. ![enter image
description here](http://i.stack.imgur.com/PFYKx.png) ![enter image
description here](http://i.stack.imgur.com/5cR3J.png)

Here is a lena512 image with 21 convolution points (time:0.006177 sec).
![enter image description here](http://i.stack.imgur.com/usXE0.png)

[**EDIT 2** : An example of using a mask to generate a list of row, col tuples
to feed in to the function.]

    
    
    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
    

![enter image description here](http://i.stack.imgur.com/Zcz2v.png)

