Python: Why are eigenvectors not the same as first PCA weights? - python

Let's generate an array:
import numpy as np
data = np.arange(30).reshape(10,3)
data=data*data
array([[ 0, 1, 4],
[ 9, 16, 25],
[ 36, 49, 64],
[ 81, 100, 121],
[144, 169, 196],
[225, 256, 289],
[324, 361, 400],
[441, 484, 529],
[576, 625, 676],
[729, 784, 841]])
Then find the eigenvalues of the covariance matrix:
mn = np.mean(data, axis=0)
data -= mn
C = np.cov(data.T)
evals, evecs = la.eig(C)
idx = np.argsort(evals)[::-1]
evecs = evecs[:,idx]
print evecs
array([[-0.53926461, -0.73656433, 0.40824829],
[-0.5765472 , -0.03044111, -0.81649658],
[-0.61382979, 0.67568211, 0.40824829]])
Now let's run the matplotlib.mlab.PCA function on the data:
import matplotlib.mlab as mlab
mpca=mlab.PCA(data)
print mpca.Wt
[[ 0.57731894 0.57740574 0.57732612]
[ 0.72184459 -0.03044628 -0.69138514]
[ 0.38163232 -0.81588947 0.43437443]]
Why are the two matrices different? I thought that in finding the PCA, first one had to find the eigenvectors of the covariance matrix, and that this would be exactly equal to the weights.

You need to normalize your data, not just center it, and the output of np.linalg.eig has to be transposed to match that of mlab.PCA:
>>> n_data = (data - data.mean(axis=0)) / data.std(axis=0)
>>> evals, evecs = np.linalg.eig(np.cov(n_data.T))
>>> evecs = evecs[:, np.argsort(evals)[::-1]].T
>>> mlab.PCA(data).Wt
array([[ 0.57731905, 0.57740556, 0.5773262 ],
[ 0.72182079, -0.03039546, -0.69141222],
[ 0.38167716, -0.8158915 , 0.43433121]])
>>> evecs
array([[-0.57731905, -0.57740556, -0.5773262 ],
[-0.72182079, 0.03039546, 0.69141222],
[ 0.38167716, -0.8158915 , 0.43433121]])

Related

Image-processing convolution kernels are calculated dynamically

