How do I use scipy.interpolate.griddata similar to griddata in MATLAB? - python

I am performing some 3D linear interpolation to a set of 3D images (96 * 96 * 60) to apply affine transform. I used MATLAB griddata function:
% T = [4*4] affine transform matrix
[X_org,Y_org,Z_org]=meshgrid(1:imgWidth,1:imgHeight,1:sliceNum);
x=X_org(:);
y=Y_org(:);
z=Z_org(:);
% Fill in v with input image data
for i = 1:length(x)
v(i)=img_in(y(i),x(i),z(i));
end
v = v';
tempsource = [X_org(:) Y_org(:) Z_org(:) ones(length(Z_org(:)),1)]';
sourceCoor =inv(T) * tempsource; % TargetCoor=T*sourceCoor, padding 1s to the matrix
%interpolation method = 'linear';
xq=sourceCoor(1,:)';
yq=sourceCoor(2,:)';
zq=sourceCoor(3,:)';
vq = griddata(x,y,z,v,xq,yq,zq);
img_out = reshape(vq,imgWidth,imgHeight,sliceNum); % the correct output
However, if I tried in Python, scipy.interpolate.griddata behaves differently. I first create same inputs, x,y,z,v,xq,yq,zq to the function:
# T = [4*4] affine transform matrix
Y_org, X_org, Z_org = np.mgrid[1:imgWidth+1, 1:imgHeight+1, 1:sliceNum+1]
x = X_org.flatten(order='F')
y = Y_org.flatten(order='F')
z = Z_org.flatten(order='F')
v = np.zeros_like(x, dtype=np.float32)
for i in range(len(x)):
v[i] = img_in[x[i]-1, y[i]-1, z[i]-1]
v = v.T
tempSoure = np.vstack((x, y, z, np.ones(z.shape[0])))
sourceCoor = np.linalg.inv(T) # tempSoure
xq = sourceCoor[0, :].T
yq = sourceCoor[1, :].T
zq = sourceCoor[2, :].T
# every variable before this line is same as in MATLAB
# vq = griddata((x, y, z), v, (xq, yq, zq))
# vq = griddata((xq, yq, zq), v, (x, y, z))
img_out = np.reshape(vq, (imgHeight, imgWidth, sliceNum))
Two approaches in last three lines of the Python code snippet do not work. The upper one takes forever to execute, while the lower one returns a different and weird result. I see that Python is 0-indexed and MATLAB is 1-indexed so I made some modification in Python version (v[i] = img_in[x[i]-1, y[i]-1, z[i]-1]{The order of indexes I passed in also changed to maintain the original v}, etc.), but I am worried the griddata function in Python will still try to fit with 0-indexed style, resulting to no solution for my 1-indexed style input. Is that the cause of my outcomes? And how can I manage to get the correct output image, assuming 0-indexed?

Related

Questions about details of the filter back projection

Lately I've been studying the filter back projection, and I download the code from github.com. I was confused the process of the filter back projection. here is part of his code:
def backproject(sinogram, theta):
"""Backprojection function.
inputs: sinogram - [n x m] numpy array where n is the number of projections and m the number of angles
theta - vector of length m denoting the angles represented in the sinogram
output: backprojArray - [n x n] backprojected 2-D numpy array"""
imageLen = sinogram.shape[0] #sinogram : [n x m] , so imageLen = n(height)
reconMatrix = np.zeros((imageLen, imageLen))
x = np.arange(imageLen)-imageLen/2
y = x.copy()
X, Y = np.meshgrid(x, y)
plt.ion()
fig2, ax = plt.subplots()
im = plt.imshow(reconMatrix, cmap='gray')
theta = theta*np.pi/180
numAngles = len(theta)
for n in range(numAngles):
Xrot = X*np.sin(theta[n])-Y*np.cos(theta[n])
XrotCor = np.round(Xrot+imageLen/2)
XrotCor = XrotCor.astype('int')
projMatrix = np.zeros((imageLen, imageLen))
m0, m1 = np.where((XrotCor >= 0) & (XrotCor <= (imageLen-1)))
s = sinogram[:,n]
projMatrix[m0, m1] = s[XrotCor[m0, m1]]
reconMatrix += projMatrix
im.set_data(Image.fromarray((reconMatrix-np.min(reconMatrix))/np.ptp(reconMatrix)*255))
ax.set_title('Theta = %.2f degrees' % (theta[n]*180/np.pi))
fig2.canvas.draw()
fig2.canvas.flush_events()
plt.close()
plt.ioff()
backprojArray = np.flipud(reconMatrix)
return backprojArray
For the loop 'for', I was confused for two weeks.
Firstly, I really don't know the following code.
Xrot = X*np.sin(theta[n])-Y*np.cos(theta[n])
XrotCor = np.round(Xrot+imageLen/2) .
I don't know how it works through geometric ways. I have drown the matrix and so on, but I still don't know the priciples.
Lastly, for the code, im.set_data(Image.fromarray((reconMatrix-np.min(reconMatrix))/np.ptp(reconMatrix)*255)) , what does it mean, cause I only know the direct back projection. And I really don't know why there's 255
Xrot = X*np.sin(theta[n])-Y*np.cos(theta[n])
This is the simple back projection algorithmm. I am also learning it so I will try to make it as simple and concise as possible.
There are some steps for FBP.
Input Sinogram image(Radon Transform)
_ Create Filter (Ram filter works best but you can try other High pass filters as well)
Forward Fourier Transform(dft function )
Apply Filter
Inverse Fourier Transform
Backprojection (Basically reversing the sinogram technique)
Backprojection is simply back projecting the values and add up them to get the original image for each projection.
im.set_data(Image.fromarray((reconMatrix-.min(reconMatrix))/np.ptp(reconMatrix)*255))
I believe this code is normalizing the image nothing else.

