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,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Script to read point (connecting point, gcp2d gcp3d) format
|
|
3
|
+
.txt/.mes/.app with data arranged in columns.
|
|
4
|
+
"""
|
|
5
|
+
from pathlib import Path, PureWindowsPath
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
from borea.worksite.worksite import Worksite
|
|
9
|
+
from borea.utils.check.check_args_reader_pt import check_header_file
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def read_file_pt(path: str, header: list, type_point: str, work: Worksite) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Read file of points.
|
|
15
|
+
|
|
16
|
+
Agrs:
|
|
17
|
+
file (str): Path of points file.
|
|
18
|
+
header (list): Header of file to read column.
|
|
19
|
+
type_point (str): Type of point is reading (co_point, gcp2d, gcp3d).
|
|
20
|
+
work (Worksite): Worksite which needs connecting points.
|
|
21
|
+
"""
|
|
22
|
+
if type_point not in ["co_point", "gcp2d", "gcp3d"]:
|
|
23
|
+
raise ValueError(f"type {type_point} in incorrect. ['co_point', 'gcp2d', 'gcp3d']")
|
|
24
|
+
|
|
25
|
+
header, type_z = check_header_file(header, type_point)
|
|
26
|
+
work.type_z_data = type_z
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
with open(Path(PureWindowsPath(path)), 'r', encoding="utf-8") as file_pts:
|
|
30
|
+
for pt in file_pts.readlines():
|
|
31
|
+
if pt != '\n' and pt[0] != '#':
|
|
32
|
+
info = pt.split()
|
|
33
|
+
|
|
34
|
+
if type_point == "gcp3d":
|
|
35
|
+
coor = np.array([float(info[header.index("X")]),
|
|
36
|
+
float(info[header.index("Y")]),
|
|
37
|
+
float(info[header.index("Z")])])
|
|
38
|
+
type_2args = "T"
|
|
39
|
+
else:
|
|
40
|
+
coor = np.array([float(info[header.index("X")]),
|
|
41
|
+
float(info[header.index("Y")])])
|
|
42
|
+
type_2args = "N"
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
getattr(work, "add_" + type_point)(info[header.index("P")],
|
|
46
|
+
info[header.index(type_2args)],
|
|
47
|
+
coor)
|
|
48
|
+
except ValueError as e:
|
|
49
|
+
raise ValueError("The letter T is missing "
|
|
50
|
+
"from the header of the file.") from e
|
|
51
|
+
file_pts.close()
|
|
52
|
+
except FileNotFoundError as e:
|
|
53
|
+
raise FileNotFoundError(f"The path {path} is incorrect !!!") from e
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_file_pt_dataframe(path: str, header: list, type_point: str) -> pd.DataFrame:
|
|
57
|
+
"""
|
|
58
|
+
Read file of points to save in Dataframe.
|
|
59
|
+
|
|
60
|
+
Agrs:
|
|
61
|
+
file (str): Path of points file.
|
|
62
|
+
header (list): Header of file to read column.
|
|
63
|
+
type_point (str): Type of point is reading (pt2d, pt3d).
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
pd.Dataframe: Dataframe of data.
|
|
67
|
+
"""
|
|
68
|
+
if type_point not in ["pt2d", "pt3d"]:
|
|
69
|
+
raise ValueError(f"type {type_point} in incorrect. ['pt2d', 'pt3d']")
|
|
70
|
+
|
|
71
|
+
header, _ = check_header_file(header, type_point)
|
|
72
|
+
|
|
73
|
+
id_pt = []
|
|
74
|
+
ttype = []
|
|
75
|
+
coor = []
|
|
76
|
+
try:
|
|
77
|
+
with open(Path(PureWindowsPath(path)), 'r', encoding="utf-8") as file_pts:
|
|
78
|
+
for pt in file_pts.readlines():
|
|
79
|
+
if pt != '\n' and pt[0] != '#':
|
|
80
|
+
info = pt.split()
|
|
81
|
+
|
|
82
|
+
if type_point == "pt3d":
|
|
83
|
+
coor.append([float(info[header.index("X")]),
|
|
84
|
+
float(info[header.index("Y")]),
|
|
85
|
+
float(info[header.index("Z")])])
|
|
86
|
+
type_2args = "T"
|
|
87
|
+
else:
|
|
88
|
+
coor.append([float(info[header.index("X")]),
|
|
89
|
+
float(info[header.index("Y")])])
|
|
90
|
+
type_2args = "N"
|
|
91
|
+
|
|
92
|
+
id_pt.append(info[header.index("P")])
|
|
93
|
+
try:
|
|
94
|
+
ttype.append(info[header.index(type_2args)])
|
|
95
|
+
except ValueError:
|
|
96
|
+
continue
|
|
97
|
+
file_pts.close()
|
|
98
|
+
except FileNotFoundError as e:
|
|
99
|
+
raise FileNotFoundError(f"The path {path} is incorrect !!!") from e
|
|
100
|
+
|
|
101
|
+
coor = np.array(coor)
|
|
102
|
+
if type_point == "pt3d":
|
|
103
|
+
df = pd.DataFrame({"id_pt": id_pt,
|
|
104
|
+
"type": ttype if ttype else None,
|
|
105
|
+
"x": coor[:, 0],
|
|
106
|
+
"y": coor[:, 1],
|
|
107
|
+
"z": coor[:, 2]})
|
|
108
|
+
else:
|
|
109
|
+
df = pd.DataFrame({"id_pt": id_pt,
|
|
110
|
+
"id_shot": ttype,
|
|
111
|
+
"column": coor[:, 0],
|
|
112
|
+
"line": coor[:, 1]})
|
|
113
|
+
return df
|
borea/stat/__init__.py
ADDED
|
File without changes
|
borea/stat/statistics.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for statistics
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import io
|
|
6
|
+
from pathlib import Path, PureWindowsPath
|
|
7
|
+
import numpy as np
|
|
8
|
+
from borea.worksite.worksite import Worksite
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Stat:
|
|
12
|
+
"""
|
|
13
|
+
Calculates site statistics.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, work: Worksite, pathoutput: str, type_point: list = None) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Definition of the class Stat.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
work (Worksite): The worksite to be treated.
|
|
21
|
+
pathoutput (str): Path to save the file.
|
|
22
|
+
type_point (list): List of type point on which we make the stats.
|
|
23
|
+
"""
|
|
24
|
+
self.work = work
|
|
25
|
+
self.pathoutput = Path(PureWindowsPath(pathoutput))
|
|
26
|
+
|
|
27
|
+
if type_point is None:
|
|
28
|
+
self.type_point = []
|
|
29
|
+
else:
|
|
30
|
+
self.type_point = type_point
|
|
31
|
+
|
|
32
|
+
self.res_world_image = []
|
|
33
|
+
self.res_image_world = []
|
|
34
|
+
self.stat_world_image = {}
|
|
35
|
+
self.stat_image_world = {}
|
|
36
|
+
|
|
37
|
+
def main_stat_and_save(self) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Calculates and saves all site statistics.
|
|
40
|
+
"""
|
|
41
|
+
self.main_stat_world_to_image()
|
|
42
|
+
self.main_stat_image_to_world()
|
|
43
|
+
self.save_stat_txt()
|
|
44
|
+
|
|
45
|
+
def main_stat_world_to_image(self) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Calculates residual and statistics on control point for world to image function.
|
|
48
|
+
"""
|
|
49
|
+
self.stat_world_to_image()
|
|
50
|
+
if self.res_world_image:
|
|
51
|
+
self.stat_world_image = self.stat_list(self.res_world_image)
|
|
52
|
+
|
|
53
|
+
def main_stat_image_to_world(self) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Calculates residual and statistics on control point for image to world function.
|
|
56
|
+
"""
|
|
57
|
+
self.stat_image_to_world()
|
|
58
|
+
if self.res_image_world:
|
|
59
|
+
self.stat_image_world = self.stat_list(self.res_image_world)
|
|
60
|
+
|
|
61
|
+
def stat_world_to_image(self) -> None:
|
|
62
|
+
"""
|
|
63
|
+
Calculates residual of controle point for world to image.
|
|
64
|
+
residual = image's ground point - calculated image's ground point
|
|
65
|
+
"""
|
|
66
|
+
for name_gcp, l_shot in self.work.gcp2d.items():
|
|
67
|
+
try:
|
|
68
|
+
if self.work.gcp3d[name_gcp].code in self.type_point or self.type_point == []:
|
|
69
|
+
for shot in l_shot:
|
|
70
|
+
try:
|
|
71
|
+
img_coor = self.work.shots[shot].gcp2d[name_gcp]
|
|
72
|
+
img_coor_calculated = self.work.shots[shot].gcp3d[name_gcp]
|
|
73
|
+
l_data = [[name_gcp, shot], img_coor - img_coor_calculated]
|
|
74
|
+
self.res_world_image.append(l_data)
|
|
75
|
+
except KeyError:
|
|
76
|
+
continue
|
|
77
|
+
except KeyError:
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
def stat_image_to_world(self) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Calculates residual of controle point for image to world.
|
|
83
|
+
residual = ground control point - calculated ground control point
|
|
84
|
+
"""
|
|
85
|
+
for name_gcp in list(self.work.gcp2d):
|
|
86
|
+
try:
|
|
87
|
+
if self.work.gcp3d[name_gcp].code in self.type_point or self.type_point == []:
|
|
88
|
+
try:
|
|
89
|
+
gcp_coor = self.work.gcp3d[name_gcp].coor
|
|
90
|
+
gcp_coor_calculated = self.work.gcp2d_in_world[name_gcp]
|
|
91
|
+
l_data = [[name_gcp], gcp_coor - gcp_coor_calculated]
|
|
92
|
+
self.res_image_world.append(l_data)
|
|
93
|
+
except KeyError:
|
|
94
|
+
continue
|
|
95
|
+
except KeyError:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
def stat_list(self, data_list: list) -> dict:
|
|
99
|
+
"""
|
|
100
|
+
Calculates statistics on residual data.
|
|
101
|
+
Min, Max, Median, Mean, Var, Sigma arithmetic and absolute.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
data_list (list): List of data.
|
|
105
|
+
"""
|
|
106
|
+
dict_output = {}
|
|
107
|
+
data = []
|
|
108
|
+
for ld in data_list:
|
|
109
|
+
data.append(ld[-1])
|
|
110
|
+
data = np.array(data)
|
|
111
|
+
|
|
112
|
+
list_stat1 = [[np.amin, np.amax], ["Min", "Max"], [np.argmin, np.argmax]]
|
|
113
|
+
list_stat2 = [[np.median, np.mean, np.var, np.std],
|
|
114
|
+
["Median", "Mean", "Var", "Sigma"]]
|
|
115
|
+
|
|
116
|
+
for func, name, argfunc in zip(list_stat1[0], list_stat1[1], list_stat1[2]):
|
|
117
|
+
pstat = []
|
|
118
|
+
abspstat = []
|
|
119
|
+
for i, j in zip(argfunc(data, axis=0), argfunc(abs(data), axis=0)):
|
|
120
|
+
if len(data_list[i][0]) == 1:
|
|
121
|
+
pstat.append(data_list[i][0][0])
|
|
122
|
+
abspstat.append(data_list[j][0][0])
|
|
123
|
+
else:
|
|
124
|
+
pstat.append(data_list[i][0])
|
|
125
|
+
abspstat.append(data_list[j][0])
|
|
126
|
+
dict_output[f"{name}_arith"] = {"val": func(data, axis=0), "data": pstat}
|
|
127
|
+
dict_output[f"{name}_abs"] = {"val": func(abs(data), axis=0), "data": abspstat}
|
|
128
|
+
|
|
129
|
+
for func, name in zip(list_stat2[0], list_stat2[1]):
|
|
130
|
+
dict_output[f"{name}_arith"] = np.round(func(data, axis=0), 2)
|
|
131
|
+
dict_output[f"{name}_abs"] = np.round(func(abs(data), axis=0), 2)
|
|
132
|
+
|
|
133
|
+
return dict_output
|
|
134
|
+
|
|
135
|
+
def save_stat_txt(self) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Save calculation statistics in a .txt file.
|
|
138
|
+
"""
|
|
139
|
+
path_riw = os.path.join(self.pathoutput, f"Stat_residu_image_to_world_{self.work.name}.txt")
|
|
140
|
+
path_miw = os.path.join(self.pathoutput, f"Stat_metric_image_to_world_{self.work.name}.txt")
|
|
141
|
+
path_rwi = os.path.join(self.pathoutput, f"Stat_residu_world_to_image_{self.work.name}.txt")
|
|
142
|
+
path_mwi = os.path.join(self.pathoutput, f"Stat_metric_world_to_image_{self.work.name}.txt")
|
|
143
|
+
|
|
144
|
+
if self.res_image_world:
|
|
145
|
+
try:
|
|
146
|
+
with open(path_riw, "w", encoding="utf-8") as file_riw:
|
|
147
|
+
file_riw.write("Control point statistics file.\n")
|
|
148
|
+
file_riw.write("\n")
|
|
149
|
+
file_riw.write("\n")
|
|
150
|
+
file_riw.write("Residue of control points from image to terrain function.\n")
|
|
151
|
+
file_riw.write("residual = ground control point -"
|
|
152
|
+
" calculated ground control point")
|
|
153
|
+
file_riw.write("\n")
|
|
154
|
+
file_riw.write("name_point res_x res_y res_z\n")
|
|
155
|
+
for data in self.res_image_world:
|
|
156
|
+
file_riw.write(f"{data[0][0]} {data[1][0]} {data[1][1]} {data[1][2]}\n")
|
|
157
|
+
file_riw.close()
|
|
158
|
+
except FileNotFoundError as e:
|
|
159
|
+
raise ValueError("The path doesn't exist !!!") from e
|
|
160
|
+
|
|
161
|
+
with open(path_miw, "w", encoding="utf-8") as file_miw:
|
|
162
|
+
file_miw.write("Control point statistics file.\n")
|
|
163
|
+
file_miw.write("\n")
|
|
164
|
+
file_miw.write("\n")
|
|
165
|
+
file_miw.write("Statistics on residual function image to world.\n")
|
|
166
|
+
file_miw.write("Name_stat: [stat_X, stat_Y, stat_Z]\n")
|
|
167
|
+
self.write_stat(file_miw, self.stat_image_world)
|
|
168
|
+
file_miw.close()
|
|
169
|
+
|
|
170
|
+
if self.res_world_image:
|
|
171
|
+
with open(path_rwi, "w", encoding="utf-8") as file_rwi:
|
|
172
|
+
file_rwi.write("Control point statistics file.\n")
|
|
173
|
+
file_rwi.write("\n")
|
|
174
|
+
file_rwi.write("\n")
|
|
175
|
+
file_rwi.write("Residual control points from terrain to image function.\n")
|
|
176
|
+
file_rwi.write("residual = image's ground point - calculated image's ground point")
|
|
177
|
+
file_rwi.write("\n")
|
|
178
|
+
file_rwi.write("name_point name_shot res_column res_line\n")
|
|
179
|
+
for data in self.res_world_image:
|
|
180
|
+
file_rwi.write(f"{data[0][0]} {data[0][1]} {data[1][0]} {data[1][1]}\n")
|
|
181
|
+
file_rwi.close()
|
|
182
|
+
|
|
183
|
+
with open(path_mwi, "w", encoding="utf-8") as file_mwi:
|
|
184
|
+
file_mwi.write("Control point statistics file.\n")
|
|
185
|
+
file_mwi.write("\n")
|
|
186
|
+
file_mwi.write("\n")
|
|
187
|
+
file_mwi.write("Statistics on residual function world to image.\n")
|
|
188
|
+
file_mwi.write("Name_stat: [stat_column, stat_line]\n")
|
|
189
|
+
self.write_stat(file_mwi, self.stat_world_image)
|
|
190
|
+
file_mwi.close()
|
|
191
|
+
|
|
192
|
+
def write_stat(self, file: io.TextIOWrapper, data: dict) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Write dictionary data statistics of the stat_list function on a file.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
file (io.TextIOWrapper): File to write on.
|
|
198
|
+
data (dict): Dictionary on data statistics to stat_list.
|
|
199
|
+
"""
|
|
200
|
+
file.write(f"Minimum_arithmetic: {data['Min_arith']['val']}\n")
|
|
201
|
+
file.write(f"Associated_point: {data['Min_arith']['data']}\n")
|
|
202
|
+
file.write(f"Minimum_absolute: {data['Min_abs']['val']}\n")
|
|
203
|
+
file.write(f"Associated_point: {data['Min_abs']['data']}\n")
|
|
204
|
+
file.write(f"Maximum_arithmetic: {data['Max_arith']['val']}\n")
|
|
205
|
+
file.write(f"Associated_point: {data['Max_arith']['data']}\n")
|
|
206
|
+
file.write(f"Maximum_absolute: {data['Max_abs']['val']}\n")
|
|
207
|
+
file.write(f"Associated_point: {data['Max_abs']['data']}\n")
|
|
208
|
+
file.write(f"Median_arithmetic: {data['Median_arith']}\n")
|
|
209
|
+
file.write(f"Median_absolute: {data['Median_abs']}\n")
|
|
210
|
+
file.write(f"Mean_arithmetic: {data['Mean_arith']}\n")
|
|
211
|
+
file.write(f"Mean_absolute: {data['Mean_abs']}\n")
|
|
212
|
+
file.write(f"Variance_arithmetic: {data['Var_arith']}\n")
|
|
213
|
+
file.write(f"Variance_absolute: {data['Var_abs']}\n")
|
|
214
|
+
file.write(f"Sigma_arithmetic: {data['Sigma_arith']}\n")
|
|
215
|
+
file.write(f"Sigma_absolute: {data['Sigma_abs']}\n")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
World image transformation module for self
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WorldImageDtm:
|
|
8
|
+
"""
|
|
9
|
+
Function world_to_image and image_to_world for dtm.
|
|
10
|
+
Class parent of dtm.
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self, gt: tuple) -> None:
|
|
13
|
+
self.gt = gt
|
|
14
|
+
|
|
15
|
+
def image_to_world(self, coor_img: np.ndarray) -> np.ndarray:
|
|
16
|
+
"""
|
|
17
|
+
Compute world coordinates from image coordinates.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
coor_img (np.array): Coordinate image [column, line].
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
np.array: x, y world coordinates.
|
|
24
|
+
"""
|
|
25
|
+
if self.gt:
|
|
26
|
+
x = (np.array(coor_img[0])+0.5) * self.gt[1] + self.gt[0]
|
|
27
|
+
y = (np.array(coor_img[1])+0.5) * self.gt[5] + self.gt[3]
|
|
28
|
+
else:
|
|
29
|
+
x, y = np.nan, np.nan
|
|
30
|
+
return np.array([x, y])
|
|
31
|
+
|
|
32
|
+
def world_to_image(self, coor_world: np.ndarray) -> np.ndarray:
|
|
33
|
+
"""
|
|
34
|
+
Compute image coordinates from world coordinates.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
coor_world (np.array): Coordinate world 2D [X, Y].
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
np.array: Image coordinates.
|
|
41
|
+
"""
|
|
42
|
+
if self.gt:
|
|
43
|
+
col = (np.array(coor_world[0]) - self.gt[0])/self.gt[1] - 0.5
|
|
44
|
+
line = (np.array(coor_world[1]) - self.gt[3])/self.gt[5] - 0.5
|
|
45
|
+
else:
|
|
46
|
+
col, line = np.nan, np.nan
|
|
47
|
+
return np.array([col, line])
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module to conversion z for WorldImageShot and ImageWorldShot
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
from borea.datastruct.shot import Shot
|
|
6
|
+
from borea.geodesy.proj_engine import ProjEngine
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def conv_z_shot_to_z_data(shot: Shot, type_z_shot: str, type_z_data: str,
|
|
10
|
+
nonadir: bool = True, approx: bool = False) -> np.ndarray:
|
|
11
|
+
"""
|
|
12
|
+
Convert type z shot to type z data. The Z of the object is NOT modified.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
shot (Shot): Shot to process.
|
|
16
|
+
type_z_shot (str): Shot type z "altitude" or "height".
|
|
17
|
+
type_z_data (str): Data type z "altitude" or "height".
|
|
18
|
+
nonadir (bool): To calculate nadir no take linear alteration.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
np.array: Shot position coordinate with type z asking.
|
|
22
|
+
"""
|
|
23
|
+
pos_shot = shot.pos_shot.copy()
|
|
24
|
+
|
|
25
|
+
if nonadir and shot.linear_alteration and not approx:
|
|
26
|
+
new_z = shot.get_z_remove_scale_factor()
|
|
27
|
+
pos_shot[2] = new_z
|
|
28
|
+
|
|
29
|
+
if type_z_shot != type_z_data:
|
|
30
|
+
if type_z_shot == "height":
|
|
31
|
+
new_z = ProjEngine().tranform_altitude(pos_shot)
|
|
32
|
+
else:
|
|
33
|
+
new_z = ProjEngine().tranform_height(pos_shot)
|
|
34
|
+
pos_shot[2] = new_z
|
|
35
|
+
|
|
36
|
+
return pos_shot
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def conv_output_z_type(coor: np.ndarray, type_z_input: str, type_z_output: str) -> np.ndarray:
|
|
40
|
+
"""
|
|
41
|
+
Convert type z to the output given.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
coor (Union[float, np.ndarray]): Coordinate [X, Y, Z].
|
|
45
|
+
type_z_input (str): Z type in input "height" or "altitude".
|
|
46
|
+
type_z_output (str): Z type in output "height" or "altitude".
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
np.array: Coodinate x, y, z.
|
|
50
|
+
"""
|
|
51
|
+
new_z = coor[2]
|
|
52
|
+
if type_z_input != type_z_output:
|
|
53
|
+
if type_z_input == "height":
|
|
54
|
+
new_z = ProjEngine().tranform_altitude(coor)
|
|
55
|
+
else:
|
|
56
|
+
new_z = ProjEngine().tranform_height(coor)
|
|
57
|
+
|
|
58
|
+
return np.array([coor[0], coor[1], new_z])
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Image world transformation module for Shot
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
from borea.datastruct.shot import Shot
|
|
6
|
+
from borea.datastruct.camera import Camera
|
|
7
|
+
from borea.datastruct.dtm import Dtm
|
|
8
|
+
from borea.geodesy.proj_engine import ProjEngine
|
|
9
|
+
from borea.transform_world_image.transform_shot.conversion_coor_shot import conv_z_shot_to_z_data
|
|
10
|
+
from borea.transform_world_image.transform_shot.conversion_coor_shot import conv_output_z_type
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ImageWorldShot():
|
|
14
|
+
"""
|
|
15
|
+
Function image_to_world for shot.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
shot (Shot): The shot for convert coordinate.
|
|
19
|
+
cam (Camera): The camera of the shot.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, shot: Shot, cam: Camera) -> None:
|
|
22
|
+
self.shot = shot
|
|
23
|
+
self.cam = cam
|
|
24
|
+
|
|
25
|
+
def image_to_world(self, img_coor: np.ndarray,
|
|
26
|
+
type_z_data: str, type_z_shot: str,
|
|
27
|
+
nonadir: bool = True) -> np.ndarray:
|
|
28
|
+
"""
|
|
29
|
+
Calculate x and y cartographique coordinate with z.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
img_coor (np.array): Image coordinate [col line].
|
|
33
|
+
type_z_data (str): Type of z data you want in output, "height" or "altitude".
|
|
34
|
+
type_z_shot (str): Type of z shot, "height" or "altitude".
|
|
35
|
+
nonadir (bool): To calculate nadir no take linear alteration.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
np.array: Cartographique coordinate [x,y,z].
|
|
39
|
+
"""
|
|
40
|
+
if type_z_data != type_z_shot and not ProjEngine().geog_to_geoid:
|
|
41
|
+
raise ValueError("Missing geoid")
|
|
42
|
+
|
|
43
|
+
if not Dtm().path_dtm:
|
|
44
|
+
raise ValueError("Missing dtm")
|
|
45
|
+
|
|
46
|
+
img_coor = np.squeeze(img_coor)
|
|
47
|
+
coor_world = self.image_world_iter(img_coor, type_z_shot, nonadir)
|
|
48
|
+
|
|
49
|
+
coor_world = conv_output_z_type(coor_world, Dtm().type_dtm, type_z_data)
|
|
50
|
+
|
|
51
|
+
return coor_world
|
|
52
|
+
|
|
53
|
+
def image_world_iter(self, img_coor: np.ndarray,
|
|
54
|
+
type_z_shot: str, nonadir: bool = True) -> np.ndarray:
|
|
55
|
+
"""
|
|
56
|
+
Calculate x and y cartographique coordinate with z.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
img_coor (np.array): Image coordinate [col line].
|
|
60
|
+
type_z_shot (str): Type of z shot, "height" or "altitude".
|
|
61
|
+
nonadir (bool): To calculate nadir no take linear alteration.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
np.array: Cartographique coordinate [x,y,z].
|
|
65
|
+
"""
|
|
66
|
+
z_world = np.full_like(img_coor[0], Dtm().get_z_world(self.shot.pos_shot[0:2]))
|
|
67
|
+
coor_world = self.image_z_to_world(img_coor, type_z_shot, z_world, nonadir)
|
|
68
|
+
precision_reached = False
|
|
69
|
+
nbr_iter = 0
|
|
70
|
+
iter_max = 10
|
|
71
|
+
while not precision_reached and nbr_iter < iter_max:
|
|
72
|
+
z_world = np.squeeze(Dtm().get_z_world(coor_world[0:2]))
|
|
73
|
+
coor_new_world = self.image_z_to_world(img_coor, type_z_shot, z_world, nonadir)
|
|
74
|
+
x_diff = (coor_new_world[0] - coor_world[0]) ** 2
|
|
75
|
+
y_diff = (coor_new_world[1] - coor_world[1]) ** 2
|
|
76
|
+
z_diff = (coor_new_world[2] - coor_world[2]) ** 2
|
|
77
|
+
dist2 = x_diff + y_diff + z_diff
|
|
78
|
+
precision_reached = np.any(dist2 < 0.01**2)
|
|
79
|
+
coor_world = coor_new_world
|
|
80
|
+
nbr_iter += 1
|
|
81
|
+
|
|
82
|
+
return coor_world
|
|
83
|
+
|
|
84
|
+
def image_z_to_world(self, img_coor: np.ndarray, type_z_shot: str,
|
|
85
|
+
z: np.ndarray = 0, nonadir: bool = True) -> np.ndarray:
|
|
86
|
+
"""
|
|
87
|
+
Calculate x and y cartographique coordinate with z.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
img_coor (np.array): Image coordinate [col line].
|
|
91
|
+
type_z_shot (str): Type of z "height" or "altitude".
|
|
92
|
+
z (Union[np.array, float]): La position z du point par défault = 0.
|
|
93
|
+
nonadir (bool): To calculate nadir no take linear alteration.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
np.array: Cartographique coordinate [x,y,z].
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(img_coor[0], np.ndarray):
|
|
99
|
+
if np.all(z == 0):
|
|
100
|
+
z = np.full_like(img_coor[0], 0)
|
|
101
|
+
|
|
102
|
+
pt_bundle = self.image_to_bundle(img_coor)
|
|
103
|
+
|
|
104
|
+
pos_shot_new_z = conv_z_shot_to_z_data(self.shot, type_z_shot, Dtm().type_dtm,
|
|
105
|
+
nonadir, self.shot.approxeucli)
|
|
106
|
+
|
|
107
|
+
pos_eucli = self.shot.projeucli.world_to_eucli(pos_shot_new_z)
|
|
108
|
+
|
|
109
|
+
pt_eucli = self.local_to_eucli(pt_bundle, pos_eucli, z)
|
|
110
|
+
|
|
111
|
+
pt_world = self.shot.projeucli.eucli_to_world(pt_eucli)
|
|
112
|
+
np.array([pt_world[0], pt_world[1], z])
|
|
113
|
+
|
|
114
|
+
return np.array([pt_world[0], pt_world[1], z])
|
|
115
|
+
|
|
116
|
+
def image_to_bundle(self, img_coor: np.ndarray) -> np.ndarray:
|
|
117
|
+
"""
|
|
118
|
+
Convert coordinate image col line to coordinate bundle.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
img_coor (np.array): Image coordinate [col line].
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
np.array: Cartographique coordinate [x,y,z].
|
|
125
|
+
"""
|
|
126
|
+
x_shot = img_coor[0] - self.cam.ppax
|
|
127
|
+
y_shot = img_coor[1] - self.cam.ppay
|
|
128
|
+
z_shot = np.full_like(x_shot, self.cam.focal)
|
|
129
|
+
x_shot, y_shot, z_shot = self.shot.f_sys_inv(x_shot, y_shot, z_shot)
|
|
130
|
+
x_bundle = x_shot / self.cam.focal * z_shot
|
|
131
|
+
y_bundle = y_shot / self.cam.focal * z_shot
|
|
132
|
+
z_bundle = z_shot
|
|
133
|
+
return np.array([x_bundle, y_bundle, z_bundle])
|
|
134
|
+
|
|
135
|
+
def local_to_eucli(self, pt_bundle: np.ndarray, pos_eucli: np.ndarray,
|
|
136
|
+
z: np.ndarray) -> np.ndarray:
|
|
137
|
+
"""
|
|
138
|
+
Convert coordinate point in local system to euclidean system.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
pt_bundle (np.array): Point to convert [X, Y, Z].
|
|
142
|
+
pos_eucli (np.array): Shot position in the euclidean system.
|
|
143
|
+
z (np.array): Z position of points.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
np.array: [X, Y, Z] in euclidean system.
|
|
147
|
+
"""
|
|
148
|
+
p_local = self.shot.mat_rot_eucli.T @ np.vstack(pt_bundle)
|
|
149
|
+
p_local = np.squeeze(p_local + pos_eucli.reshape((3, 1)))
|
|
150
|
+
lamb = (z - pos_eucli[2])/(p_local[2] - pos_eucli[2])
|
|
151
|
+
x_local = pos_eucli[0] + (p_local[0] - pos_eucli[0]) * lamb
|
|
152
|
+
y_local = pos_eucli[1] + (p_local[1] - pos_eucli[1]) * lamb
|
|
153
|
+
return np.array([x_local, y_local, z])
|