Using standard numpy and cv2.filter2D solutions I can apply static convolutions to an image:
import numpy as np
convolution_kernel = np.array([[-2, -1, 0],
[-1, 1, 1],
[0, 1, 2]])
import cv2
image = cv2.imread('1.png') result = cv2.filter2D(image, -1, convolution_kernel)
(example from https://stackoverflow.com/a/58383803/3310334)
Every pixel at [i, j] in the output image has a value calculated by centering a 3x3 "window" onto [i, j] in the input image, and then multiplying each value in the window by the corresponding value in the convolution kernel (Hadamard product) and finally summing the 9 products to get the value for [i, j] in the output image (for each color channel).
(image from: https://github.com/ashushekar/image-convolution-from-scratch#convolution)
In my case, the function to perform to calculate for each output pixel is not as simple as sum of Hadamard product. It is for each pixel calculated from operations performed on known-size windows into two input matrices centered around that pixel.
I have two input matrixes ("images"), like
A = [[179, 97, 77, 118, 144, 105],
[ 68, 56, 184, 210, 141, 230],
[178, 166, 218, 47, 106, 172],
[ 38, 183, 50, 185, 48, 87],
[ 60, 200, 228, 232, 6, 190],
[253, 75, 231, 166, 117, 134]]
B = [[116, 95, 94, 220, 80, 223],
[135, 9, 166, 78, 5, 129],
[102, 167, 120, 81, 141, 29],
[ 83, 117, 81, 129, 255, 48],
[130, 231, 165, 7, 187, 169],
[ 44, 137, 16, 50, 229, 202]]
And in the output matrix, each [i, j] pixel should be calculated as the sum of all of A[u,v] ** 2 - B[u,v] ** 2 values for [u, v] coordinates within 3x3 "windows" onto the two (same-sized) input matrixes.
How can I calculate this output matrix quickly in Python?
Using numpy, it seems to be the 3x3 sums of A * A - B * B, but how to do those sums? Or is there another "2d map" process I could be using?
I've written a loop-based solution to calculate the expected output for these two examples:
W = 3 # size of kernel is WxW
out = np.zeros(A.shape)
difference_of_squares = A * A - B * B
for i, j in np.ndindex(out.shape):
starti = max(i - W//2, 0) # use smaller kernels at input's boundaries, output will have same dimension as input
stopi = min(i - W//2 + W, np.shape(out)[0]) # I'm not worried at this point about what happens at boundaries
startj = max(j - W//2, 0) # standard convolution solutions are often just reducing output size or padding input with zeroes
stopj = min(j - W//2 + W, np.shape(out)[1])
out[i, j] = np.sum(difference_of_squares[starti:stopi, startj:stopj])
print(out)
[[ 8423. 11816. 10372. 41125. 35287. 31747.]
[ 29370. 65887. 38811. 61252. 51033. 51845.]
[ 24756. 60119. 109133. 35101. 70005. 18757.]
[ 8641. 62463. 126935. 14530. 2255. -64752.]
[ 36623. 110426. 163513. 33812. -50035. -146450.]
[ 22268. 100132. 130190. 83010. -10163. -88994.]]
You can use scipy.signal.convolve2d:
from scipy.signal import convolve2d
# Same shape as original (6x6)
>>> convolve2d(A**2-B**2, np.ones((3, 3), dtype=int), mode='same')
array([[ 8423, 11816, 10372, 41125, 35287, 31747],
[ 29370, 65887, 38811, 61252, 51033, 51845],
[ 24756, 60119, 109133, 35101, 70005, 18757],
[ 8641, 62463, 126935, 14530, 2255, -64752],
[ 36623, 110426, 163513, 33812, -50035, -146450],
[ 22268, 100132, 130190, 83010, -10163, -88994]])
# Shape reduce by 1 (5x5)
>>> convolve2d(A**2-B**2, np.ones((3, 3), dtype=int), mode='valid')
array([[ 65887, 38811, 61252, 51033],
[ 60119, 109133, 35101, 70005],
[ 62463, 126935, 14530, 2255],
[110426, 163513, 33812, -50035]])
Note: You have to play around with the "mode" and "limit" parameters until you get what you want.
Update
If the border is not a problem at this point, you can use sliding_window_view:
from numpy.lib.stride_tricks import sliding_window_view
>>> np.sum(sliding_window_view(A**2-B**2, (3, 3)), axis=(2, 3))
array([[ 65887, 38811, 61252, 51033],
[ 60119, 109133, 35101, 70005],
[ 62463, 126935, 14530, 2255],
[110426, 163513, 33812, -50035]])

I'm not able to get the correct homography points

I'm trying to create homography matrix from this field:
(source image with ids:)
To the destination image:
The points for the source points are:
pts_src = [[ 761, 704],
[ 910, 292],
[1109, 544],
[ 619, 479],
[ 656, 373 ],
[1329, 446],
[ 20, 559],
[ 87, 664],
[ 238, 501],
[ 399, 450]]
And the points for destination points (new image):
pts_dst = [[147, 330],
[ 35 , 20],
[147, 225],
[ 75, 203],
[ 35, 155],
[147, 155],
[ 35, 317],
[ 75, 351],
[ 35, 237],
[ 35, 203]]
I tried to create homography matrix with the following code:
import numpy as np
pts_src = np.array(pts_src)
pts_dst = np.array(pts_dst)
h, status = cv2.findHomography(pts_src, pts_dst)
print(h) #homography matrix
And I got the following homography matrix:
[[ 4.00647822e-01 1.41196305e+00 -6.90548584e+02]
[-1.28068526e-01 3.03783700e+00 -6.98945354e+02]
[ 3.12182175e-04 4.06980322e-03 1.00000000e+00]]
I tried to check if the homography matrix is correct, so I used the first coordinate from the source image (761, 704), and check if I get the correct coordinate in destination image, which should be (147, 330). I tried to use the equation new_x, new_y, new_z = h*(x,y,z):
p = [761, 704, 1]
print(np.matmul(h, p))
And I got:
array([ 608.36639573, 1342.23174648, 4.1027121 ])
Which is very far (and for some reason the z is 4.1).
And I also tried the second point (910, 292), and I should get (35 , 20), but I got [86.33414416, 71.5606917 , 2.47246832].
Any idea why I'm not able to get the correct coordinates?
The solution was mentioned in the comments thanks to #fana - dividing the x and y coordinates by z (x/z, y/z).

Numpy Group Reshaping / Indexing

The situation is I'd like to take the following Python / NumPy code:
# Procure some data:
z = np.zeros((32,32))
chunks = []
for i in range(0,32,step):
for j in range(0,32,step):
chunks.append( z[i:i+step,j:j+step] )
chunks = np.array(chunks)
chunks.shape # (256, 2, 2)
And vectorize it / remove the for loops. Is this possible? I don't mind much about ordering of the final array, e.g. 256,2,2 vs 2,2,256, as long as the spatial structure remains the same. That is, blocks of 2x2 from the original array.
Perhaps some magic using :: in addition to regular indexing can do this? Any NumPy masters here?
You may need transpose:
a = np.arange(1024).reshape(32,32)
a.reshape(16,2,16,2).transpose((0,2,1,3)).reshape(-1,2,2)
Output:
array([[[ 0, 1],
[ 32, 33]],
[[ 2, 3],
[ 34, 35]],
[[ 4, 5],
[ 36, 37]],
...,
[[ 986, 987],
[1018, 1019]],
[[ 988, 989],
[1020, 1021]],
[[ 990, 991],
[1022, 1023]]])

Python find convolution kernel if input image and output image is known

I have a problem with convolution kernel in python. It is about simple convolution operator. I have input matrix and output matrix. I want to find a possible convolution kernel with size(5x5). How to solve this problem with python, numpy or tensorflow ?
import scipy.signal as ss
input_img = np.array([[94, 166, 76, 106, 152, 232],
[48, 242, 30, 98, 46, 210],
[52, 60, 86, 60, 216, 248],
[52, 236, 116, 240, 224, 184],
[138, 160, 146, 254, 236, 252],
[94, 100, 224, 246, 152, 74]], dtype=float)
output_img = np.array([[15, 49, 23, 105, 0, 0],
[43,30, 108, 124, 0, 0],
[58, 120, 112, 92, 0, 0],
[73, 127, 118, 126, 0, 0],
[112, 123, 76, 37, 0, 0],
[0, 0, 0, 0, 0, 0]], dtype=float)
# I want to find this kernel
conv = np.zeros((5,5), dtype=int)
# So if I do convolution operator, output_img will resulting a value same as I defined above
output_img = ss.convolve2d(input_img, conv, padding='same')
As far as I understood, you need to reconstruct window weights by given input, output arrays and window size. This is possible, I think, especially, if input array (image) is sufficiently big.
Look at the code below:
import scipy.signal as ss
import numpy as np
source_dataset = np.random.rand(20, 10)
sample_convolution = np.diag([1, 1, 1])
output_dataset = ss.convolve2d(data, sample_convolution, mode='same')
conv_size = c.shape[0]
# Given output_dataset, source_datset, and conv_size we need to reconstruct
# window weights.
def reconstruct(data, output, csize):
half_size = int(csize / 2)
min_row_ind = half_size
max_row_ind = int(data.shape[0]) - half_size
min_col_ind = half_size
max_col_ind = int(data.shape[1]) - half_size
A = list()
b = list()
for i in np.arange(min_row_ind, max_row_ind, dtype=int):
for j in np.arange(min_col_ind, max_col_ind, dtype=int):
A.append(data[(i - half_size):(i + half_size + 1), (j - half_size):(j + half_size + 1)].ravel().tolist())
b.append(output[i, j])
if len(A) == csize * csize and np.linalg.matrix_rank(A) == csize * csize:
return (np.linalg.pinv(A)#np.array(b)[:, np.newaxis]).reshape(csize, csize)
if len(A) < csize*csize:
raise Exception("Insufficient data")
result = reconstruct(source_dataset, output_dataset, 3)
I got the following result
array([[ 1.00000000e+00, -1.77635684e-15, -1.11022302e-16],
[ 0.00000000e+00, 1.00000000e+00, -8.88178420e-16],
[ 0.00000000e+00, -1.22124533e-15, 1.00000000e+00]])
So, it works as expected; but definitely need to be improved to take into account edge effects, case when size of window is even etc.

Elementwise multiplication of NumPy arrays of matrices

I have two NumPy arrays (of equal length), each with (equally-sized, square) NumPy matrices as elements. I want to do elementwise matrix multiplication of these two arrays, i.e. get back a single array where the i-th element is the matrix product of the i-th elements of my two arrays.
When I simply try to multiply the arrays together, it seems that the program tries to calculate the matrix product of the arrays, and then fails because their dimensionality is too high (1 for the array + 2 for the matrices which are its elements).
The problem could of course be solved with a for-loop, but I was hoping that there was some way in which it could be done that keeps everything internal to NumPy, in order to take full advantage of its increased efficiency.f
EDIT:
To clarify, say I have two arrays np.array([A, B, C]) and np.array([X, Y, Z]) where A, B, C, X, Y and Z are all 3x3 square matrices, what I need is a function that will return np.array([A*X, B*Y, C*Z]), where * is matrix multiplication.
Operators are "element-wise" by default for numpy arrays. Just use the # operator (matrix multiplication) instead of *:
In [24]: A = np.arange(9).reshape(3,3)
In [25]: X = np.array([A[:], A[:]*2, A[:]*3])
In [26]: Y = X[:]
In [27]: X # Y
Out[27]:
array([[[ 15, 18, 21],
[ 42, 54, 66],
[ 69, 90, 111]],
[[ 60, 72, 84],
[168, 216, 264],
[276, 360, 444]],
[[135, 162, 189],
[378, 486, 594],
[621, 810, 999]]])
In [28]: X[0] # Y[0]
Out[28]:
array([[ 15, 18, 21],
[ 42, 54, 66],
[ 69, 90, 111]])
In [29]: X[1] # Y[1]
Out[29]:
array([[ 60, 72, 84],
[168, 216, 264],
[276, 360, 444]])
In [30]: X[2] # Y[2]
Out[30]:
array([[135, 162, 189],
[378, 486, 594],
[621, 810, 999]])
HTH.
* in numpy will do elementwise operations, i.e.:
>>> a
array([[[0.86812606, 0.16249293, 0.61555956],
[0.12381998, 0.84800823, 0.80731896],
[0.56910074, 0.4071833 , 0.069167 ]],
[[0.69742877, 0.45354268, 0.7220556 ],
[0.86638233, 0.97552151, 0.85580334],
[0.01171408, 0.35997806, 0.72999056]]])
>>> b
array([[[0.17162968, 0.52103661, 0.05433799],
[0.19999652, 0.01852179, 0.7936977 ],
[0.22392469, 0.34535168, 0.92808129]],
[[0.7044144 , 0.03183893, 0.16469416],
[0.6214784 , 0.57722859, 0.23789282],
[0.934214 , 0.61396596, 0.5356328 ]]])
>>> a * b
array([[[0.1489962 , 0.08466477, 0.03344827],
[0.02476357, 0.01570663, 0.6407672 ],
[0.12743571, 0.14062144, 0.06419259]],
[[0.49127887, 0.01444031, 0.11891834],
[0.5384379 , 0.5630989 , 0.20358947],
[0.01094346, 0.22101428, 0.39100689]]])
Isn't this what you looking for?

Categories

Resources