Why the PCA image doesnt resemble the original image at all?

I am trying to implement PCA without any library for image dimension reduction. I tried the code in the O'Reilly Computer Vision book and implement it on a sample lenna picture:
from PIL import Image
from numpy import *
def pca(X):
num_data, dim = X.shape
mean_X = X.mean(axis=0)
X = X - mean_X
if dim > num_data:
# PCA compact trick
M = np.dot(X, X.T) # covariance matrix
e, U = np.linalg.eigh(M) # calculate eigenvalues an deigenvectors
tmp = np.dot(X.T, U).T
V = tmp[::-1] # reverse since the last eigenvectors are the ones we want
S = np.sqrt(e)[::-1] #reverse since the last eigenvalues are in increasing order
for i in range(V.shape[1]):
V[:,i] /= S
else:
# normal PCA, SVD method
U,S,V = np.linalg.svd(X)
V = V[:num_data] # only makes sense to return the first num_data
return V, S, mean_X
img=color.rgb2gray(io.imread('D:\lenna.png'))
x,y,z=pca(img)
plt.imshow(x)
but the image plot of the pca doesnt look like the original image like at all.
As far as i know PCA kinda reduce the image dimension but it will still somehow resemble the original image but in lower detail. Whats wrong with the code?
Well, nothing is wrong per se in your code, but you're not displaying the right thing if I do understand what you actually want to do!
What I would write for your problem is the following:
def pca(X, number_of_pcs):
num_data, dim = X.shape
mean_X = X.mean(axis=0)
X = X - mean_X
if dim > num_data:
# PCA compact trick
M = np.dot(X, X.T) # covariance matrix
e, U = np.linalg.eigh(M) # calculate eigenvalues an deigenvectors
tmp = np.dot(X.T, U).T
V = tmp[::-1] # reverse since the last eigenvectors are the ones we want
S = np.sqrt(e)[::-1] #reverse since the last eigenvalues are in increasing order
for i in range(V.shape[1]):
V[:,i] /= S
return V, S, mean_X
else:
# normal PCA, SVD method
U, S, V = np.linalg.svd(X, full_matrices=False)
# reconstruct the image using U, S and V
# otherwise you're just outputting the eigenvectors of X*X^T
V = V.T
S = np.diag(S)
X_hat = np.dot(U[:, :number_of_pcs], np.dot(S[:number_of_pcs, :number_of_pcs], V[:,:number_of_pcs].T))
return X_hat, S, mean_X
The change here lies in the fact that we want to reconstruct the image using a given number of eigenvectors (determined by number_of_pcs).
The thing to remember is that in np.linalg.svd, the columns of U are the eigenvectors of X.X^T.
When doing that, we obtain the following results (displayed here using 1 and 10 principal components):
X_hat, S, mean_X = pca(img, 1)
plt.imshow(X_hat)
X_hat, S, mean_X = pca(img, 10)
plt.imshow(X_hat)
PS: note that the picture aren't displayed in grayscale because of matplotlib.pyplot, but this is a very minor issue here.

How to efficiently compute the heat map of two Gaussian distribution in Python?

