Reputation: 7852
import numpy as np
import pandas as pd
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
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.
Is there a better way? I would appreciate a shorter, more-numpy oriented, or more transparent method.
Upvotes: 2
Views: 3850
Reputation: 803
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 4000x6000 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.
Upvotes: 2
Reputation: 81
A more transparent method would be first to define the center of your image, i.e something like
Read your array as an image in openCV :
img = cv2.imread("inputImage")
height, width = img.shape
x_center=(width/2)
y_center=(height/2)
Then for each pixels in your numpy/image array you can compute the distance between each pixel of your numpy array and the above center by computing the euclidean distance:
D = dist.euclidean((xA, yA), (x_center, y_center))
PS : You can simply use img.shape from numpy but openCV gives you many method related to distance computing.
Upvotes: 3