Confusion about homography matrix - python

I'm trying to get a homography matrix that describes the transformation from one image to another. I tried doing this by using an eigendecomposition and taking the smallest eigenvector. Apparently, I have to reshape it into a 3x3 matrix, but the numpy linalg function returns an eigenvalue of shape (9,9). (trying to compute it from 4 points)
A is a 8x9 matrix. pts1 and pts2 are arrays of 4 points in the source image and the target image respectively.
Code starts from symmetry matrix for homography calculation (size 8x9)
A_t = A.transpose()
sym_mat = np.dot(A_t,A)
eig_val,eig_vec = np.linalg.eig(sym_mat)
#sort according to value
idx = np.argsort(eig_val)
eig_val = eig_val[idx]
eig_vec = eig_vec[:,idx]
# Return the eigenvector corresponding to the smallest eigenvalue, reshaped
# as a 3x3 matrix.
H = np.reshape(smallest,(3,3))
`

Related

Do the projection (with Jacobian) and marginalisation (inversion of matrix and remove a row/column and reinversion) commute?

I try to check the equality or the inequality between 2 Fisher matrices.
The goal is too see if the projection (with Jacobian) and marginalisation (inversion of matrix and remove a row/column and reinversion) commute.
Each of these 2 matrices is computed slightly differently. These 2 matrices are Fisher matrices.
Actually, this is the computation of changing parameters between initial parameters for each row/colum and final parameters for the final matrix. That's why in both computations, I am using the Jacobian J formulating the derivatives between initial and final parameters :
The formula is : F_final = J^T F_initial J
The first matrix has size 5x5 and the second one has 4x4 size. They are identical except the 4th row/column.
First : I inverse the 5x5 matrix (which gives a 5x5 covariance matrix). Then, I "marginalise", that is to say, I remove the 4th row/column of this covariance matrix. Then, I inverse again to get a 4x4 matrix.
Finally, I perform a projection with a Jacobian 4x4 with formula : F_final = J^T F_initial J : so I get at the end a 4x4 matrix
For the second matrix to build : I am doing directly projection on 5x5 second matrix (which I recall is identical to the 5x5 except for the 4th row/column).
I perform this projection with the Jacobian 5x5. Then I get the second projected matrix 5x5. Finally, I remove the 4th row/column on this 5x5 matrix to get a 4x4 matrix new projected matrix.
I wonder under which conditions I could have equality between the 2 matrices 4x4. I don't know if my method is correct.
To show you a practical example, I put below a small Matlab script that tries to follow all the reasoning explained above :
clear;
clc;
% Big_31 Fisher :
FISH_Big_1_SYM = sym('sp_', [4,4], 'real');
% Force symmetry for Big_31
FISH_Big_1_SYM = tril(FISH_Big_1_SYM.') + triu(FISH_Big_1_SYM,1);
% Big_32 Fisher :
FISH_Big_2_SYM = sym('sp_', [5,5], 'real');
% Force symmetry for Big_32
FISH_Big_2_SYM = tril(FISH_Big_2_SYM.') + triu(FISH_Big_2_SYM,1);
% Jacobian 1
J_1_SYM = sym('j_', [4,4], 'real');
% Jacobian 2
J_2_SYM = sym('j_', [5,5], 'real');
% Remove 4th row/column
J_2_SYM(4,:) = [];
J_2_SYM(:,4) = [];
% Projection
FISH_proj_1 = J_1_SYM'*FISH_Big_1_SYM*J_1_SYM;
size(FISH_proj_1)
% Invert Fisher_2
COV_Big_2_SYM = inv(FISH_Big_2_SYM);
% Remove 4th row/column
COV_Big_2_SYM(4,:) = [];
COV_Big_2_SYM(:,4) = [];
% Re-inverse
FISH_Big_2_SYM_new = inv(COV_Big_2_SYM);
% Projection 2x2
FISH_proj_2 = J_2_SYM'*FISH_Big_2_SYM_new*J_2_SYM;
size(FISH_proj_2)
% Test equality between 2 matrices
isequal(FISH_proj_1,FISH_proj_2)
The problem with this script is even I have small matrices (4x4 or 5x5), the code takes a little bit long runtime but the result is that matrices are different.
Update
I gave some feedback from persons. An important point is at this portion of Matlab code :
When I do :
% Remove 4th row/column
J_2_SYM(4,:) = [];
J_2_SYM(:,4) = [];
I don't remove elements line j_5_1, j_5_2, j_5_3 of Jacobian J, these terms won't disappear when I do the projection. On the other side, these terms will remain in the other method, in the sense that I take into account of them.
So is it a lost battle ?
If yes, which modifications or assumptions could lead to have an equality ? i.e to have both operations do commute.
To do np.dot last dimension of first matrix must be the same as first dimension of second one. They are not, so you are getting ValueError, that shapes are not aligned.
Everything seems to be fine as you printed, but then you forgot about lines:
j_temp = np.copy(J_2_SYM)
# Add row/col into J_2_SYM
j_temp = np.insert(j_temp, 2, J_NEU_row_SYM[0,:], axis=0)
j_temp = np.insert(j_temp, 2, J_NEU_col_SYM[:,0], axis=1)
# Copy built J_2_SYM
J_2_SYM = np.copy(j_temp)
So that's where you change size of J_2_SYM, and after all it is (33, 16), so you cannot do dot product with (32, 32) array.

is it possible to optimize submatrixes operations in numpy?

I'm working on a project and i'm doing additions on diagonal submatrixes of a square matrix of big size (180x180) with a "kernel" square matrix of size around 40*40.
size1 = matrix1.shape[0] #square matrix shape 180x180
sizeKernel = kernelMatrix.shape[0] # square matrix shape 40x40
newMatrix1 = matrix1.copy()
np.fill_diagonal(newMatrix1, 0)
for i in range(size1):
newMatrix1[i:i+sizeKernel,i:i+sizeKernel] += matrix1[i][i] * kernelMatrix[:size1-i, :size1-i]
The goal is to distribute diagonal coefficients with the kernel matrix to other coefficients (a kind of partial convolution).
As an example, let's say the matrix is size 4x4 and the kernel is size 2x2:
a = [[1,2,1,0],
[3,-1,0,0],
[0,1,1,1],
[0,2,0,2]]
kern = [[1,2],
[0,1]]
we create the matrix res by copying every coef of a but the diagonal coefs:
res = [[0,2,1,0],
[3,0,0,0],
[0,1,0,1],
[0,2,0,0]]
we distribute every coefficient at diagonal to the bottom right submatrix of same size as kernel (if not possible we just take the remaining and cut the kernel matrix)
For the first coefficient of diagonal it gives:
res[0:2, 0:2] = res[0:2,0:2] + a[0,0]*kern
res[0:2, 0:2] = [[0,2] + 1 * [[1,2],
[3,0]] [0,1]]
res = [[1,4,1,0],
[3,1,0,0],
[0,1,0,1],
[0,2,0,0]]
Then we do the same idea for the other coefs. For the last coef of diagonal, instead of taking the whole kernel, we take apply the same thing extending the elements of the matrix with 0s.
As it is not vectorized, it takes quite a long time to compute this.
Another way to compute this would be to use directly the scipy method fftconvolve or convolve2d only on the diagonal matrix extracted, and add the result to the matrix with 0s on the diagonal:
newMatrix = matrix1.copy()
matrix1Size = matrix1.shape[0]
np.fill_diagonal(newMatrix , 0)
newMatrix += scipy.signal.fftconvolve(matrix1, kernelMatrix)[:matrix1Size,:matrixSize]
but it takes approximately as much time as the first method (of course their are more calculation if we do the full convolution of a whole matrix)
Is there any method to vectorize this calculation on submatrixes?
Thank you in advance!

python: how to calculate eigenvalues and eigenvectors of structure tensor?

In a for loop where I iterate over the image size I calculate the 2D structure tensor for each pixel:
tensor = np.array([[pixel_image_1, pixel_image_3], [pixel_image_3, pixel_imgage_2]])
where convolution happens like this:
pixel_image_1 = Gaussian * ux^2 (at [i,j])
pixel_image_2 = Gaussian * uy^2 (at [i,j])
pixel_image_3 = Gaussian * ux x uy (at [i,j])
Next I need the einenvectors(v1,v2)/eigenvalues(mu1,mu2) of the structure tensor
I found out that I can calculate the eigenvectors/eigenvalues like the following:
eigenvalue, eigenvector = np.linalg.eig(tensor)
I do not understand how to extract (v1,v2), (mu1,mu2) out of eigenvalue, eigenvector?
Could someone help me here pls?

Calculating Mean Squared Error through Matrix Arithmetic on Numpy Matrices of Binary Images

I have 2 binary images, one is a ground truth, and one is an image segmentation that I produced.
I am trying to calculate the mean squared distance ...
Let G = {g1, g2, . . . , gN} be the points in the ground truth boundary.
Let B = {b1, b2, . . . , bM} be the points in the segmented boundary.
Define d(p, p0) be a measure of distance between points p and p0 (e.g. Euclidean, city block, etc.)
between the two images using the following algorithm.
def MSD(A,G):
'''
Takes a thresholded binary image, and a ground truth img(binary), and computes the mean squared absolute difference
:param A: The thresholded binary image
:param G: The ground truth img
:return:
'''
sim = np.bitwise_xor(A,G)
sum = 0
for i in range(0,sim.shape[0]):
for j in range(0,sim.shape[1]):
if (sim[i,j] == True):
min = 9999999
for k in range(0,sim.shape[0]):
for l in range(0,sim.shape[1]):
if (sim[k, l] == True):
e = abs(i-k) + abs(j-l)
if e < min:
min = e
mink = k
minl = l
sum += min
return sum/(sim.shape[0]*sim.shape[1])
This algorithm is too slow though and never completes.
This example and this example (Answer 3) might show method of how to get the mean squared error using Matrix arithmetic, but I do not understand how these examples make any sense or why they work.
So if I understand your formula and code correctly, you have one (binary) image B and a (ground truth) image G. "Points" are defined by the pixel positions where either image has a True (or at least nonzero) value. From your bitwise_xor I deduce that both images have the same shape (M,N).
So the quantity d^2(b,g) is at worst an (M*N, M*N)-sized array, relating each pixel of B to each pixel of G. It's even better: we only need a shape (m,n) if there are m nonzeros in B and n nonzeros in G. Unless your images are huge we can get away with keeping track of this large quantity. This will cost memory but we will win a lot of CPU time by vectorization. So then we only have to find the minimum of this distance with respect to every n possible value, for each m. Then just sum up each minimum. Note that the solution below uses extreme vectorization, and it can easily eat up your memory if the images are large.
Assuming Manhattan distance (with the square in d^2 which seems to be missing from your code):
import numpy as np
# generate dummy data
M,N = 100,100
B = np.random.rand(M,N) > 0.5
G = np.random.rand(M,N) > 0.5
def MSD(B, G):
# get indices of nonzero pixels
nnz_B = B.nonzero() # (x_inds, y_inds) tuple, x_inds and y_inds are shape (m,)
nnz_G = G.nonzero() # (x_inds', y_inds') each with shape (n,)
# np.array(nnz_B) has shape (2,m)
# compute squared Manhattan distance
dist2 = abs(np.array(nnz_B)[...,None] - np.array(nnz_G)[:,None,:]).sum(axis=0)**2 # shape (m,n)
# alternatively: Euclidean for comparison:
#dist2 = ((np.array(nnz_B)[...,None] - np.array(nnz_G)[:,None,:])**2).sum(axis=0)
mindist2 = dist2.min(axis=-1) # shape (m,) of minimum square distances
return mindist2.mean() # sum divided by m, i.e. the MSD itself
print(MSD(B, G))
If the above uses too much memory we can introduce a loop over the elements of nnz_B, and only vectorize in the elements of nnz_G. This will take more CPU power and less memory. This trade-off is typical for vectorization.
An efficient method for calculating this distance is using the Distance Transform. SciPy has an implementation in the ndimage package: scipy.ndimage.morphology.distance_transform_edt.
The idea is to compute a distance transform for the background of the ground-truth image G. This leads to a new image D that is 0 for each pixel that is nonzero in G, and for each zero pixel in G there will be the distance to the nearest nonzero pixel.
Next, for each nonzero pixel in B (or A in the code that you posted), you look at the corresponding pixel in D. This is the distance to G for that pixel. So, simply average all the values in D for which B is nonzero to obtain your result.
import numpy as np
import scipy.ndimage as nd
import matplotlib.pyplot as pp
# Create some test data
img = pp.imread('erika.tif') # a random image
G = img > 120 # the ground truth
img = img + np.random.normal(0, 20, img.shape)
B = img > 120 # the other image
D = nd.morphology.distance_transform_edt(~G)
msd = np.mean(D[B]**2)

Difference between the size of the original image decomposed by wavedec2 and reconstructed by waverec2

I'm using Pywt to work with wavelet transform. I'm doing the decomposition to get the detail coefficients and then the reconstruction by now.
I invoke the wavedec2 function with this arguments: X (ndarray 125x125), w = 'sym4', n = 4
coeffs = pywt.wavedec2(x,w,n);
The problem is when i make the reconstruction, I'm wating for a ndarray of 125x125 but the result of this function is a ndarray of 126x126.
xc = pywt.waverec2(coeffs,w);
Why I have an extra row and column?

Categories

Resources