ign-borea 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- borea/__init__.py +0 -0
- borea/datastruct/__init__.py +0 -0
- borea/datastruct/camera.py +25 -0
- borea/datastruct/dtm.py +119 -0
- borea/datastruct/gcp.py +22 -0
- borea/datastruct/shot.py +222 -0
- borea/datastruct/workdata.py +220 -0
- borea/format/__init__.py +0 -0
- borea/format/conl.py +143 -0
- borea/format/rpc.py +244 -0
- borea/geodesy/__init__.py +0 -0
- borea/geodesy/approx_euclidean_proj.py +91 -0
- borea/geodesy/euclidean_proj.py +25 -0
- borea/geodesy/local_euclidean_proj.py +127 -0
- borea/geodesy/proj_engine.py +70 -0
- borea/geodesy/projectionlist/__init__.py +0 -0
- borea/geodesy/projectionlist/search_proj.py +60 -0
- borea/geodesy/transform_geodesy.py +114 -0
- borea/process/__init__.py +0 -0
- borea/process/p_add_data/__init__.py +0 -0
- borea/process/p_add_data/p_add_shot.py +63 -0
- borea/process/p_add_data/p_file_gcp2d.py +55 -0
- borea/process/p_add_data/p_file_gcp3d.py +53 -0
- borea/process/p_add_data/p_gen_param.py +76 -0
- borea/process/p_add_data/p_pt2d.py +48 -0
- borea/process/p_add_data/p_pt3d.py +48 -0
- borea/process/p_add_data/p_unit_shot.py +48 -0
- borea/process/p_add_data/p_write.py +23 -0
- borea/process/p_format/__init__.py +0 -0
- borea/process/p_format/p_read_opk.py +78 -0
- borea/process/p_format/p_write_con.py +36 -0
- borea/process/p_format/p_write_opk.py +64 -0
- borea/process/p_format/p_write_rpc.py +48 -0
- borea/process/p_func/__init__.py +0 -0
- borea/process/p_func/p_control.py +67 -0
- borea/process/p_func/p_image_world.py +48 -0
- borea/process/p_func/p_spaceresection.py +51 -0
- borea/process/p_func/p_world_image.py +49 -0
- borea/reader/__init__.py +0 -0
- borea/reader/orientation/__init__.py +0 -0
- borea/reader/orientation/manage_reader.py +33 -0
- borea/reader/orientation/reader_opk.py +58 -0
- borea/reader/reader_camera.py +52 -0
- borea/reader/reader_point.py +113 -0
- borea/stat/__init__.py +0 -0
- borea/stat/statistics.py +215 -0
- borea/transform_world_image/__init__.py +0 -0
- borea/transform_world_image/transform_dtm/__init__.py +0 -0
- borea/transform_world_image/transform_dtm/world_image_dtm.py +47 -0
- borea/transform_world_image/transform_shot/__init__.py +0 -0
- borea/transform_world_image/transform_shot/conversion_coor_shot.py +58 -0
- borea/transform_world_image/transform_shot/image_world_shot.py +153 -0
- borea/transform_world_image/transform_shot/world_image_shot.py +117 -0
- borea/transform_world_image/transform_worksite/__init__.py +0 -0
- borea/transform_world_image/transform_worksite/image_world_intersection.py +154 -0
- borea/transform_world_image/transform_worksite/image_world_least_square.py +184 -0
- borea/transform_world_image/transform_worksite/image_world_work.py +49 -0
- borea/transform_world_image/transform_worksite/space_resection.py +343 -0
- borea/transform_world_image/transform_worksite/world_image_work.py +43 -0
- borea/utils/__init__.py +0 -0
- borea/utils/check/__init__.py +0 -0
- borea/utils/check/check_args_opk.py +59 -0
- borea/utils/check/check_args_reader_pt.py +44 -0
- borea/utils/check/check_array.py +56 -0
- borea/utils/check/check_header.py +90 -0
- borea/utils/check/check_order_axe.py +50 -0
- borea/utils/miscellaneous/__init__.py +0 -0
- borea/utils/miscellaneous/miscellaneous.py +83 -0
- borea/utils/miscellaneous/param_bundle.py +36 -0
- borea/utils/miscellaneous/sparse.py +31 -0
- borea/utils/singleton/__init__.py +0 -0
- borea/utils/singleton/singleton.py +23 -0
- borea/utils/xml/__init__.py +0 -0
- borea/utils/xml/xml.py +63 -0
- borea/worksite/__init__.py +0 -0
- borea/worksite/worksite.py +240 -0
- borea/writer/__init__.py +0 -0
- borea/writer/manage_writer.py +23 -0
- borea/writer/writer_con.py +29 -0
- borea/writer/writer_df_to_txt.py +32 -0
- borea/writer/writer_opk.py +70 -0
- borea/writer/writer_rpc.py +55 -0
- borea_tools/__init__.py +0 -0
- borea_tools/opk_control.py +33 -0
- borea_tools/opk_to_conl.py +33 -0
- borea_tools/opk_to_opk.py +33 -0
- borea_tools/opk_to_rpc.py +33 -0
- borea_tools/pt_image_to_world.py +32 -0
- borea_tools/pt_world_to_image.py +32 -0
- borea_tools/ptfile_image_to_world.py +32 -0
- borea_tools/ptfile_world_to_image.py +32 -0
- borea_tools/spaceresection_opk.py +34 -0
- ign_borea-0.1.5.dist-info/LICENSE +21 -0
- ign_borea-0.1.5.dist-info/METADATA +274 -0
- ign_borea-0.1.5.dist-info/RECORD +98 -0
- ign_borea-0.1.5.dist-info/WHEEL +5 -0
- ign_borea-0.1.5.dist-info/entry_points.txt +10 -0
- ign_borea-0.1.5.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for recalculate shooting position
|
|
3
|
+
"""
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy.spatial.transform import Rotation as R
|
|
7
|
+
from borea.utils.miscellaneous.miscellaneous import angle_degree_2vect, min_max_pt, normalize
|
|
8
|
+
from borea.worksite.worksite import Worksite
|
|
9
|
+
from borea.datastruct.camera import Camera
|
|
10
|
+
from borea.datastruct.shot import Shot
|
|
11
|
+
from borea.transform_world_image.transform_shot.world_image_shot import WorldImageShot
|
|
12
|
+
from borea.transform_world_image.transform_shot.image_world_shot import ImageWorldShot
|
|
13
|
+
# pylint: disable-next=line-too-long
|
|
14
|
+
from borea.transform_world_image.transform_worksite.image_world_intersection import WorldIntersection # noqa: E501
|
|
15
|
+
from borea.utils.miscellaneous.param_bundle import set_param_bundle_diff
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SpaceResection:
|
|
19
|
+
"""
|
|
20
|
+
Recalculates the shot's 6 external orientation parameters,
|
|
21
|
+
the 3 angles omega, phi, kappa and its position x, y, z.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
work (Woksite): Worksite to make space resection
|
|
25
|
+
"""
|
|
26
|
+
def __init__(self, work: Worksite) -> None:
|
|
27
|
+
self.work = work
|
|
28
|
+
|
|
29
|
+
def space_resection_to_worksite(self, pt2d: pd.DataFrame, pt3d: pd.DataFrame,
|
|
30
|
+
pinit: dict) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Calculates the shot's 6 external orientation parameters,
|
|
33
|
+
the 3 angles omega, phi, kappa and its position x, y, z.
|
|
34
|
+
For all shot with a variation pixel.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
pt2d (pd.Dataframe): Table of image coordinate point with image.
|
|
38
|
+
pt3d (pd.Dataframe): Table of ground coordinate point.
|
|
39
|
+
pinit (dict): Dictionnary with 3d coordinate for initialization "coor_init".
|
|
40
|
+
"""
|
|
41
|
+
for name_shot, group in pt2d.groupby("id_shot"):
|
|
42
|
+
if group.shape[0] < 3:
|
|
43
|
+
print(f"Shot {name_shot} has fewer than three points,"
|
|
44
|
+
" so it cannot calculate its position and orientation.")
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
dfm = group.merge(pt3d, how="inner", on="id_pt")
|
|
48
|
+
obs_img = np.array([dfm["column"].to_numpy(), dfm["line"].to_numpy()])
|
|
49
|
+
obs_world = np.array([dfm["x"].to_numpy(), dfm["y"].to_numpy(), dfm["z"].to_numpy()])
|
|
50
|
+
|
|
51
|
+
pinit["kappa"] = self.init_kappa(dfm)
|
|
52
|
+
self.work.shots[name_shot] = self.space_resection_shot(name_shot, obs_img,
|
|
53
|
+
obs_world, pinit)
|
|
54
|
+
self.work.set_param_shot(self.work.approxeucli)
|
|
55
|
+
|
|
56
|
+
if pt3d["type"].iloc[0]:
|
|
57
|
+
type_pt = "gcp2d"
|
|
58
|
+
else:
|
|
59
|
+
type_pt = "co_points"
|
|
60
|
+
self.work.set_point_image_dataframe(pt2d, type_pt)
|
|
61
|
+
self.work.set_point_world_dataframe(pt3d, type_pt)
|
|
62
|
+
|
|
63
|
+
def space_resection_on_worksite(self, add_pixel: tuple = (0, 0)) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Recalculates the shot's 6 external orientation parameters,
|
|
66
|
+
the 3 angles omega, phi, kappa and its position x, y, z.
|
|
67
|
+
For all shot with a variation pixel.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
add_pixel (tuple): Factor (column, line) added on observable point.
|
|
71
|
+
"""
|
|
72
|
+
for key_shot, item_shot in self.work.shots.items():
|
|
73
|
+
self.work.shots[key_shot] = self.space_resection_gap(item_shot, add_pixel)
|
|
74
|
+
|
|
75
|
+
def space_resection_shot(self, name_shot: str, pt_img: np.ndarray,
|
|
76
|
+
pt_world: np.ndarray, pinit: dict) -> Shot:
|
|
77
|
+
"""
|
|
78
|
+
Calculates the shot's 6 external orientation parameters,
|
|
79
|
+
the 3 angles omega, phi, kappa and its position x, y, z.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
name_shot (str): Name of shot to calculte externa parameters.
|
|
83
|
+
pt_img (np.array): Coordonnees image of points.
|
|
84
|
+
pt_world (np.array): Coordonnees world of points.
|
|
85
|
+
pinit (dict): Dictionnary with 3d coordinate for initialization and kappa.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Shot: Adjusted shot.
|
|
89
|
+
"""
|
|
90
|
+
# Initialization of adjusted shot
|
|
91
|
+
cam = self.work.cameras[list(self.work.cameras.keys())[0]]
|
|
92
|
+
shot_adjust = Shot(name_shot, pinit["coor_init"], np.array([0, 0, pinit["kappa"]]),
|
|
93
|
+
cam.name_camera, "degree", False, "opk")
|
|
94
|
+
shot_adjust.set_param_eucli_shot(self.work.approxeucli)
|
|
95
|
+
z_nadir = ImageWorldShot(shot_adjust, cam).image_to_world(np.array([cam.ppax, cam.ppay]),
|
|
96
|
+
self.work.type_z_shot,
|
|
97
|
+
self.work.type_z_shot, False)[2]
|
|
98
|
+
shot_adjust.set_z_nadir(z_nadir)
|
|
99
|
+
|
|
100
|
+
# Calculate euclidean position
|
|
101
|
+
pt_eucli = shot_adjust.projeucli.world_to_eucli(pt_world)
|
|
102
|
+
|
|
103
|
+
# Least-square methode
|
|
104
|
+
shot_adjust = self.least_square_shot(shot_adjust, pt_img, pt_eucli, pt_world)
|
|
105
|
+
|
|
106
|
+
return shot_adjust
|
|
107
|
+
|
|
108
|
+
def init_kappa(self, dfpt: pd.DataFrame) -> float:
|
|
109
|
+
"""
|
|
110
|
+
Calculates a kappa angle for acquisition initialization.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
dfpt (pd.DataFrame): Coordonnees of points in image and world.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
float: kappa angle.
|
|
117
|
+
"""
|
|
118
|
+
pt_img = np.array([dfpt["column"].to_numpy(), dfpt["line"].to_numpy()])
|
|
119
|
+
pt_mini, arg_min = min_max_pt(pt_img, "min")
|
|
120
|
+
pt_maxi, arg_max = min_max_pt(pt_img, "max")
|
|
121
|
+
pt_minw = dfpt.iloc[arg_min].get(["x", "y"]).to_numpy()
|
|
122
|
+
pt_maxw = dfpt.iloc[arg_max].get(["x", "y"]).to_numpy()
|
|
123
|
+
vect_i = pt_maxi - pt_mini
|
|
124
|
+
vect_w = pt_maxw - pt_minw
|
|
125
|
+
norm_vi = normalize(vect_i)[0]
|
|
126
|
+
norm_vw = normalize(vect_w)[0]
|
|
127
|
+
kappa = angle_degree_2vect(norm_vi, norm_vw)
|
|
128
|
+
return np.floor(kappa)
|
|
129
|
+
|
|
130
|
+
def space_resection_gap(self, shot: Shot, add_pixel: tuple = (0, 0)) -> Shot:
|
|
131
|
+
"""
|
|
132
|
+
Recalculates the shot's 6 external orientation parameters,
|
|
133
|
+
the 3 angles omega, phi, kappa and its position x, y, z.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
shot (Shot): Shot to recalculte externa parameters.
|
|
137
|
+
add_pixel (tuble): Pixel to be added to change marker.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Shot: Adjusted shot.
|
|
141
|
+
"""
|
|
142
|
+
# Initialization observation point
|
|
143
|
+
obs, pt_world = self.take_obs(shot)
|
|
144
|
+
|
|
145
|
+
# Calculate euclidean position
|
|
146
|
+
pt_eucli = shot.projeucli.world_to_eucli(pt_world)
|
|
147
|
+
|
|
148
|
+
# Add factor
|
|
149
|
+
obs[0] += add_pixel[0]
|
|
150
|
+
obs[1] += add_pixel[1]
|
|
151
|
+
|
|
152
|
+
# Initialization of adjusted shot
|
|
153
|
+
shot_adjust = Shot(shot.name_shot, shot.pos_shot, shot.ori_shot,
|
|
154
|
+
shot.name_cam, shot.unit_angle, shot.linear_alteration, shot.order_axe)
|
|
155
|
+
shot_adjust.set_param_eucli_shot(shot.approxeucli)
|
|
156
|
+
shot_adjust.set_z_nadir(shot.z_nadir)
|
|
157
|
+
|
|
158
|
+
# Least-square methode
|
|
159
|
+
shot_adjust = self.least_square_shot(shot_adjust, obs, pt_eucli, pt_world)
|
|
160
|
+
|
|
161
|
+
shot_adjust.co_points = shot.co_points
|
|
162
|
+
shot_adjust.gcp2d = shot.gcp2d
|
|
163
|
+
shot_adjust.gcp3d = shot.gcp3d
|
|
164
|
+
|
|
165
|
+
return shot_adjust
|
|
166
|
+
|
|
167
|
+
def least_square_shot(self, shot_adjust: Shot, obs: np.ndarray,
|
|
168
|
+
pt_eucli: np.ndarray, pt_world: np.ndarray) -> Shot:
|
|
169
|
+
"""
|
|
170
|
+
Least-square methode to calcule the shot's 6 external orientation parameters.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
shot_adjust (Shot): The shot to adjust.
|
|
174
|
+
obs (np.array): Observation of point in image [c_obs, l_obs].
|
|
175
|
+
pt_eucli (np.array): Observation of world point in euclidean system [X, Y, Z].
|
|
176
|
+
pt_world (np.array): Observation of world point [X, Y, Z].
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Shot: Adjusted shot.
|
|
180
|
+
"""
|
|
181
|
+
bool_iter = True
|
|
182
|
+
count_iter = 0
|
|
183
|
+
while bool_iter:
|
|
184
|
+
count_iter += 1
|
|
185
|
+
|
|
186
|
+
dx = self.func_least_square(shot_adjust, obs, pt_eucli, pt_world)
|
|
187
|
+
|
|
188
|
+
# Calculate new x = x0 + dx for position and rotation matrix
|
|
189
|
+
new_pos_eucli = np.array([shot_adjust.pos_shot[0] + dx[0],
|
|
190
|
+
shot_adjust.pos_shot[1] + dx[1],
|
|
191
|
+
shot_adjust.pos_shot[2] + dx[2]])
|
|
192
|
+
new_mat_eucli = shot_adjust.mat_rot_eucli @ R.from_rotvec(dx[3:]).as_matrix()
|
|
193
|
+
|
|
194
|
+
# Creation of new shot with new parameter
|
|
195
|
+
imc_new_adjust = Shot.from_param_euclidean(shot_adjust.name_shot, new_pos_eucli,
|
|
196
|
+
new_mat_eucli, shot_adjust.name_cam,
|
|
197
|
+
shot_adjust.unit_angle,
|
|
198
|
+
shot_adjust.linear_alteration,
|
|
199
|
+
shot_adjust.order_axe,
|
|
200
|
+
shot_adjust.approxeucli)
|
|
201
|
+
imc_new_adjust.set_z_nadir(shot_adjust.z_nadir)
|
|
202
|
+
|
|
203
|
+
# Look difference to know if you want to stop the calculation
|
|
204
|
+
diff_coord = np.array([imc_new_adjust.pos_shot]) - np.array([shot_adjust.pos_shot])
|
|
205
|
+
diff_opk = np.array([imc_new_adjust.ori_shot]) - np.array([shot_adjust.ori_shot])
|
|
206
|
+
|
|
207
|
+
if (np.all(diff_coord < 10 ** -3) and np.all(diff_opk < 10 ** -6)) or count_iter > 100:
|
|
208
|
+
# print(count_iter)
|
|
209
|
+
bool_iter = False
|
|
210
|
+
|
|
211
|
+
# Replace adjusted place
|
|
212
|
+
shot_adjust = imc_new_adjust
|
|
213
|
+
|
|
214
|
+
return shot_adjust
|
|
215
|
+
|
|
216
|
+
def func_least_square(self, shot_adjust: Shot, obs: np.ndarray,
|
|
217
|
+
pt_eucli: np.ndarray, pt_world: np.ndarray) -> np.ndarray:
|
|
218
|
+
"""
|
|
219
|
+
Calculate the least-squares equation dx = (A.T @ A)**-1 @ A.T @ B
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
shot_adjust (Shot): The shot to adjust.
|
|
223
|
+
obs (np.array): Observation of point in image [c_obs, l_obs].
|
|
224
|
+
pt_eucli (np.array): Observation of world point in euclidean system [X, Y, Z].
|
|
225
|
+
pt_world (np.array): Observation of world point [X, Y, Z].
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
np.array: dx.
|
|
229
|
+
"""
|
|
230
|
+
cam = self.work.cameras[shot_adjust.name_cam]
|
|
231
|
+
# Calculate position column and line with new shot f(x0)
|
|
232
|
+
f0 = WorldImageShot(shot_adjust,
|
|
233
|
+
cam
|
|
234
|
+
).world_to_image(pt_world,
|
|
235
|
+
self.work.type_z_data,
|
|
236
|
+
self.work.type_z_shot)
|
|
237
|
+
|
|
238
|
+
# Calculate residual vector B
|
|
239
|
+
v_res = np.c_[obs[0] - f0[0], obs[1] - f0[1]].reshape(2 * len(pt_eucli[0]))
|
|
240
|
+
|
|
241
|
+
# Creation of A with mat_obs_axia
|
|
242
|
+
# Calculate dx = (A.T @ A)**-1 @ A.T @ B
|
|
243
|
+
return np.squeeze(np.linalg.lstsq(self.mat_obs_axia(pt_eucli, shot_adjust),
|
|
244
|
+
v_res, rcond=None)[0])
|
|
245
|
+
|
|
246
|
+
def mat_obs_axia(self, pt_eucli: np.ndarray, imc_adjust: Shot) -> np.ndarray:
|
|
247
|
+
"""
|
|
248
|
+
Setting up the mat_a matrix to solve the system by axiator.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
pt_eucli (np.array): Coordinate [X, Y, Z] euclidean.
|
|
252
|
+
imc_adjust (Shot): adjusted shot.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
np.array: Matrix A.
|
|
256
|
+
"""
|
|
257
|
+
vect_a, vect_u, mat_v = set_param_bundle_diff(imc_adjust, pt_eucli)
|
|
258
|
+
|
|
259
|
+
# Axiator of vect_a
|
|
260
|
+
a_axiator = np.zeros((3 * len(vect_a[0]), 3))
|
|
261
|
+
a_axiator[0::3, 1] = -vect_a[2]
|
|
262
|
+
a_axiator[0::3, 2] = vect_a[1]
|
|
263
|
+
a_axiator[1::3, 0] = vect_a[2]
|
|
264
|
+
a_axiator[1::3, 2] = -vect_a[0]
|
|
265
|
+
a_axiator[2::3, 0] = -vect_a[1]
|
|
266
|
+
a_axiator[2::3, 1] = vect_a[0]
|
|
267
|
+
|
|
268
|
+
cam = self.work.cameras[imc_adjust.name_cam]
|
|
269
|
+
mat_a = -np.tile(np.repeat(cam.focal / vect_u[2] ** 2, 2), (6, 1)).T
|
|
270
|
+
mat_a[:, :3] *= (mat_v @ imc_adjust.mat_rot_eucli)
|
|
271
|
+
|
|
272
|
+
mat_a[:, 3:] *= np.einsum('lij, ljk->lik',
|
|
273
|
+
(mat_v @ imc_adjust.mat_rot_eucli).reshape(-1, 2, 3),
|
|
274
|
+
# pylint: disable-next=too-many-function-args
|
|
275
|
+
a_axiator.reshape(-1, 3, 3)).reshape(-1, 3)
|
|
276
|
+
|
|
277
|
+
return mat_a
|
|
278
|
+
|
|
279
|
+
def take_obs(self, shot: Shot) -> tuple:
|
|
280
|
+
"""
|
|
281
|
+
Check co point on the shot to use in observation.
|
|
282
|
+
If there aren't enough points, add 20 random observation points.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
shot (Shot): The shot to adjust.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
tuple: np.array(obs_image), np.array(pt_world).
|
|
289
|
+
"""
|
|
290
|
+
add_pt = True
|
|
291
|
+
pt_world_random = 0
|
|
292
|
+
if shot.co_points:
|
|
293
|
+
if not self.work.co_pts_world:
|
|
294
|
+
WorldIntersection(self.work).calculate_image_world_by_intersection("co_points")
|
|
295
|
+
obs, pt_world = self.work.get_coor_pt_img_and_world(shot.name_shot, "co_points")
|
|
296
|
+
if obs.shape[1] >= 7:
|
|
297
|
+
add_pt = False
|
|
298
|
+
|
|
299
|
+
if add_pt:
|
|
300
|
+
cam = self.work.cameras[shot.name_cam]
|
|
301
|
+
# Initialization of 20 points for shooting position
|
|
302
|
+
obs_random, z_world = self.seed_20_point(cam)
|
|
303
|
+
# Calculate world position
|
|
304
|
+
x_world, y_world, _ = ImageWorldShot(shot,
|
|
305
|
+
cam
|
|
306
|
+
).image_z_to_world(obs_random,
|
|
307
|
+
self.work.type_z_shot,
|
|
308
|
+
z_world)
|
|
309
|
+
pt_world_random = np.array([x_world, y_world, z_world])
|
|
310
|
+
|
|
311
|
+
if add_pt and shot.co_points:
|
|
312
|
+
obs = np.concatenate((obs, obs_random), axis=1)
|
|
313
|
+
pt_world[2] += 350 # 350 is the mean of z_obs in seed_20_point
|
|
314
|
+
pt_world = np.concatenate((pt_world, pt_world_random), axis=1)
|
|
315
|
+
add_pt = False
|
|
316
|
+
|
|
317
|
+
if add_pt:
|
|
318
|
+
obs = obs_random
|
|
319
|
+
pt_world = pt_world_random
|
|
320
|
+
|
|
321
|
+
return obs, pt_world
|
|
322
|
+
|
|
323
|
+
def seed_20_point(self, cam: Camera) -> tuple:
|
|
324
|
+
"""
|
|
325
|
+
Positioning of 20 points on an image by percentage position on width and height.
|
|
326
|
+
The z-world position is given by fixed values.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
cam (Camera): Camera of the shot.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
np.array: Tuple of 3 elements (position column obs, position line obs, z world).
|
|
333
|
+
"""
|
|
334
|
+
pourcent_x = np.array([6.25, 12.5, 18.75, 25, 31.25, 37.5, 37.5, 43.75, 50, 56.25,
|
|
335
|
+
62.5, 62.5, 68.75, 68.75, 75, 81.25, 87.5, 87.5, 93.75, 93.75]) / 100
|
|
336
|
+
pourcent_y = np.array([80, 20, 60, 90, 30, 10, 50, 70, 20, 40,
|
|
337
|
+
60, 90, 20, 80, 50, 20, 10, 70, 40, 90]) / 100
|
|
338
|
+
z_obs = np.array([200, 320, 250, 240, 330, 335, 340, 330, 350, 360,
|
|
339
|
+
360, 350, 370, 355, 380, 400, 450, 400, 500, 300])
|
|
340
|
+
c_obs = pourcent_x * cam.width
|
|
341
|
+
l_obs = pourcent_y * cam.height
|
|
342
|
+
|
|
343
|
+
return np.array([c_obs, l_obs]), z_obs
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
World image transformation module for Worksite
|
|
3
|
+
"""
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from borea.worksite.worksite import Worksite
|
|
6
|
+
from borea.transform_world_image.transform_shot.world_image_shot import WorldImageShot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class WorldImageWork:
|
|
11
|
+
"""
|
|
12
|
+
Class to calculate world coordinate to image coordinate in worksite.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name (str): Name of the worksite.
|
|
16
|
+
"""
|
|
17
|
+
work: Worksite
|
|
18
|
+
|
|
19
|
+
def calculate_world_to_image(self, lcode: list) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Calculates the position of gcp3d which corresponds to the data code
|
|
22
|
+
in the images they appear in.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
lcode (list): gcp code.
|
|
26
|
+
"""
|
|
27
|
+
if self.work.gcp3d and self.work.gcp2d:
|
|
28
|
+
for name_gcp, gcp in self.work.gcp3d.items():
|
|
29
|
+
if gcp.code in lcode or lcode == []:
|
|
30
|
+
try:
|
|
31
|
+
list_shots = self.work.gcp2d[name_gcp]
|
|
32
|
+
for name_shot in list_shots:
|
|
33
|
+
shot = self.work.shots[name_shot]
|
|
34
|
+
cam = self.work.cameras[shot.name_cam]
|
|
35
|
+
coor_img = WorldImageShot(shot,
|
|
36
|
+
cam).world_to_image(gcp.coor,
|
|
37
|
+
self.work.type_z_data,
|
|
38
|
+
self.work.type_z_shot)
|
|
39
|
+
self.work.shots[name_shot].gcp3d[name_gcp] = coor_img
|
|
40
|
+
except KeyError:
|
|
41
|
+
print(f"Warning: id point {name_gcp} is present "
|
|
42
|
+
"in gcp3d but not in gcp2d.")
|
|
43
|
+
continue
|
borea/utils/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A script for verification header str in manage reader.
|
|
3
|
+
"""
|
|
4
|
+
from borea.utils.check.check_header import check_head, check_h_z, get_type_z_and_header
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_args_opk(args: dict) -> tuple:
|
|
8
|
+
"""
|
|
9
|
+
Check args for reading an oopk.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
args (dict): Information for reading an opk file.
|
|
13
|
+
keys:
|
|
14
|
+
"interval" (list): Interval of lines taken into account,
|
|
15
|
+
[i, j] if i or j is None = :. e.g. [1, None] = [1:].
|
|
16
|
+
"header" (list): List of column type file.
|
|
17
|
+
"unit_angle" (str): Unit of angle 'degrees' or 'radian'.
|
|
18
|
+
"linear_alteration" (bool): True if data corrected by linear alteration.
|
|
19
|
+
|
|
20
|
+
Return:
|
|
21
|
+
tuple: args, header and type of z shot.
|
|
22
|
+
"""
|
|
23
|
+
if args["unit_angle"] not in ["degree", "radian"]:
|
|
24
|
+
raise ValueError(f"Unit angles is incorrect {args['unit_angle']},"
|
|
25
|
+
"correct writing is degree or radian.")
|
|
26
|
+
|
|
27
|
+
if args["interval"][0] is not None and args["interval"][0] > 0:
|
|
28
|
+
args["interval"][0] -= 1
|
|
29
|
+
if args["interval"][1] is not None:
|
|
30
|
+
args["interval"][1] -= 1
|
|
31
|
+
|
|
32
|
+
header, type_z = check_header_file(args["header"])
|
|
33
|
+
|
|
34
|
+
return args, header, type_z
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def check_header_file(header: list) -> tuple:
|
|
38
|
+
"""
|
|
39
|
+
Check if the header of the file is good.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
header (list): List of column type file.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
tuple: Header without type, type of z, type of angle.
|
|
46
|
+
"""
|
|
47
|
+
list_letter = ['S', 'N', 'X', 'Y', 'Z', 'H', 'O', 'P', 'K', 'C']
|
|
48
|
+
|
|
49
|
+
bad_head, ms_error_letter, head, symbol = check_head(header, list_letter)
|
|
50
|
+
|
|
51
|
+
misss = set(list_letter[1:]) - symbol
|
|
52
|
+
bad_head, ms_error_letter = check_h_z(bad_head, misss, ms_error_letter)
|
|
53
|
+
|
|
54
|
+
ms_error = "Your header is not correct.\n"
|
|
55
|
+
ms_error += ms_error_letter
|
|
56
|
+
if bad_head:
|
|
57
|
+
raise ValueError(ms_error)
|
|
58
|
+
|
|
59
|
+
return get_type_z_and_header(head)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A script for verification args to read a point file.
|
|
3
|
+
"""
|
|
4
|
+
from borea.utils.check.check_header import check_head, check_h_z, get_type_z_and_header
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_header_file(header: list, type_pt: str) -> tuple:
|
|
8
|
+
"""
|
|
9
|
+
Check if the header of the file is good.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
header (list): List of column type file.
|
|
13
|
+
type_pt (str): Type of point is reading (co_point, gcp2d, gcp3d).
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
tuple: header, type_z
|
|
17
|
+
"""
|
|
18
|
+
type_z = None
|
|
19
|
+
list_letter = ['S', 'N', 'X', 'Y', 'Z', 'H', 'P', 'T']
|
|
20
|
+
|
|
21
|
+
bad_head, ms_error_letter, _, symbol = check_head(header, list_letter)
|
|
22
|
+
|
|
23
|
+
if type_pt in ["gcp3d", "pt3d"]:
|
|
24
|
+
ll_type = ['P', 'T', 'X', 'Y', 'Z', 'H']
|
|
25
|
+
misss = set(ll_type) - symbol
|
|
26
|
+
if misss != set() and misss != set("T"):
|
|
27
|
+
if "T" in misss:
|
|
28
|
+
misss.remove("T")
|
|
29
|
+
bad_head, ms_error_letter = check_h_z(bad_head, misss, ms_error_letter)
|
|
30
|
+
if not bad_head:
|
|
31
|
+
header, type_z = get_type_z_and_header(header)
|
|
32
|
+
else:
|
|
33
|
+
ll_type = ['P', 'N', 'X', 'Y']
|
|
34
|
+
misss = set(ll_type) - symbol
|
|
35
|
+
if misss != set():
|
|
36
|
+
bad_head = True
|
|
37
|
+
ms_error_letter += f"The letters {misss} are missing.\n"
|
|
38
|
+
|
|
39
|
+
ms_error = "Your header is not correct.\n"
|
|
40
|
+
ms_error += ms_error_letter
|
|
41
|
+
if bad_head:
|
|
42
|
+
raise ValueError(ms_error)
|
|
43
|
+
|
|
44
|
+
return header, type_z
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A script combining various data conversion and format verification functions.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Union
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def check_array_transfo(x: Union[np.ndarray, float],
|
|
9
|
+
y: Union[np.ndarray, float],
|
|
10
|
+
z: Union[np.ndarray, float] = None) -> tuple:
|
|
11
|
+
"""
|
|
12
|
+
Checks the dimension of vectors if dim = 1 outputs the element in a variable.
|
|
13
|
+
x,y,z are in the same dimension and they must be vectors if array
|
|
14
|
+
possible dimension if array : (1,), (n,), (n,1) or (1,n).
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
x (Union[np.array, float]): Vector x.
|
|
18
|
+
y (Union[np.array, float]): Vector y.
|
|
19
|
+
z (Union[np.array, float]): Vector z.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
tuple: x, y, z.
|
|
23
|
+
"""
|
|
24
|
+
if isinstance(x, np.ndarray): # look if is an array
|
|
25
|
+
t = np.shape(x)
|
|
26
|
+
if len(t) == 1: # look dim == (n,)
|
|
27
|
+
if t[0] == 1: # look n == 1
|
|
28
|
+
# takes out the elements
|
|
29
|
+
x = float(x[0])
|
|
30
|
+
y = float(y[0])
|
|
31
|
+
if z is not None:
|
|
32
|
+
z = float(z[0])
|
|
33
|
+
|
|
34
|
+
if len(t) == 2: # look dim == (n,m)
|
|
35
|
+
if t[0] != 1: # look n != 1
|
|
36
|
+
# conversion dim (n,1) to (1,n)
|
|
37
|
+
x = x.T
|
|
38
|
+
y = y.T
|
|
39
|
+
if z is not None:
|
|
40
|
+
z = z.T
|
|
41
|
+
# conversion dim (1,n) to (n,)
|
|
42
|
+
x = np.squeeze(x, axis=0)
|
|
43
|
+
y = np.squeeze(y, axis=0)
|
|
44
|
+
if z is not None:
|
|
45
|
+
z = np.squeeze(z, axis=0)
|
|
46
|
+
|
|
47
|
+
# same checkup to the top
|
|
48
|
+
t2 = np.shape(x)
|
|
49
|
+
if len(t2) == 1:
|
|
50
|
+
if t2[0] == 1:
|
|
51
|
+
x = float(x[0])
|
|
52
|
+
y = float(y[0])
|
|
53
|
+
if z is not None:
|
|
54
|
+
z = float(z[0])
|
|
55
|
+
|
|
56
|
+
return x, y, z
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A script for verification header list str.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def check_head(header: list, check_letter: list) -> tuple:
|
|
7
|
+
"""
|
|
8
|
+
Checks that the header contains all the letters in the check letter.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
header (list): List of column type file.
|
|
12
|
+
check_letter (list): List of good letter in header.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
tuple: bad_head, ms_error, head, symbol
|
|
16
|
+
"""
|
|
17
|
+
bad_head = False
|
|
18
|
+
ms_error_letter = ""
|
|
19
|
+
symbol = set()
|
|
20
|
+
head = []
|
|
21
|
+
|
|
22
|
+
for l_type in header:
|
|
23
|
+
if l_type in check_letter:
|
|
24
|
+
if l_type not in head or l_type == "S":
|
|
25
|
+
head.append(l_type)
|
|
26
|
+
if l_type != "S":
|
|
27
|
+
symbol.add(l_type)
|
|
28
|
+
else:
|
|
29
|
+
bad_head = True
|
|
30
|
+
ms_error_letter += f"Symbol {l_type} appears several times, "
|
|
31
|
+
ms_error_letter += "this is incorrect, must appear only once\n"
|
|
32
|
+
else:
|
|
33
|
+
bad_head = True
|
|
34
|
+
ms_error_letter += f"Symbol {l_type} is not recognized, "
|
|
35
|
+
ms_error_letter += f"list of symbol recognized {check_letter}\n"
|
|
36
|
+
|
|
37
|
+
return bad_head, ms_error_letter, head, symbol
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def check_h_z(bad_head: bool, misss: set, ms_error_letter: str) -> tuple:
|
|
41
|
+
"""
|
|
42
|
+
Check letter H and Z in header.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
bad_head (bool): boolean if the header is false.
|
|
46
|
+
misss (set): Diff between list_letter and symbol.
|
|
47
|
+
ms_error_letter (str): Error message.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
tuple: bad_head, ms_error_letter
|
|
51
|
+
"""
|
|
52
|
+
if misss != {"Z"} and misss != {"H"} and misss != set():
|
|
53
|
+
bad_head = True
|
|
54
|
+
if "Z" in misss and "H" in misss:
|
|
55
|
+
miss = misss.copy()
|
|
56
|
+
miss.remove("Z")
|
|
57
|
+
miss.remove("H")
|
|
58
|
+
ms_error_letter += f"The letters 'Z' or 'H' are missing and lettres {miss}.\n"
|
|
59
|
+
elif "Z" in misss:
|
|
60
|
+
misss.remove("Z")
|
|
61
|
+
ms_error_letter += f"The letters {misss} are missing.\n"
|
|
62
|
+
else:
|
|
63
|
+
misss.remove("H")
|
|
64
|
+
ms_error_letter += f"The letters {misss} are missing.\n"
|
|
65
|
+
|
|
66
|
+
if misss == set():
|
|
67
|
+
bad_head = True
|
|
68
|
+
ms_error_letter += "The letters Z and H cannot be in the same string.\n"
|
|
69
|
+
|
|
70
|
+
return bad_head, ms_error_letter
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_type_z_and_header(header: list) -> tuple:
|
|
74
|
+
"""
|
|
75
|
+
Return type of z, height if H and altitude if Z
|
|
76
|
+
and header with the H replaced by a Z.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
header (list): List of column type file.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
tuple: Header and type_z.
|
|
83
|
+
"""
|
|
84
|
+
if "H" in header:
|
|
85
|
+
type_z = "height"
|
|
86
|
+
header[header.index('H')] = "Z"
|
|
87
|
+
else:
|
|
88
|
+
type_z = "altitude"
|
|
89
|
+
|
|
90
|
+
return header, type_z
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Script to check order axe and return axe for scipy
|
|
3
|
+
"""
|
|
4
|
+
from borea.utils.check.check_header import check_head
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_order_axe(order_axe: str) -> str:
|
|
8
|
+
"""
|
|
9
|
+
Check if code is good and convert for scipy rotation.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
order_axe (str): Order of rotation matrix axes.
|
|
13
|
+
|
|
14
|
+
Retuns:
|
|
15
|
+
str: Order of ratation matrix axe for scipy.
|
|
16
|
+
"""
|
|
17
|
+
list_val = ['o', 'p', 'k']
|
|
18
|
+
|
|
19
|
+
bad_head, ms_error_letter, _, symbol = check_head(list(order_axe), list_val)
|
|
20
|
+
|
|
21
|
+
misss = set(list_val) - symbol
|
|
22
|
+
if misss != set():
|
|
23
|
+
bad_head = True
|
|
24
|
+
ms_error_letter += f"The letters {misss} are missing.\n"
|
|
25
|
+
|
|
26
|
+
ms_error = "Your order axe is not correct.\n"
|
|
27
|
+
ms_error += ms_error_letter
|
|
28
|
+
if bad_head:
|
|
29
|
+
raise ValueError(ms_error)
|
|
30
|
+
|
|
31
|
+
return convert_opk_to_xyz(order_axe)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def convert_opk_to_xyz(order_axe: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Convert for scipy formalism.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
order_axe (str): Order of rotation matrix axes.
|
|
40
|
+
|
|
41
|
+
Retuns:
|
|
42
|
+
str: Order of ratation matrix axe for scipy.
|
|
43
|
+
"""
|
|
44
|
+
letter_conv = {"o": "x", "p": "y", "k": "z"}
|
|
45
|
+
|
|
46
|
+
form_xyz = ""
|
|
47
|
+
for i in order_axe:
|
|
48
|
+
form_xyz += letter_conv[i]
|
|
49
|
+
|
|
50
|
+
return form_xyz
|