Mamo neki
parent
bae4983ce1
commit
cf2f58566f
|
@ -1,29 +0,0 @@
|
||||||
import numpy as np
|
|
||||||
import cv2
|
|
||||||
from matplotlib import pyplot as plt
|
|
||||||
|
|
||||||
def normalize_points(P):
|
|
||||||
# P must be a Nx2 vector of points
|
|
||||||
# first coordinate is x, second is y
|
|
||||||
|
|
||||||
# returns: normalized points in homogeneous coordinates and 3x3 transformation matrix
|
|
||||||
|
|
||||||
mu = np.mean(P, axis=0) # mean
|
|
||||||
scale = np.sqrt(2) / np.mean(np.sqrt(np.sum((P-mu)**2,axis=1))) # scale
|
|
||||||
T = np.array([[scale, 0, -mu[0]*scale],[0, scale, -mu[1]*scale],[0,0,1]]) # transformation matrix
|
|
||||||
P = np.hstack((P,np.ones((P.shape[0],1)))) # homogeneous coordinates
|
|
||||||
res = np.dot(T,P.T).T
|
|
||||||
return res, T
|
|
||||||
|
|
||||||
def draw_epiline(l,h,w):
|
|
||||||
# l: line equation (vector of size 3)
|
|
||||||
# h: image height
|
|
||||||
# w: image width
|
|
||||||
|
|
||||||
x0, y0 = map(int, [0, -l[2]/l[1]])
|
|
||||||
x1, y1 = map(int, [w-1, -(l[2]+l[0]*w)/l[1]])
|
|
||||||
|
|
||||||
plt.plot([x0,x1],[y0,y1],'r')
|
|
||||||
|
|
||||||
plt.ylim([0,h])
|
|
||||||
plt.gca().invert_yaxis()
|
|
|
@ -4,14 +4,6 @@ from matplotlib import pyplot as plt
|
||||||
import cv2
|
import cv2
|
||||||
import uz_framework.image as uz_image
|
import uz_framework.image as uz_image
|
||||||
import uz_framework.text as uz_text
|
import uz_framework.text as uz_text
|
||||||
import os
|
|
||||||
|
|
||||||
##############################################
|
|
||||||
# EXCERCISE 1: Exercise 1: Image derivatives #
|
|
||||||
##############################################
|
|
||||||
|
|
||||||
def ex1():
|
|
||||||
one_b()
|
|
||||||
|
|
||||||
def one_a() -> None:
|
def one_a() -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -50,16 +42,114 @@ def one_c() -> None:
|
||||||
in x axis in the left camera and at the pixel 300 in the right camera. How far is the
|
in x axis in the left camera and at the pixel 300 in the right camera. How far is the
|
||||||
object (in meters) in this case? How far is the object if the object is detected at
|
object (in meters) in this case? How far is the object if the object is detected at
|
||||||
pixel 540 in the right camera? Solve this task analytically and bring your solution
|
pixel 540 in the right camera? Solve this task analytically and bring your solution
|
||||||
to the presentation of the exercise.
|
to the presentation of the exercise
|
||||||
|
|
||||||
|
z1 = 0,16216m
|
||||||
|
z2 = 0.402m
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def one_d() -> None:
|
||||||
|
"""
|
||||||
|
Write a script that calculates the disparity for an image pair. Use
|
||||||
|
the images in the directory disparity. Since the images were pre-processed we can
|
||||||
|
limit the search for the most similar pixel to the same row in the other image. Since
|
||||||
|
just the image intensity carries too little information, we will instead compare small
|
||||||
|
"""
|
||||||
|
|
||||||
|
left_image = uz_image.imread_gray('/home/gasperspagnolo/Documents/faks_git/uz_assignments/assignment5/data/disparity/office_left.png', uz_image.ImageType.float64)
|
||||||
|
right_image = uz_image.imread_gray('/home/gasperspagnolo/Documents/faks_git/uz_assignments/assignment5/data/disparity/office_right.png', uz_image.ImageType.float64)
|
||||||
|
|
||||||
|
d = uz_image.get_disparity(left_image, right_image)
|
||||||
|
|
||||||
|
plt.imshow(d, cmap='gray')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def two_b() -> None:
|
||||||
|
"""
|
||||||
|
mplement a function fundamental_matrix that is given a set of (at least) eight
|
||||||
|
pairs of points from two images and computes the fundamental matrix using the
|
||||||
|
eight-point algorithm.
|
||||||
|
As the eight-point algorithm can be numerically unstable, it is usually not executed
|
||||||
|
directly on given pairs of points. Instead, the input is first normalized by centering
|
||||||
|
them to their centroid and scaling their positions so that the average distance to the
|
||||||
|
centroid is √2. To achieve this, you can use the function normalize_points from
|
||||||
|
the supplementary material.
|
||||||
|
Extend the function fundamental_matrix so that it first normalizes the input point-
|
||||||
|
set of the left camera (we get transformed points and the transformation matrix T1)
|
||||||
|
and then transform the input point set of the right camera (we get the transformed
|
||||||
|
points and the transformation matrix T2). Using the transformed points the algo-
|
||||||
|
rithm computes fundamental matrix ˆF, then transforms it into the original space
|
||||||
|
using both transformation matrices F = TT
|
||||||
|
2 ˆFT1.
|
||||||
|
Test your function for fundamental matrix estimation using ten correspondence
|
||||||
|
pairs that you load from the file house_points.txt. The columns are formatted as
|
||||||
|
follows: x1, y1, x2, y2, i.e. the first column contains the x-coordinates of the points for
|
||||||
|
the first image etc. Compute the fundamental matrix F and for each point in each
|
||||||
|
image calculate the corresponding epipolar line in the other image. You can draw the
|
||||||
|
epipolar lines using draw_epiline from the supplementary material. According to
|
||||||
|
epipolar geometry the corresponding epipolar line should pass through the point. As
|
||||||
|
a testing reference the correct fundamental matrix is included in the supplementary
|
||||||
|
material in file house_fundamental.txt
|
||||||
|
"""
|
||||||
|
|
||||||
|
points = np.loadtxt('./data/epipolar/house_points.txt')
|
||||||
|
left_image = uz_image.imread_gray('./data/epipolar/house1.jpg', uz_image.ImageType.float64)
|
||||||
|
right_image = uz_image.imread_gray('./data/epipolar/house2.jpg', uz_image.ImageType.float64)
|
||||||
|
|
||||||
|
p1 = points[:, :2]
|
||||||
|
p2 = points[:, 2:]
|
||||||
|
|
||||||
|
lines_p1, lines_p2 = uz_image.get_epipolar_lines(p1, p2)
|
||||||
|
|
||||||
|
plt.imshow(left_image, cmap='gray')
|
||||||
|
for line, point in zip(lines_p1, p1):
|
||||||
|
uz_image.draw_epiline(line, left_image.shape[0], left_image.shape[1])
|
||||||
|
plt.plot(point[0], point[1], 'r', marker='o', markersize=10)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
plt.imshow(right_image, cmap='gray')
|
||||||
|
for line, point in zip(lines_p2, p2):
|
||||||
|
uz_image.draw_epiline(line, right_image.shape[0], right_image.shape[1])
|
||||||
|
plt.plot(point[0], point[1], 'r', marker='o', markersize=10)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def two_c() -> None:
|
||||||
|
"""
|
||||||
|
We use the reprojection error as a quantitative measure of the quality of the estimated fundamental matrix.
|
||||||
|
Write a function reprojection_error that calculates the reprojection error of a
|
||||||
|
fundamental matrix F given two matching points. For each point, the function
|
||||||
|
should calculate the corresponding epipolar line from the point’s match in the other
|
||||||
|
image, then calculate the perpendicular distance between the point and the line
|
||||||
|
where a, b and c are the parameters of the epipolar line. Finally, the function should
|
||||||
|
return the average of the two distances.
|
||||||
|
Write a script that performs two tests: (1) compute the reprojection error for points
|
||||||
|
p1 = [85, 233]T
|
||||||
|
in the left image-plane and p2 = [67, 219]T
|
||||||
|
in right image-plane using
|
||||||
|
the fundamental matrix (the error should be approximately 0.15 pixels). (2) Load
|
||||||
|
the points from the file house_points.txt and compute the average of symmetric
|
||||||
|
reprojection errors for all pairs of points. If your calculation is correct, the average
|
||||||
|
error should be approximately 0.33 pixels.
|
||||||
|
"""
|
||||||
|
points = np.loadtxt('./data/epipolar/house_points.txt')
|
||||||
|
p1 = points[:, :2]
|
||||||
|
p2 = points[:, 2:]
|
||||||
|
F = uz_image.fundamential_matrix(p1, p2)
|
||||||
|
print('Reprojection error for house points', uz_image.reprojection_error(F, np.array([[85, 233], [85, 233]]), np.array([[67, 219], [67, 219]])))
|
||||||
|
print('Reprojection error for house points', uz_image.reprojection_error(F, p1, p2))
|
||||||
|
|
||||||
|
|
||||||
# ######## #
|
# ######## #
|
||||||
# SOLUTION #
|
# SOLUTION #
|
||||||
# ######## #
|
# ######## #
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ex1()
|
#one_d()
|
||||||
|
two_c()
|
||||||
#ex2()
|
#ex2()
|
||||||
#ex3()
|
#ex3()
|
||||||
|
|
||||||
|
|
|
@ -1460,3 +1460,160 @@ def sift(grayscale_image: npt.NDArray[np.float64],
|
||||||
keypoints = keypoints[:, :-2]
|
keypoints = keypoints[:, :-2]
|
||||||
|
|
||||||
return np.array(keypoints), np.array(descriptors)
|
return np.array(keypoints), np.array(descriptors)
|
||||||
|
|
||||||
|
|
||||||
|
def get_disparity(left_image: npt.NDArray[np.float64], right_image: npt.NDArray[np.float64],\
|
||||||
|
window_size: int = 35, patch_size: int = 11, reduce_size: float = 0.5) -> npt.NDArray[np.float64]:
|
||||||
|
|
||||||
|
if(patch_size % 2 == 0 or window_size % 2 == 0):
|
||||||
|
raise ValueError("Patch/window size must be odd")
|
||||||
|
|
||||||
|
# Reduce the size of the images
|
||||||
|
left_image = cv2.resize(left_image, None, fx=reduce_size, fy=reduce_size)
|
||||||
|
right_image = cv2.resize(right_image, None, fx=reduce_size, fy=reduce_size)
|
||||||
|
|
||||||
|
disparity = np.zeros(left_image.shape)
|
||||||
|
|
||||||
|
def get_patches_per_row(y, image, patch_size):
|
||||||
|
# Get the patches for the given y value
|
||||||
|
dd = patch_size//2
|
||||||
|
patches = []
|
||||||
|
for x in range(dd, image.shape[1] - dd, 1):
|
||||||
|
patches.append(image[y-dd:y+dd, x-dd:x+dd+1])
|
||||||
|
return np.array(patches)
|
||||||
|
|
||||||
|
def compute_ncc(a, bs):
|
||||||
|
nccs = []
|
||||||
|
a = a - np.average(a)
|
||||||
|
for b in bs:
|
||||||
|
b = b - np.average(b)
|
||||||
|
nccs.append(np.sum(a*b) / (np.sqrt(np.sum(a**2)) * np.sqrt(np.sum(b**2))))
|
||||||
|
return np.array(nccs)
|
||||||
|
|
||||||
|
# Get the patches
|
||||||
|
dd = patch_size//2
|
||||||
|
dw = window_size//2
|
||||||
|
|
||||||
|
for y in tqdm(range(dd, left_image.shape[0] - dd, 1), desc="Computing disparity"):
|
||||||
|
# Get the patches for the right image
|
||||||
|
right_patches = get_patches_per_row(y, right_image, patch_size)
|
||||||
|
left_patches = get_patches_per_row(y, left_image, patch_size)
|
||||||
|
dd = patch_size//2
|
||||||
|
dw = window_size//2
|
||||||
|
xlen = len(left_patches)
|
||||||
|
|
||||||
|
for ix in range(xlen):
|
||||||
|
# Compute the NCC
|
||||||
|
x = ix + dd
|
||||||
|
patch = left_patches[ix]
|
||||||
|
|
||||||
|
window = right_patches[max(0, ix-dw):min(xlen, ix+dw+1)]
|
||||||
|
ncc = compute_ncc(patch, window)
|
||||||
|
|
||||||
|
# Get the index of the maximum value
|
||||||
|
index = np.argmax(ncc) + 1
|
||||||
|
|
||||||
|
# Compute the disparity
|
||||||
|
disparity[y, x] = abs(dw - index)
|
||||||
|
|
||||||
|
return disparity
|
||||||
|
|
||||||
|
def normalize_points(P):
|
||||||
|
# P must be a Nx2 vector of points
|
||||||
|
# first coordinate is x, second is y
|
||||||
|
|
||||||
|
# returns: normalized points in homogeneous coordinates and 3x3 transformation matrix
|
||||||
|
|
||||||
|
mu = np.mean(P, axis=0) # mean
|
||||||
|
scale = np.sqrt(2) / np.mean(np.sqrt(np.sum((P-mu)**2,axis=1))) # scale
|
||||||
|
T = np.array([[scale, 0, -mu[0]*scale],[0, scale, -mu[1]*scale],[0,0,1]]) # transformation matrix
|
||||||
|
P = np.hstack((P,np.ones((P.shape[0],1)))) # homogeneous coordinates
|
||||||
|
res = np.dot(T,P.T).T
|
||||||
|
return res, T
|
||||||
|
|
||||||
|
def draw_epiline(l,h,w):
|
||||||
|
# l: line equation (vector of size 3)
|
||||||
|
# h: image height
|
||||||
|
# w: image width
|
||||||
|
|
||||||
|
x0, y0 = map(int, [0, -l[2]/l[1]])
|
||||||
|
x1, y1 = map(int, [w-1, -(l[2]+l[0]*w)/l[1]])
|
||||||
|
|
||||||
|
plt.plot([x0,x1],[y0,y1],'r')
|
||||||
|
|
||||||
|
plt.ylim([0,h])
|
||||||
|
plt.gca().invert_yaxis()
|
||||||
|
|
||||||
|
def fundamential_matrix(P1, P2):
|
||||||
|
# P1 and P2 are Nx2 vectors of points
|
||||||
|
|
||||||
|
# returns: fundamental matrix
|
||||||
|
N = P1.shape[0]
|
||||||
|
# normalize points
|
||||||
|
P1n, T1 = normalize_points(P1)
|
||||||
|
P2n, T2 = normalize_points(P2)
|
||||||
|
|
||||||
|
# compute fundamental matrix
|
||||||
|
A = np.zeros((N,9))
|
||||||
|
for i in range(N):
|
||||||
|
u = P1n[i,0]
|
||||||
|
v = P1n[i,1]
|
||||||
|
u_ = P2n[i,0]
|
||||||
|
v_ = P2n[i,1]
|
||||||
|
A[i,:] = [u*u_, u_*v, u_, u*v_, v*v_, v_, u, v, 1]
|
||||||
|
|
||||||
|
U,S,V = np.linalg.svd(A)
|
||||||
|
# F is the last column of V
|
||||||
|
F = V[-1,:].reshape((3,3))
|
||||||
|
|
||||||
|
# enforce rank 2
|
||||||
|
U,S,V = np.linalg.svd(F)
|
||||||
|
S[-1] = 0
|
||||||
|
F = np.dot(U,np.dot(np.diag(S),V))
|
||||||
|
|
||||||
|
# denormalize
|
||||||
|
F = np.dot(T2.T,np.dot(F,T1))
|
||||||
|
|
||||||
|
return F
|
||||||
|
|
||||||
|
|
||||||
|
def get_epipolar_lines(p1, p2):
|
||||||
|
F = fundamential_matrix(p1, p2)
|
||||||
|
|
||||||
|
p1 = np.hstack((p1, np.ones((p1.shape[0], 1))))
|
||||||
|
p2 = np.hstack((p2, np.ones((p2.shape[0], 1))))
|
||||||
|
|
||||||
|
lines_p2 = np.dot(F, p1.T).T
|
||||||
|
lines_p1 = np.dot(F.T, p2.T).T
|
||||||
|
|
||||||
|
return lines_p1, lines_p2
|
||||||
|
|
||||||
|
|
||||||
|
def reprojection_error(F, p1, p2):
|
||||||
|
|
||||||
|
# Add a column of ones to the points, even if there is single point
|
||||||
|
p1 = np.hstack((p1, np.ones((p1.shape[0], 1))))
|
||||||
|
p2 = np.hstack((p2, np.ones((p2.shape[0], 1))))
|
||||||
|
|
||||||
|
lines_right = []
|
||||||
|
lines_left = []
|
||||||
|
for p_left, p_right in zip(p1, p2):
|
||||||
|
# Left to right
|
||||||
|
lines_right.append(np.dot(F, p_left).T)
|
||||||
|
# Right to left
|
||||||
|
lines_left.append(np.dot(F.T, p_right).T)
|
||||||
|
|
||||||
|
lines_right = np.array(lines_right)
|
||||||
|
lines_left = np.array(lines_left)
|
||||||
|
|
||||||
|
# Normalize
|
||||||
|
lines_right = lines_right / lines_right[:, 2][:, np.newaxis]
|
||||||
|
lines_left = lines_left / lines_left[:, 2][:, np.newaxis]
|
||||||
|
|
||||||
|
# Compute the distances
|
||||||
|
d_left = np.abs(np.sum(lines_right * p2, axis=1)) / np.sqrt(lines_right[:, 0] ** 2 + lines_right[:, 1] ** 2)
|
||||||
|
d_right = np.abs(np.sum(lines_left * p1, axis=1)) / np.sqrt(lines_left[:, 0] ** 2 + lines_left[:, 1] ** 2)
|
||||||
|
|
||||||
|
repr_err = np.sum((d_left + d_right) ) / (2 * p1.shape[0])
|
||||||
|
|
||||||
|
return repr_err
|
||||||
|
|
Loading…
Reference in New Issue