I am trying to do ray tracing implementation using python but i am getting a white and black image for output. the shape is correct but the the image lacks colors. I am new to this type of programming so i dont really know what is causing the problem here.
My code
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot
def vector_nomalization(vector): #function to normalize a vector
return vector / np.linalg.norm(vector)
def intersection_with_sphere(center, radius, origin, direction_vector): #function to detect intersection between sphere and ray
b = 2 * np.dot(direction_vector, origin - center)
c = np.linalg.norm(origin - center) ** 2 - radius ** 2 # die länge von (origin - center)^2 - radius
delta = b **2 - 4 * c
if delta > 0:
t1 = (-b +np.sqrt(delta))/2
t2 = (-b - np.sqrt(delta))/2
if t1 > 0 and t2 > 0:
return min(t1,t2) #distance from origin to the nearest intersection point
return None
def closest_intersection(spheres, origin, direction): #find closest sphere that intersects with our ray
distances = [intersection_with_sphere(sphere['center'], sphere['radius'], origin, direction) for sphere in spheres]
closest_sphere = None
min_distance = np.inf
for index, distance in enumerate(distances):
if distance and distance < min_distance:
min_distance = distance
closest_sphere = spheres[index]
return closest_sphere, min_distance
def reflection_ray(vector, axis):
return vector - 2 * np.dot(vector, axis) * axis
spheres =[
{ 'center': np.array([-0.2, 0, -1]), 'radius': 0.7, 'ambient': np.array([0.1, 0, 0]), 'Diffuse': np.array([0.7, 0, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([0.1, -0.3, 0]), 'radius': 0.1, 'ambient': np.array([0.1, 0, 0.1]), 'Diffuse': np.array([0.7, 0, 0.7]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([-0.3, 0, 0]), 'radius': 0.15, 'ambient': np.array([0, 0.1, 0]), 'Diffuse': np.array([0, 0.6, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([0, -9000, 0]), 'radius': 9000 - 0.7, 'ambient': np.array([0.1, 0.1, 0.1]), 'Diffuse': np.array([0.6, 0.6, 0.6]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5}
]
light = {'position': np.array([5, 5, 5]), 'ambient': np.array([1, 1, 1]), 'Diffuse': np.array([1, 1, 1]), 'specular': np.array([1, 1, 1])}
width = 300
height = 200
max_depth = 3
camera = np.array([0, 0, 1])
ratio = float(width) / height # ratio = image width / image height
screen = (
-1, 1 / ratio, 1, -1 / ratio) # screen defined by 4 numbers: left,top,right,bottom (x coordinate ranges from -1 to 1)
image = np.zeros((height, width, 3))
for i, y in enumerate(np.linspace(screen[1], screen[3], height)): # splits the screen into width and height points in x and y directions
for j, x in enumerate(np.linspace(screen[0], screen[2], width)):
pixel = np.array([x, y, 0])
origin = camera
direction_vector = vector_nomalization(pixel - origin) #oder auch ray
color = np.zeros((3))
reflection = 1
for k in range(max_depth):
#intersection?
closest_sphere, min_distance = closest_intersection(spheres, origin, direction_vector)
if closest_sphere is None:
break
#intersectionpoint between ray and closest sphere
intersection = origin + min_distance * direction_vector
normalized_to_surface = vector_nomalization(intersection - closest_sphere['center'])
shifted_point = intersection + 1e-5 * normalized_to_surface
lights_intersection = vector_nomalization(light['position'] - shifted_point)
_, min_distance = closest_intersection(spheres, shifted_point, lights_intersection)
lights_intersection_distance = np.linalg.norm(light['position']-intersection)
shadowed = min_distance < lights_intersection_distance
if shadowed:
break
#RGB
illumination = np.zeros((3))
#diffuse
illumination += closest_sphere['Diffuse'] * light['Diffuse'] * np.dot(lights_intersection,
normalized_to_surface)
#specular
camera_intersection = vector_nomalization(camera - intersection)
camera_light = vector_nomalization(lights_intersection + camera_intersection)
illumination += closest_sphere['specular'] * light['specular'] * np.dot(normalized_to_surface,
camera_light)
color += reflection + illumination
reflection *= closest_sphere['reflection']
origin = shifted_point
direction_vector = reflection_ray(direction_vector,normalized_to_surface)
image[i, j] = np.clip(color, 0, 1)
print("progress: %d/%d" % (i + 1, height))
plt.imsave('image.png', image)
This is what i am getting
and this is how its supposed to look like
Why you're getting only white (and black) is because of your 'reflection'
you have hard-coded reflection = 1 on line 66 - The devil's in the details, as they say!
And then on line 103: color += reflection + illumination. As reflection starts at 1 for every new iteration in the for j, x loop, you're making sure here that color is at least 1, 1, 1. So that's why you're getting white.
Related
I'm trying to extract rotation matrix from affine transform matrix (via QR decomposition), but it gives me wrong rotations. After applying rotation vectors should have zero angle between them. I have no idea what is going wrong.
Here is the code that is currently giving the unexpected output:
import numpy as np
import math
np.set_printoptions(suppress=True)
def compute_affine(ins, out): # matrices 3x4
# https://stackoverflow.com/questions/8873462/how-to-perform-coordinates-affine-transformation-using-python
# finding transformation
l = len(ins)
entry = lambda r, d: np.linalg.det(np.delete(np.vstack([r, ins.T, np.ones(l)]), d, axis=0))
M = np.array([[(-1) ** i * entry(R, i) for R in out.T] for i in range(l + 1)])
print(M)
A, t = np.hsplit(M[1:].T / (-M[0])[:, None], [l - 1])
t = np.transpose(t)[0]
# output transformation
print("Affine transformation matrix:\n", A)
print("Affine transformation translation vector:\n", t)
# unittests
print("TESTING:")
for p, P in zip(np.array(ins), np.array(out)):
image_p = np.dot(A, p) + t
result = "[OK]" if np.allclose(image_p, P) else "[ERROR]"
print(p, " mapped to: ", image_p, " ; expected: ", P, result)
return A, t
def dot_product_angle(v1,v2):
if np.linalg.norm(v1) == 0 or np.linalg.norm(v2) == 0:
print("Zero magnitude vector!")
else:
vector_dot_product = np.dot(v1,v2)
arccos = np.arccos(vector_dot_product / (np.linalg.norm(v1) * np.linalg.norm(v2)))
angle = np.degrees(arccos)
return angle
return 0
if __name__=="__main__":
import numpy as np
src = np.array([
[1, 0, 0],
[0, 0, 1],
[1, 0, 1],
[1, 1, 1],
])
uv = np.array([
[0.1, 0],
[0, -0.1],
[0.1, -0.1],
[1, 1]
])
angle = 45
rot_matrix = np.array([[math.cos(angle), -math.sin(angle), 0 ],
[math.sin(angle), math.cos(angle), 0],
[0, 0, 1]])
dst = np.dot(src, rot_matrix)
# compute affine matrices
src_A, src_t = compute_affine(src, uv)
dst_A, dst_t = compute_affine(dst, uv)
src_Q, src_R = np.linalg.qr(np.vstack([src_A, np.cross(src_A[0], src_A[1])]))
dst_Q, dst_R = np.linalg.qr(np.vstack([dst_A, np.cross(dst_A[0], dst_A[1])]))
vec1 = np.dot(src_Q[:-1], src[0])
vec2 = np.dot(dst_Q[:-1], dst[0])
if not dot_product_angle(vec1, vec2) == 0:
raise Exception("Angle is invalid should be zero")
given a contour, I can extract mean, and eigenvectors by performing PCA. Then I want to project all pixels inside a contour on an eigenvector. Below are my code and my images
my input images
Read image, extract contours, and draw the first component
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
src = cv.imread(cv.samples.findFile('/Users/bryan/Desktop/lung.png'))
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
def drawAxis(img, p_, q_, colour, scale):
p = list(p_)
q = list(q_)
## [visualization1]
angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
# Here we lengthen the arrow by a factor of scale
q[0] = p[0] - scale * hypotenuse * cos(angle)
q[1] = p[1] - scale * hypotenuse * sin(angle)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
# create the arrow hooks
p[0] = q[0] + 9 * cos(angle + pi / 4)
p[1] = q[1] + 9 * sin(angle + pi / 4)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
p[0] = q[0] + 9 * cos(angle - pi / 4)
p[1] = q[1] + 9 * sin(angle - pi / 4)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
def getOrientation(pts, img, scale_factor=25):
## [pca]
# Construct a buffer used by the pca analysis
sz = len(pts)
data_pts = np.empty((sz, 2), dtype=np.float64)
for i in range(data_pts.shape[0]):
data_pts[i, 0] = pts[i, 0, 0]
data_pts[i, 1] = pts[i, 0, 1]
# Perform PCA analysis
mean = np.empty(0)
mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
# Store the center of the object
cntr = (int(mean[0, 0]), int(mean[0, 1]))
## [pca]
## [visualization]
# Draw the principal components
cv.circle(img, cntr, 3, (255, 0, 255), 2)
p1 = (
cntr[0] + scale_factor * eigenvectors[0, 0],
cntr[1] + scale_factor * eigenvectors[0, 1])
p2 = (
cntr[0] - scale_factor * eigenvectors[1, 0],
cntr[1] - scale_factor * eigenvectors[1, 1])
drawAxis(img, cntr, p1, (0, 255, 0), 4)
## [visualization]
# doing projections along eigenvectors
dim1_ = []
for _ in data_pts:
p = make_vector_projection(_, np.array(p1))
dim1_.append(p.astype(int))
dim1 = np.array(dim1_)
dim2_ = []
for _ in data_pts:
p = make_vector_projection(_, np.array(p2))
dim2_.append(p.astype(int))
dim2 = np.array(dim2_)
return mean, eigenvectors, eigenvalues, p1, p2, dim1, dim2
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
plt.figure(figsize=(10, 10))
plt.axis('equal')
plt.gca().invert_yaxis()
plt.imshow(src)
Look as I expected but I want to double-check, I compute angles between a random vector in the first dimension and the first eigenvector. I defined
def unit_vector(vector):
""" Returns the unit vector of the vector. """
return vector / np.linalg.norm(vector)
def angle_between(v1, v2):
""" Returns the angle in radians between vectors 'v1' and 'v2'::
>>> angle_between((1, 0, 0), (0, 1, 0))
1.5707963267948966
>>> angle_between((1, 0, 0), (1, 0, 0))
0.0
>>> angle_between((1, 0, 0), (-1, 0, 0))
3.141592653589793
"""
v1_u = unit_vector(v1)
v2_u = unit_vector(v2)
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
draw_point = dim1[45].astype(int) # extract the first dimension
print(draw_point, evecs[0], angle_between(draw_point, p1))
print('#'* 10)
I got output like below, angle_between is in radian, close to zero means they are paralleled
[ 97 148] [ 0.14189901 -0.98988114] 0.002321780300502494
##########
[332 163] [-0.22199134 -0.97504864] 0.0006249775357550807
##########
My problem occurs when I want to plot projected points on eigenvectors. Points I plotted don't lie on green line (my eigenvector). My code is
point_colors = [
(0, 0, 255), # blue
(0, 255, 0) # green
]
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
draw_point = dim1[45].astype(int)
print(draw_point, evecs[0], angle_between(draw_point, p1))
cv.circle(src, (draw_point[1], draw_point[0]), 7, point_colors[i], 2) # plot point
print('#'* 10)
and output
My question is, I want to plot all projected pixels on the eigenvector lines, but my calculation doesn't seem correct as points don't lie on the eigenvector lines. Could you help?
I am trying to solve a complicated problem.
For example, I have a batch of 2D predicted images (softmax output, value between 0 and 1) with size: Batch x H x W and ground truth Batch x H x W
The light gray color pixels are the background with value 0, and the dark gray color pixels are the foreground with value 1. I try to compute the mass center coordinates using scipy.ndimage.center_of_mass on each ground truth image. Then I get the center location point C (red color) for each ground truth. The C points set is Batch x 1.
Now, for each pixel A (yellow color) in the predicted images, I want to get three pixels B1, B2, B3 (blue color) which are the closest to A on the line AC (here C is corresponding location of mass center in ground truth).
I used following code to get the three closest points B1, B2, B3.
def connect(ends, m=3):
d0, d1 = np.abs(np.diff(ends, axis=0))[0]
if d0 > d1:
return np.c_[np.linspace(ends[0, 0], ends[1, 0], m + 1, dtype=np.int32),
np.round(np.linspace(ends[0, 1], ends[1, 1], m + 1))
.astype(np.int32)]
else:
return np.c_[np.round(np.linspace(ends[0, 0], ends[1, 0], m + 1))
.astype(np.int32),
np.linspace(ends[0, 1], ends[1, 1], m + 1, dtype=np.int32)]
So the B points set is Batch x 3 x H x W.
Then, I want to compute like this: |Value(A)-Value(B1)|+|Value(A)-Value(B2)|+|Value(A)-Value(B3)|. The size of the result should be Batch x H x W.
Is there any numpy vectorization tricks that can be used to update the value of each pixel in predicted images? Or can this be solved using pytorch functions? I need to find a method to update the whole image. The predicted image is the softmax output. I cannot use for loop to compute each single value since it will become non-differentiable. Thanks a lot.
As suggested by #Matin, you could consider Bresenham's algorithm to get your points on the AC line.
A simplistic PyTorch implementation could be as follows (directly adapted from the pseudo-code here ; could be optimized):
import torch
def get_points_from_low(x0, y0, x1, y1, num_points=3):
dx = x1 - x0
dy = y1 - y0
xi = torch.sign(dx)
yi = torch.sign(dy)
dy = dy * yi
D = 2 * dy - dx
y = y0
x = x0
points = []
for n in range(num_points):
x = x + xi
is_D_gt_0 = (D > 0).long()
y = y + is_D_gt_0 * yi
D = D + 2 * dy - is_D_gt_0 * 2 * dx
points.append(torch.stack((x, y), dim=-1))
return torch.stack(points, dim=len(x0.shape))
def get_points_from_high(x0, y0, x1, y1, num_points=3):
dx = x1 - x0
dy = y1 - y0
xi = torch.sign(dx)
yi = torch.sign(dy)
dx = dx * xi
D = 2 * dx - dy
y = y0
x = x0
points = []
for n in range(num_points):
y = y + yi
is_D_gt_0 = (D > 0).long()
x = x + is_D_gt_0 * xi
D = D + 2 * dx - is_D_gt_0 * 2 * dy
points.append(torch.stack((x, y), dim=-1))
return torch.stack(points, dim=len(x0.shape))
def get_points_from(x0, y0, x1, y1, num_points=3):
is_dy_lt_dx = (torch.abs(y1 - y0) < torch.abs(x1 - x0)).long()
is_x0_gt_x1 = (x0 > x1).long()
is_y0_gt_y1 = (y0 > y1).long()
sign = 1 - 2 * is_x0_gt_x1
x0_comp, x1_comp, y0_comp, y1_comp = x0 * sign, x1 * sign, y0 * sign, y1 * sign
points_low = get_points_from_low(x0_comp, y0_comp, x1_comp, y1_comp, num_points=num_points)
points_low *= sign.view(-1, 1, 1).expand_as(points_low)
sign = 1 - 2 * is_y0_gt_y1
x0_comp, x1_comp, y0_comp, y1_comp = x0 * sign, x1 * sign, y0 * sign, y1 * sign
points_high = get_points_from_high(x0_comp, y0_comp, x1_comp, y1_comp, num_points=num_points) * sign
points_high *= sign.view(-1, 1, 1).expand_as(points_high)
is_dy_lt_dx = is_dy_lt_dx.view(-1, 1, 1).expand(-1, num_points, 2)
points = points_low * is_dy_lt_dx + points_high * (1 - is_dy_lt_dx)
return points
# Inputs:
# (#todo: extend A to cover all points in maps):
A = torch.LongTensor([[0, 1], [8, 6]])
C = torch.LongTensor([[6, 4], [2, 3]])
num_points = 3
# Getting points between A and C:
# (#todo: what if there's less than `num_points` between A-C?)
Bs = get_points_from(A[:, 0], A[:, 1], C[:, 0], C[:, 1], num_points=num_points)
print(Bs)
# tensor([[[1, 1],
# [2, 2],
# [3, 2]],
# [[7, 6],
# [6, 5],
# [5, 5]]])
Once you have your points, you could retrieve their "values" (Value(A), Value(B1), etc.) using torch.index_select() (note that as of now, this method only accept 1D indices, so you need to unravel your data). All things put together, this would look like something such as the following (extending A from shape (Batch, 2) to (Batch, H, W, 2) is left for exercise...)
# Inputs:
# (#todo: extend A to cover all points in maps):
A = torch.LongTensor([[0, 1], [8, 6]])
C = torch.LongTensor([[6, 4], [2, 3]])
batch_size = A.shape[0]
num_points = 3
map_size = (9, 9)
map_num_elements = map_size[0] * map_size[1]
map_values = torch.stack((torch.arange(0, map_num_elements).view(*map_size),
torch.arange(0, -map_num_elements, -1).view(*map_size)))
# Getting points between A and C:
# (#todo: what if there's less than `num_points` between A-C?)
Bs = get_points_from(A[:, 0], A[:, 1], C[:, 0], C[:, 1], num_points=num_points)
# Get map values in positions A:
A_unravel = torch.arange(0, batch_size) * map_num_elements
A_unravel = A_unravel + A[:, 0] * map_size[1] + A[:, 1]
values_A = torch.index_select(map_values.view(-1), dim=0, index=A_unravel)
print(values_A)
# tensor([ 1, -4])
# Get map values in positions A:
A_unravel = torch.arange(0, batch_size) * map_num_elements
A_unravel = A_unravel + A[:, 0] * map_size[1] + A[:, 1]
values_A = torch.index_select(map_values.view(-1), dim=0, index=A_unravel)
print(values_A)
# tensor([ 1, -78])
# Get map values in positions B:
Bs_flatten = Bs.view(-1, 2)
Bs_unravel = (torch.arange(0, batch_size)
.unsqueeze(1)
.repeat(1, num_points)
.view(num_points * batch_size) * map_num_elements)
Bs_unravel = Bs_unravel + Bs_flatten[:, 0] * map_size[1] + Bs_flatten[:, 1]
values_B = torch.index_select(map_values.view(-1), dim=0, index=Bs_unravel)
values_B = values_B.view(batch_size, num_points)
print(values_B)
# tensor([[ 10, 20, 29],
# [-69, -59, -50]])
# Compute result:
res = torch.abs(values_A.unsqueeze(-1).expand_as(values_B) - values_B)
print(res)
# tensor([[ 9, 19, 28],
# [ 9, 19, 28]])
res = torch.sum(res, dim=1)
print(res)
# tensor([56, 56])
Using Python, I would like to draw a sphere in one color and on that sphere a number of spherical caps. The caps are given by their center points (the angles phi, theta) and radii.
Is this possible at all, perhaps with MayaVi?
Using matplotlib:
def plot_spherical_cap(ax, b, opening_angle, radius=1.0):
r = elevation
phi = numpy.linspace(0, 2 * numpy.pi, 30)
theta = numpy.linspace(0, opening_angle, 20)
X = r * numpy.stack([
numpy.outer(numpy.cos(phi), numpy.sin(theta)),
numpy.outer(numpy.sin(phi), numpy.sin(theta)),
numpy.outer(numpy.ones(numpy.size(phi)), numpy.cos(theta)),
], axis=-1)
# rotate X such that [0, 0, 1] gets rotated to `c`;
# <https://math.stackexchange.com/a/476311/36678>.
a = numpy.array([0.0, 0.0, 1.0])
a_x_b = numpy.cross(a, b)
a_dot_b = numpy.dot(a, b)
if a_dot_b == -1.0:
X_rot = -X
else:
X_rot = (
X +
numpy.cross(a_x_b, X) +
numpy.cross(a_x_b, numpy.cross(a_x_b, X)) / (1.0 + a_dot_b)
)
ax.plot_surface(
X_rot[..., 0], X_rot[..., 1], X_rot[..., 2],
rstride=3, cstride=3,
color='#1f77b4',
alpha=0.5,
linewidth=0
)
return
I am currently using OpenCV in Python to correct for image distortion in an aerial image. I have the data for the roll, pitch, and yaw. I understand that I need to create a warp matrix, and apply the matrix to my original coordinate points to create the output points for the image. I am able to affect the way the image moves, but I feel that there is an error because the only values that seem to work for very small values.
Here is my current code:
warp_mat = np.array([[math.cos(theta)*math.cos(psy), math.cos(phi)*math.sin(psy)+math.sin(phi)*math.sin(theta)*math.cos(psy), math.sin(phi)*math.sin(psy)-math.cos(phi)*math.sin(theta)*math.cos(psy)],\
[-1*math.cos(theta)*math.sin(psy), math.cos(phi)*math.cos(psy)-math.sin(phi)*math.sin(theta)*math.sin(psy), math.sin(phi)*math.cos(psy)+math.cos(phi)*math.sin(theta)*math.sin(psy)],\
[math.sin(theta), -1*math.sin(phi)*math.cos(theta), math.cos(phi)*math.cos(theta)]], dtype='float32')
srcPts = np.array([[-2064, 1161, 1],\
[2064, 1161, 1],\
[2064, -1161, 1],\
[-2064, -1161, 1]], dtype='float32')
dstPts = np.empty(shape = (4,3), dtype='float32')
dstPts[0][0] = srcPts[0][0] * warp_mat[0][0] + srcPts[0][1] * warp_mat[1][0] + srcPts[0][2] * warp_mat[2][0];
dstPts[0][1] = srcPts[0][0] * warp_mat[0][1] + srcPts[0][1] * warp_mat[1][1] + srcPts[0][2] * warp_mat[2][1];
dstPts[0][2] = srcPts[0][0] * warp_mat[0][2] + srcPts[0][1] * warp_mat[1][2] + srcPts[0][2] * warp_mat[2][2];
dstPts[1][0] = srcPts[1][0] * warp_mat[0][0] + srcPts[1][1] * warp_mat[1][0] + srcPts[1][2] * warp_mat[2][0];
dstPts[1][1] = srcPts[1][0] * warp_mat[0][1] + srcPts[1][1] * warp_mat[1][1] + srcPts[1][2] * warp_mat[2][1];
dstPts[1][2] = srcPts[1][0] * warp_mat[0][2] + srcPts[1][1] * warp_mat[1][2] + srcPts[1][2] * warp_mat[2][2];
dstPts[2][0] = srcPts[2][0] * warp_mat[0][0] + srcPts[2][1] * warp_mat[1][0] + srcPts[2][2] * warp_mat[2][0];
dstPts[2][1] = srcPts[2][0] * warp_mat[0][1] + srcPts[2][1] * warp_mat[1][1] + srcPts[2][2] * warp_mat[2][1];
dstPts[2][2] = srcPts[2][0] * warp_mat[0][2] + srcPts[2][1] * warp_mat[1][2] + srcPts[2][2] * warp_mat[2][2];
dstPts[3][0] = srcPts[3][0] * warp_mat[0][0] + srcPts[3][1] * warp_mat[1][0] + srcPts[3][2] * warp_mat[2][0];
dstPts[3][1] = srcPts[3][0] * warp_mat[0][1] + srcPts[3][1] * warp_mat[1][1] + srcPts[3][2] * warp_mat[2][1];
dstPts[3][2] = srcPts[3][0] * warp_mat[0][2] + srcPts[3][1] * warp_mat[1][2] + srcPts[3][2] * warp_mat[2][2];
dstPts[0][0] = dstPts[0][0] / dstPts[0][2];
dstPts[0][1] = dstPts[0][1] / dstPts[0][2];
dstPts[0][2] = dstPts[0][2] / dstPts[0][2];
dstPts[1][0] = dstPts[1][0] / dstPts[1][2];
dstPts[1][1] = dstPts[1][1] / dstPts[1][2];
dstPts[1][2] = dstPts[1][2] / dstPts[1][2];
dstPts[2][0] = dstPts[2][0] / dstPts[2][2];
dstPts[2][1] = dstPts[2][1] / dstPts[2][2];
dstPts[2][2] = dstPts[2][2] / dstPts[2][2];
dstPts[3][0] = dstPts[3][0] / dstPts[3][2];
dstPts[3][1] = dstPts[3][1] / dstPts[3][2];
dstPts[3][2] = dstPts[3][2] / dstPts[3][2];
srcPts2 = np.array([[srcPts[0][0],srcPts[0][1]],\
[srcPts[1][0],srcPts[1][1]],\
[srcPts[2][0],srcPts[2][1]],\
[srcPts[3][0],srcPts[3][1]]], dtype='float32')
dstPts2 = np.array([[dstPts[0][0],dstPts[0][1]],\
[dstPts[1][0],dstPts[1][1]],\
[dstPts[2][0],dstPts[2][1]],\
[dstPts[3][0],dstPts[3][1]]], dtype='float32')
transMatrix = cv.getPerspectiveTransform(srcPts2, dstPts2)
dst = cv.warpPerspective(imgFile,transMatrix,(4128,2322) ,borderMode = cv.BORDER_CONSTANT,borderValue = 0)
At the beginning of your code you are calculating the warp matrix by projecting four points and then use getPerspectiveTransform() to find out the transFormation matrix. This should work, but it is more complicated than necessary. If you know the roll, pitch and yaw angles, you can directly calculate the transformation matrix. Have a look at the function BirdsEyeView() in http://image2measure.net/files/calib3Dto2D.cpp . It does exactly that.
I had to change the line
Mat R = RX * RY * RZ;
to
Mat R = RZ * RX * RY;
in order to get it the transformation right.
f is the focal length in pixels.
If rh is the horizontal resolution of your image and oh the horizontal opening angle of the camera
f = (rh/2)/tan(oh/2)
Choose the same value for Distance if you don't want to scale the image, a bigger one than f to enlarge it or a smaller one to shrink it.
The code BirdsEyeView() works for me but I don't know why the Roll and Pitch angles are exchanged. When I change "alpha", the image is warped in pitch and when I change "beta" the image in warped in roll. So, I changed my rotation matrix, as can be seen below.
Also, the RY has a signal error. You can check Ry at: http://en.wikipedia.org/wiki/Rotation_matrix
I think this is the reason why Adrian changed the multiplication order from R = RX * RY * RZ to R = RZ * RX * RY.
The rotation metrix I use:
Mat RX = (Mat_<double>(4, 4) <<
1, 0, 0, 0,
0, cos(beta), -sin(beta), 0,
0, sin(beta), cos(beta), 0,
0, 0, 0, 1);
Mat RY = (Mat_<double>(4, 4) <<
cos(alpha), 0, sin(alpha), 0,
0, 1, 0, 0,
-sin(alpha), 0, cos(alpha), 0,
0, 0, 0, 1);
Mat RZ = (Mat_<double>(4, 4) <<
cos(gamma), -sin(gamma), 0, 0,
sin(gamma), cos(gamma), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
Regards