Can this be done faster with numpy? - python

There's a color image, a numpy array of shape (h,w,3) with N=h*w pixels; there's an array labels of shape (h,w), each label an integer between 1 and M. N is 10^6-10^7, M is 10^3-10^4.
I need to produce
a result image (h,w,3) where the color of each pixel labelled l is the mean color of all pixels labelled l. I.e.:
def recolor1(image, labels):
result = np.empty(shape=(h,w,3))
for label in np.unique(labels):
mask = labels==label
mean = np.mean(image[mask], axis=0)
result[mask] = mean
return result
The code is straightforward, but runs in O(M.N) (the computation of mask is O(N) and the loop runs M times).
An O(N) recolor2 is possible. Basically you go over the labels and image pixels twice. First to compute an auxiliary array, indexed by label, where you keep the sums of each primary and the number of pixels for that label. Then you compute the averages for each label. Then you go over labels and pixels again, computing result. The O(M) time to find the averages is noise.
With recolor2 written in Python, recolor1 and recolor2 break even for N=1000000 and M=1000 at ~4s. As expected, recolor1's time grows linearly to ~20s for M=5000, while recolor2's remains essentially the same.
4s for a relatively small image is not great and it will get much worse for larger images. I'm no expert in numpy and associated libraries. Is there an O(N) solution there?

Let's try np.bincount and loop over the channels:
result = np.stack([np.bincount(labels.flat, weights=img[...,i].flat)[labels-1]
for i in range(3)],
axis=-1)
which takes about 35ms on my system with h,w,M = 1000,1000,1000.
Note This compute the sum, but mean should be easy enough.

Related

Create a fast filter with a custom function on a numpy array

I would like to implement a filter on numpy array which compute locally (given a footprint) the average distance to the central pixel.
This function is similar to the local standard deviation, but takes the center pixel as the reference instead of the average.
One part of the problem is that my arrays are multimodal 2d images (rgb for example).
I have a full numpy implementation, that uses indexing tricks to perform the task, but it is limited to a 3x3 neighborhood for technical reasons.
I was thinking implementing this in cython, following this for example, but it seems not to work for vector images (with more than one value per pixel).
A numpy implementation (kind of pseudo code!) would look something like this:
img: np.ndarray # source image of shape (n, m, 3) for example
mask: np.ndarray # mask representing valid data (n, m)
footprint = disk(5)
n_px_nh = footprint.sum()
out = np.zeros(img.shape)
for i, neighborhood, masked_px in local_2d_filter(img, footprint, mask):
# going through
center_px = neighborhood[n_px_nh/2] # works if n_px_nh is odd of course
center_diff = 0
for px in neighborhood[masked_px]:
center_diff += ((px - center_px) ** 2).sum() ** 0.5 # distance to the center pixel
center_diff /= n_px_nh
out[i] = center_diff
local_2d_filter would be a function, like scipy.ndimage.generic_filter, which goes through an image, returning pixels in the footprint, around the center pixel .
Has anyone an idea on how to implement such filter ?
Thanks

Vectorized sliding / rolling numpy nanmean

I currently use this code to do a moving window average:
n=500
x_copy=np.hstack((np.full(n,np.nan),copy.deepcopy(x),np.full(n,np.nan)))
x_values=[]
for i in range(n,len(x)+n):
x_values.append(np.nanmean(x[i-n:i+n+1]))
plt.plot(x_values)
with x the array I'm working on and n is half the length of the window. However, I need to do this quickly, as I have to do roughly 4400*10 of this operation, with arrays around 60000 elements in length. After searching for a while, I found that np.convolve should work, so I have this code instead:
plt.plot(np.convolve(x, np.ones(((2*n),))/(2*n), mode='valid'),zorder=2)
While this is really fast, it's not doing exactly what I need it to do, as it seems to stop 500*2 units before the end of the array. For reference, here is the image of the plots of both of them: the blue is my own code, the orange is the convolution. I want to use convolve to speed up my moving window average, though I don't know how.
Reference
Set those NaNs to 0s and then use np.convolve on those masked versions -
# Window-size
W = 2*n+1
# Non-nans mask
m = ~np.isnan(x)
# "Masked" input array
x0 = np.where(m,x,0)
# Setup conv kernel and perform conv on x0 and mask m for the counts to divide
K = np.ones(W)
out = (np.convolve(x0,K)/np.convolve(m,K))[W-1:]

Is there an efficient way of applying a radial average in keras?

I would like to apply a radial average at the end of a keras pipeline.
At the second to last step, I have an image of size n x n. I then want to map this n x n image to a 1 x n/2 vector, where vector[x] = mean(image(radialPosition = x)). I.e. I want to average all points of distance X from the center of the image, and set this as output[x]. We can assume that n is odd, so the center point is a single point.
I have considered looping over all radii, and selecting the desired indices, as well as a dot product between the image and multiple "averaging" matrices, but neither of these seem computationally efficient.
Is there a better way of doing this?

Numpy Array python dimension uniform