I am trying to produce a heat map where the pixel values are governed by two independent 2D Gaussian distributions. Let them be Kernel1 (muX1, muY1, sigmaX1, sigmaY1) and Kernel2 (muX2, muY2, sigmaX2, sigmaY2) respectively. To be more specific, the length of each kernel is three times its standard deviation. The first Kernel has sigmaX1 = sigmaY1 and the second Kernel has sigmaX2 < sigmaY2. Covariance matrix of both kernels are diagonal (X and Y are independent). Kernel1 is usually completely inside Kernel2.
I tried the following two approaches and the results are both unsatisfactory. Can someone give me some advice?
Approach1:
Iterate over all pixel value pairs (i, j) on the map and compute the value of I(i,j) given by I(i,j) = P(i, j | Kernel1, Kernel2) = 1 - (1 - P(i, j | Kernel1)) * (1 - P(i, j | Kernel2)). Then I got the following result, which is good in terms of smoothness. But it takes 10 seconds to run on my computer, which is too slow.
Codes:
def genDensityBox(self, height, width, muY1, muX1, muY2, muX2, sigmaK1, sigmaY2, sigmaX2):
densityBox = np.zeros((height, width))
for y in range(height):
for x in range(width):
densityBox[y, x] += 1. - (1. - multivariateNormal(y, x, muY1, muX1, sigmaK1, sigmaK1)) * (1. - multivariateNormal(y, x, muY2, muX2, sigmaY2, sigmaX2))
return densityBox
def multivariateNormal(y, x, muY, muX, sigmaY, sigmaX):
return norm.pdf(y, loc=muY, scale=sigmaY) * norm.pdf(x, loc=muX, scale=sigmaX)
Approach2:
Generate two images corresponding to two kernels separately and then blend them together using certain alpha value. Each image is generated by taking the outer product of two one-dimensional Gaussian filter. Then I got the following result, which is very crude. But the advantage of this approach is that it is very fast due to the use of outer product between two vectors.
Since the first one is slow and the second on is crude, I am trying to find a new approach that achieves good smoothness and low time-complexity at the same time. Can someone give me some help?
Thanks!
For the second approach, the 2D Gaussian map can be easily generated as mentioned here:
def gkern(self, sigmaY, sigmaX, yKernelLen, xKernelLen, nsigma=3):
"""Returns a 2D Gaussian kernel array."""
yInterval = (2*nsigma+1.)/(yKernelLen)
yRow = np.linspace(-nsigma-yInterval/2.,nsigma+yInterval/2.,yKernelLen + 1)
kernelY = np.diff(st.norm.cdf(yRow, 0, sigmaY))
xInterval = (2*nsigma+1.)/(xKernelLen)
xRow = np.linspace(-nsigma-xInterval/2.,nsigma+xInterval/2.,xKernelLen + 1)
kernelX = np.diff(st.norm.cdf(xRow, 0, sigmaX))
kernelRaw = np.sqrt(np.outer(kernelY, kernelX))
kernel = kernelRaw / (kernelRaw.sum())
return kernel
Your approach is fine other than that you shouldn't loop over norm.pdf but just push all values at which you want the kernel(s) evaluated, and then reshape the output to the desired shape of the image.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
# create 2 kernels
m1 = (-1,-1)
s1 = np.eye(2)
k1 = multivariate_normal(mean=m1, cov=s1)
m2 = (1,1)
s2 = np.eye(2)
k2 = multivariate_normal(mean=m2, cov=s2)
# create a grid of (x,y) coordinates at which to evaluate the kernels
xlim = (-3, 3)
ylim = (-3, 3)
xres = 100
yres = 100
x = np.linspace(xlim[0], xlim[1], xres)
y = np.linspace(ylim[0], ylim[1], yres)
xx, yy = np.meshgrid(x,y)
# evaluate kernels at grid points
xxyy = np.c_[xx.ravel(), yy.ravel()]
zz = k1.pdf(xxyy) + k2.pdf(xxyy)
# reshape and plot image
img = zz.reshape((xres,yres))
plt.imshow(img); plt.show()
This approach shouldn't take too long:
In [26]: %timeit zz = k1.pdf(xxyy) + k2.pdf(xxyy)
1000 loops, best of 3: 1.16 ms per loop
Based on Paul's answer, I made a function to make a heatmap of gaussians taking as input the centers of the gaussians (it could be helpful to others) :
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
def points_to_gaussian_heatmap(centers, height, width, scale):
gaussians = []
for y,x in centers:
s = np.eye(2)*scale
g = multivariate_normal(mean=(x,y), cov=s)
gaussians.append(g)
# create a grid of (x,y) coordinates at which to evaluate the kernels
x = np.arange(0, width)
y = np.arange(0, height)
xx, yy = np.meshgrid(x,y)
xxyy = np.stack([xx.ravel(), yy.ravel()]).T
# evaluate kernels at grid points
zz = sum(g.pdf(xxyy) for g in gaussians)
img = zz.reshape((height,width))
return img
W = 800 # width of heatmap
H = 400 # height of heatmap
SCALE = 64 # increase scale to make larger gaussians
CENTERS = [(100,100),
(100,300),
(300,100)] # center points of the gaussians
img = points_to_gaussian_heatmap(CENTERS, H, W, SCALE)
plt.imshow(img); plt.show()

