Calculate pixel distance from centre of image

import numpy as np
import pandas as pd

Problem

I have an image, n pixel wide, m pixel tall. Both of these numbers are even. Pixels are squares. Pixel values are in a numpy array.

I need to calculate the distance from each pixel’s centre to the centre of the image. Ie the pixels just next to the centre should have associated values sqrt(2)/2. If the image is like a chess-board, the pixel corresponding to g6 square should have associated distance value should be (2.5^2+1.5^2)^0.5=2.91


My solution

I have achieved the task by the following code:

image=np.zeros([2,4]) # let's say this is my image
df = pd.DataFrame(image)

distances = 
pd.DataFrame(
df.apply(
    lambda row: (row.index+0.5-len(df.index)/2)**2,axis=0).to_numpy()+
df.T.apply(
    lambda row: (row.index+0.5-len(df.columns)/2)**2,axis=0).T.to_numpy())
    .apply(np.sqrt).to_numpy()

distances will be:

array([[1.58113883, 0.70710678, 0.70710678, 1.58113883],
       [1.58113883, 0.70710678, 0.70710678, 1.58113883]])

As expected.


Question

Is there a better way? I would appreciate a shorter, more-numpy oriented, or more transparent method.

Answer

I do not know any specific algorithms to do this except this straightforward implementation in Numpy, that is, using the indices (creates array of indices from shape of array) and linalg.norm (calculates norms) functions. Note we also use broadcasting by indexing new dimensions in center[:,None,None] (necessary because of indices intrinsic output shape).

import numpy as np
import pandas as pd

# Numpy function
def np_function(image):
    center = np.array([(image.shape[0])/2, (image.shape[1])/2])
    distances = np.linalg.norm(np.indices(image.shape) - center[:,None,None] + 0.5, axis = 0)

    return distances

# Pandas function
def pd_function(image):
    df = pd.DataFrame(image)
    
    distances = 
    pd.DataFrame(
    df.apply(
        lambda row: (row.index+0.5-len(df.index)/2)**2,axis=0).to_numpy()+
    df.T.apply(
        lambda row: (row.index+0.5-len(df.columns)/2)**2,axis=0).T.to_numpy())
        .apply(np.sqrt).to_numpy()
        
    return distances

For a 4000×6000 image, Numpy’s method is more than one order of magnitude faster than the original function in my computer. You could also just compute the distances from the center to one octant and then copy the results conveniently to the remaining octants (exploiting simmetry), but this will probably be only worth for big images imho.