I have 2 dimensional array with 15 elements in one dimension and variable length in second dimension
for example
>>print abc.size()
15
>>print abc[0].size()
5873
>>print abc[1].size()
9825
How can i make array dimensions uniform either using numpy or skikit sparse array. the data is hog features of an image.
Assuming you want to align all the arrays to the left, and pad to the right with zeros, then you could first find the maximum length with
max_len = max([abc[i].size() for for i in range(abc.size())])
and then pad using zeros:
import numpy as np
for i in range(abc.size()):
abc[i] = np.append(abc[i], np.zeros(max_len - abc[i].size())
We have here two possible cases:
abc is a list of images, and for each image abc[i] is the set of hog features of the image i.
abc is one image and each abc[i] is the i-th hog feature of the image
For the first case, the image sizes or the hog parameters (size for the neighbour) differ from one image to another, so you need to adjust the parameters in order to calculate the hog features properly for all the images (if you want fixed sized descriptors).
For the second case, your hog computation is not correct (it shouldn't happen that the sizes of the hog descriptors are different for the same image).
So, in any of the cases, there is no way of resizing your arrays. You need to fix your hog computations.
Edit: related to your problem, you have a dataset of different size images. The are two possible common approaches for image classification with hog descriptors. But first, a quick summary of HOG:
HOG splits the image in M x N windows of size m x n each and calculates a histogram oriented gradients with fixed W number of bins (number of orientations) in that window. Hence, you will end up with M x N x W features. Features are usually flattened in a 2D vector of size K x W with K = M x N.
Now, for classification there are 2 common approaches:
Combine all the features of an image in one, this is, perform an average (or weighted average or norm) over the K features to end up with a vector of size W for each image (the number of orientations).
To preserve (more or less) the spatial relationship of the features, another more common approach is to concatenate all the features in order to end up with a flattened 1D vector of size Z, with Z = K x W/
From your data, I think you are trying to perform the 2nd step. The problem you are facing is that the images have different size, and therefore, for a fixed window size m x n the number of features differ from one image to another.
The way you could fix that, is by fixing the number of features M x N you want, and for a given image, calculate m = height / M and n = width / N and calculate the HOG descriptors with that custom m x n window size (which is different for every image). This way, you will end up with an K = M x N vector with the same K (but different window size) for every image.
With a fixed K and therefore fixed Z you would be able to perform classification.
I don't know which library are you using for computing the HOG, but m x n window size parameter should be easy to manually set up for every image.
Hope it helps!

Sliding Gabor Filter in python

Taken from the gabor filter example from skimage calculating a gabor filter for an image is easy:
import numpy as np
from scipy import ndimage as nd
from skimage import data
from skimage.util import img_as_float
from skimage.filter import gabor_kernel
brick = img_as_float(data.load('brick.png'))
kernel = np.real(gabor_kernel(0.15, theta = 0.5 * np.pi,sigma_x=5, sigma_y=5))
filtered = nd.convolve(brick, kernel, mode='reflect')
mean = filtered.mean()
variance = filtered.var()
brick is simply a numpy array. Suppose I have a 5000*5000 numpy array. What I want to achieve is to generate two new 5000*5000 numpy arrays where the pixels are the mean and var values of the gabor filter of the 15*15 window centered on them.
Could anyone help me achieve this?
EDIT
¿Why did I get downvoted? Anyway, to clarify I show an example on how to calculate a gabor filter on a single image. I would like to simply calculate a gabor filter on small square subsets of a very large image (hence the sliding window).
There no standard methods to do this (that I know of), but you can do it yourself directly.
Each pixel in the convolution is the sum of the values of the shift gabor filter times the image pixels. That is, each pixel in the convolution is basically the mean to within a constant normalization factor, so filtered is basically your mean.
The variance is a bit more difficult since that is the sum of the squares, and of course, you need to calculate the sqaures before you calculate the sums. But, you can do this easy enough by pre-squaring both the image and the kernel, that is:
N = kernel.shape[0]*kernel.shape[1]
mean = nd.convolve(brick, kernel, mode='reflect')/N
var = nd.convolve(brick*brick, kernel*kernel, mode='reflect')/N - mean*mean
If you just want to calculate the sliding average of an image (convolution with a square kernel with all 1's), the fast method is:
# fsize is the filter size in pixels
# integrate in the X direction
r_sum = numpy.sum(img[:, :fsize], axis=1)
r_diff = img[:, fsize:] - img[:, :-fsize]
r_int = numpy.cumsum(numpy.hstack((r_sum.reshape(-1,1), r_diff)), axis=1)
# integrate in the Y direction
c_sum = numpy.sum(r_img[:fsize, :], axis=0)
c_diff = r_img[fsize:, :] - r_img[:-fsize, :]
c_int = numpy.cumsum(numpy.vstack((c_sum, c_diff)), axis=0)
# now we have an array of sums, average can be obtained by division
avg_img = c_int / (f_size * f_size)
This method returns an image which is size-1 pixels smaller in both directions, so you'll have to take care of border effects yourself. The edge most pixels are bad anyway, but it is up to you to choose the correct border fill, if you need one. The algorithm is the fastest way to obtain the mean (fewest calculations), especially much faster than numpy.convolve.
Similar trickery can be used in calculating the variance, if both the image and its square are averaged as above. Then
npts = fsize * fsize
variance = (rolling_sum(img**2) - rolling_sum(img)/npts) / npts
where rolling_sum is a sliding sum (i.e. the algorithm above without the last division). So, only two rolling sums (image and its square) are required to calculate the rolling variance.
(Warning: the code above is untested, it is there just to illustrate the idea.)

Categories

Resources