Python: Scipy's curve_fit for NxM arrays?

Usually I use Scipy.optimize.curve_fit to fit custom functions to data.
Data in this case was always a 1 dimensional array.
Is there a similiar function for a two dimensional array?
So, for example, I have a 10x10 numpy array. Then I have a function that does some stuff and creates a 10x10 numpy array, and I want to fit the function, so that the resulting 10x10 array has the best fit to the input array.
Maybe an example is better :)
data = pyfits.getdata('data.fits') #fits is an image format, this gives me a NxM numpy array
mod1 = pyfits.getdata('mod1.fits')
mod2 = pyfits.getdata('mod2.fits')
mod3 = pyfits.getdata('mod3.fits')
mod1_1D = numpy.ravel(mod1)
mod2_1D = numpy.ravel(mod2)
mod3_1D = numpy.ravel(mod3)
def dostuff(a,b): #originaly this is a function for 2D arrays
newdata = (mod1_1D*12)+(mod2_1D)**a - mod3_1D/b
return newdata
Now a and b should be fitted, so that newdata is as close as possible to data.
What I got so far:
data1D = numpy.ravel(data)
data_X = numpy.arange(data1D.size)
fit = curve_fit(dostuff,data_X,data1D)
But print fit only gives me
(array([ 1.]), inf)
I do have some nans in the arrays, maybe thats a problem?
The goal is to express the 2D function as a 1D function: g(x, y, ...) --> f(xy, ...)
Converting the coordinate pair (x, y) into a single number xy may seem tricky at first. But it's actually quite simple. Just enumerate all data points and you have a single number that uniquely defines each coordinate pair. The fitted function simply has to reconstruct the original coordinates, do it's calculations and return the result.
Example that fits a 2D linear gradient in a 20x10 image:
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
n, m = 10, 20
# noisy example data
x = np.arange(m).reshape(1, m)
y = np.arange(n).reshape(n, 1)
z = x + y * 2 + np.random.randn(n, m) * 3
def f(xy, a, b):
i = xy // m # reconstruct y coordinates
j = xy % m # reconstruct x coordinates
out = i * a + j * b
return out
xy = np.arange(z.size) # 0 is the top left pixel and 199 is the top right pixel
res = sp.optimize.curve_fit(f, xy, np.ravel(z))
z_est = f(xy, *res[0])
z_est2d = z_est.reshape(n, m)
plt.subplot(2, 1, 1)
plt.plot(np.ravel(z), label='original')
plt.plot(z_est, label='fitted')
plt.legend()
plt.subplot(2, 2, 3)
plt.imshow(z)
plt.xlabel('original')
plt.subplot(2, 2, 4)
plt.imshow(z_est2d)
plt.xlabel('fitted')
I would recommend using symfit for this, I wrote that to take care of all of the magic for you automatically.
In symfit you would just write the equation pretty much as you would on paper, and then you can run the fit.
I would do something like this:
from symfit import parameters, variables, Fit
# Assuming all this data is in the form of NxM arrays
data = pyfits.getdata('data.fits')
mod1 = pyfits.getdata('mod1.fits')
mod2 = pyfits.getdata('mod2.fits')
mod3 = pyfits.getdata('mod3.fits')
a, b = parameters('a, b')
x, y, z, u = variables('x, y, z, u')
model = {u: (x * 12) + y**a - z / b}
fit = Fit(model, x=mod1, y=mod2, z=mod3, u=data)
fit_result = fit.execute()
print(fit_result)
Unfortunatelly I have not yet included examples of the kind you need in the docs yet, but if you just look at the docs I think you can figure it out in case this doesn't work out of the box.

Specify absolute colour for 3D points in MayaVi

