Alex I
Alex I

Reputation: 20307

Fill image with nearest value within region

I'd like fill in an image outside a circular area with the nearest value within the circle. The effect is something like skimage's mode='edge' but applying to a circular area of an image instead of a rectangular area.

Simple code which does the right thing - extremely slowly:

def circle_pad(img, xc, yc, r):
    img_out = img.copy()

    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            d = math.sqrt( (i-yc)**2 + (j-xc)**2 )
            if d > r:
                i1, j1 = int( yc + (i-yc)*(r/d) ), int( xc + (j-xc)*(r/d) )
                img_out[i,j] = img[i1,j1]

    return img_out

How to speed this up with numpy? (probably avoid looping over each pixel in python code; typical images are tens of millions pixels)

I thought of using something along the lines of meshgrid as a starting point to calculate the coordinates of the value to fill in at each point, but the way to do it isn't clear.

Upvotes: 0

Views: 713

Answers (1)

Alex I
Alex I

Reputation: 20307

Solved using mgrid - not pretty but blazing fast. Just in case it's useful as an example for other folks with similar image processing problesm:

def circle_pad(img, xc, yc, r):
    mg = np.mgrid[:img.shape[0],0:img.shape[1]]
    yi, xi = mg[0,:,:], mg[1,:,:]

    mask = ((yi-yc)**2 + (xi-xc)**2) < r**2

    d = np.sqrt( (yi-yc)**2 + (xi-xc)**2 )
    d = np.clip(d, r, None)
    ye = yc + (yi-yc)*(r/d)
    xe = xc + (xi-xc)*(r/d)

    ye = np.clip(ye.astype(int), 0, img.shape[0])
    xe = np.clip(xe.astype(int), 0, img.shape[1])

    img_out = img * mask + img[ye,xe] * (~mask)
    return img_out

The key parts are:

  • create a meshgrid-like index arrays xi, yi with np.mgrid - each has the same size as the image
  • calculate the arrays of coordinates xe, ye of the nearest edge pixel by doing math of xi, yi
  • replace values by subscripting the image, like this: img[ye,xe]

Upvotes: 2

Related Questions