File size: 5,543 Bytes
0bd918c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552cab8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# -----------------------------------------------------------------------------
#
# This file is part of the PantoScanner distribution on: 
# https://huggingface.co/spaces/swissrail/PantoScanner
#
# PantoScanner - Analytics and measurement capability for technical objects.
# Copyright (C) 2017-2024 Schweizerische Bundesbahnen SBB
#
# Authors (C) 2024 L. Hofstetter (lukas.hofstetter@sbb.ch)
# Authors (C) 2017 U. Gehrig (urs.gehrig@sbb.ch)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# -----------------------------------------------------------------------------

import numpy as np
from numpy import linalg
import math


def obj_2_cam_coords(angles, distance, obj_coordinates, angle_order=('z', 'y', 'x')):
    d_x, d_y, d_z = distance  # distance from cam to object in camera coordinates
    x_obj, y_obj, z_obj = obj_coordinates
    d_cam2obj = np.asarray([d_x, d_y, d_z])
    p_obj = np.asarray([x_obj, y_obj, z_obj])
    rot_angles = {}
    for angle, axis in zip(angles, angle_order):
        rot_angles[axis] = angle

    rot_matrices = {'x': np.asarray([[1, 0, 0],
                                     [0, math.cos(rot_angles['x']), -math.sin(rot_angles['x'])],
                                     [0, math.sin(rot_angles['x']), math.cos(rot_angles['x'])]]),
                    'y': np.asarray([[math.cos(rot_angles['y']), 0, math.sin(rot_angles['y'])],
                                     [0, 1, 0],
                                     [-math.sin(rot_angles['y']), 0, math.cos(rot_angles['y'])]]),
                    'z': np.asarray([[math.cos(rot_angles['z']), -math.sin(rot_angles['z']), 0],
                                     [math.sin(rot_angles['z']), math.cos(rot_angles['z']), 0],
                                     [0, 0, 1]])}
    rot_matrix = np.eye(3)
    for axis in angle_order:
        rot_matrix = rot_matrix.dot(rot_matrices[axis])
    rot_vector = rot_matrix.dot(p_obj)
    p_cam = rot_vector + d_cam2obj
    return p_cam[0].item(), p_cam[1].item(), p_cam[2].item()


def line_surface_intersect(p_0_l, r_l, p_0_s, u_l, v_l):
    m_dir_matirx = np.asarray([[r_l[0], -u_l[0], -v_l[0]], [r_l[1], -u_l[1], -v_l[1]], [r_l[2], -u_l[2], -v_l[2]]], dtype='float')
    d_p_0 = np.asarray([p_0_s[0] - p_0_l[0], p_0_s[1] - p_0_l[1], p_0_s[2] - p_0_l[2]], dtype='float')
    inv_matrix = linalg.inv(m_dir_matirx)
    lin_sol = inv_matrix.dot(d_p_0)
    q = lin_sol[0].item()
    s = lin_sol[1].item()
    t = lin_sol[2].item()
    section_point = np.asarray([p_0_l[0], p_0_l[1], p_0_l[2]], dtype='float') + q * np.asarray([r_l[0], r_l[1], r_l[2]], dtype='float')
    return [(q, s, t), (section_point[0], section_point[1], section_point[2])]


def pixel2_physical(pix_coordinates, pixel_size, n_pix_x, n_pix_y):
    physical_coord_list = []
    if type(pix_coordinates) is not list:
        pix_coord_list = [pix_coordinates]
    else:
        pix_coord_list = pix_coordinates
    for x, y in pix_coord_list:
        x_p = - (x - 0.5 * n_pix_x) * pixel_size
        y_p = (y - 0.5 * n_pix_y) * pixel_size
        physical_coord_list.append((x_p, y_p))
    return physical_coord_list


def physical2_ray(phys_coordinates, focal_length):
    ray_direction_list = []
    if type(phys_coordinates) is not list:
        phys_coord_list = [phys_coordinates]
    else:
        phys_coord_list = phys_coordinates
    for x, y in phys_coord_list:
        length = (x**2 + y**2 + focal_length**2)**0.5
        x_new = x / length
        y_new = y / length
        z_new = focal_length / length
        ray_direction_list.append((x_new, y_new, z_new))
    return ray_direction_list


def pix2_object_surf(pix_coords, eul_angles, distance, focal_length, pixel_size, n_pix_x, n_pix_y,  p_0_surf, dir_1_surf, dir_2_surf, angle_order=('x', 'y', 'z')):  #xyz
    # p_0 is the vector pointing to the surface origin expressed in object coordinates
    # dir_surf are the directions of the two vector spanning the surface, expressed in object coordinates
    obj_surf_coords = []
    if type(pix_coords) is not list:
        pix_coord_list = [pix_coords]
    else:
        pix_coord_list = pix_coords
    physical_coords = pixel2_physical(pix_coord_list, pixel_size, n_pix_x, n_pix_y)
    ray_directions = physical2_ray(physical_coords, focal_length)
    ray_start_point = (0, 0, -focal_length)
    dir_1_surf_cam = obj_2_cam_coords(eul_angles, (0, 0, 0), dir_1_surf, angle_order=angle_order)
    dir_2_surf_cam = obj_2_cam_coords(eul_angles, (0, 0, 0), dir_2_surf, angle_order=angle_order)
    p_0_surf_cam = obj_2_cam_coords(eul_angles, distance, p_0_surf, angle_order=angle_order)

    for this_direction in ray_directions:
        result = line_surface_intersect(ray_start_point, this_direction, p_0_surf_cam, dir_1_surf_cam, dir_2_surf_cam)
        obj_surf_coords.append(result)
    return obj_surf_coords


#my_test = pix2_object_surf([(800, 1400)], [0, 0, 0], [0, 0, 6000], 75, 0.0045, 2200, 3208, [0, 0, 10], (1, 0, 0), (0, 1, 0))