I am using the MayaVi Python library to plot 3d points, using the points3d class. The documentation specifies that the colour of each point is specified through a fourth argument, s:
In addition, you can pass a fourth array s of the same shape as x, y,
and z giving an associated scalar value for each point, or a function
f(x, y, z) returning the scalar value. This scalar value can be used
to modulate the color and the size of the points.
This specifies a scalar value for each point, which maps the point to a colourmap, such as copper, jet or hsv. E.g. from their documentation:
import numpy
from mayavi.mlab import *
def test_points3d():
t = numpy.linspace(0, 4*numpy.pi, 20)
cos = numpy.cos
sin = numpy.sin
x = sin(2*t)
y = cos(t)
z = cos(2*t)
s = 2+sin(t)
return points3d(x, y, z, s, colormap="copper", scale_factor=.25)
Gives:
Instead, I would like to specify the actual value for each point as an (r, g, b) tuple. Is this possible in MayaVi? I have tried replacing the s with an array of tuples, but an error is thrown.
After struggling with this for most of today, I found a relatively simple way to do exactly what the question asks -- specify an RGB tuple for each point. The trick is just to define a color map with exactly the same number of entries as there are points to plot, and then set the argument to be a list of indices:
# Imports
import numpy as np
from mayavi.mlab import quiver3d, draw
# Primitives
N = 200 # Number of points
ones = np.ones(N)
scalars = np.arange(N) # Key point: set an integer for each point
# Define color table (including alpha), which must be uint8 and [0,255]
colors = (np.random.random((N, 4))*255).astype(np.uint8)
colors[:,-1] = 255 # No transparency
# Define coordinates and points
x, y, z = colors[:,0], colors[:,1], colors[:,2] # Assign x, y, z values to match color
pts = quiver3d(x, y, z, ones, ones, ones, scalars=scalars, mode='sphere') # Create points
pts.glyph.color_mode = 'color_by_scalar' # Color by scalar
# Set look-up table and redraw
pts.module_manager.scalar_lut_manager.lut.table = colors
draw()
I've found a better way to set the colors directly.
You can create your own direct LUT pretty easily. Let's say we want 256**3 granularity:
#create direct grid as 256**3 x 4 array
def create_8bit_rgb_lut():
xl = numpy.mgrid[0:256, 0:256, 0:256]
lut = numpy.vstack((xl[0].reshape(1, 256**3),
xl[1].reshape(1, 256**3),
xl[2].reshape(1, 256**3),
255 * numpy.ones((1, 256**3)))).T
return lut.astype('int32')
# indexing function to above grid
def rgb_2_scalar_idx(r, g, b):
return 256**2 *r + 256 * g + b
#N x 3 colors. <This is where you are storing your custom colors in RGB>
colors = numpy.array([_.color for _ in points])
#N scalars
scalars = numpy.zeros((colors.shape[0],))
for (kp_idx, kp_c) in enumerate(colors):
scalars[kp_idx] = rgb_2_scalar_idx(kp_c[0], kp_c[1], kp_c[2])
rgb_lut = create_8bit_rgb_lut()
points_mlab = mayavi.mlab.points3d(x, y, z, scalars, mode='point')
#magic to modify lookup table
points_mlab.module_manager.scalar_lut_manager.lut._vtk_obj.SetTableRange(0, rgb_lut.shape[0])
points_mlab.module_manager.scalar_lut_manager.lut.number_of_colors = rgb_lut.shape[0]
points_mlab.module_manager.scalar_lut_manager.lut.table = rgb_lut
You can use a rgb look up table and map your rgb values to it using whatever logic you want. Here's a simple example:
import numpy, random
from mayavi.mlab import *
def cMap(x,y,z):
#whatever logic you want for colors
return [random.random() for i in x]
def test_points3d():
t = numpy.linspace(0, 4*numpy.pi, 20)
cos = numpy.cos
sin = numpy.sin
x = sin(2*t)
y = cos(t)
z = cos(2*t)
s = cMap(x,y,z)
return points3d(x, y, z, s, colormap="spectral", scale_factor=0.25)
test_points3d()
I have no idea what color scheme you want, but you can evaluate the positions of x,y,z and return whatever scalar corresponds to the rgb value you are seeking.
This can now simply be done with the color argument
from mayavi import mlab
import numpy as np
c = np.random.rand(200, 3)
r = np.random.rand(200) / 10.
mlab.points3d(c[:, 0], c[:, 1], c[:, 2], r, color=(0.2, 0.4, 0.5))
mlab.show()

Categories