imicpe 1.0.7__py3-none-any.whl → 1.0.8.1__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.
- imicpe/_version.py +1 -1
- imicpe/reco3D/__init__.py +4 -0
- imicpe/reco3D/angles.py +117 -0
- imicpe/reco3D/calculPose.py +284 -0
- imicpe/reco3D/evalTrajecto.py +167 -0
- imicpe/reco3D/vue3D.py +216 -0
- {imicpe-1.0.7.dist-info → imicpe-1.0.8.1.dist-info}/METADATA +1 -1
- {imicpe-1.0.7.dist-info → imicpe-1.0.8.1.dist-info}/RECORD +11 -6
- {imicpe-1.0.7.dist-info → imicpe-1.0.8.1.dist-info}/WHEEL +1 -1
- {imicpe-1.0.7.dist-info → imicpe-1.0.8.1.dist-info}/licenses/LICENSE +0 -0
- {imicpe-1.0.7.dist-info → imicpe-1.0.8.1.dist-info}/top_level.txt +0 -0
imicpe/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__="1.0.
|
|
1
|
+
__version__="1.0.8.1"
|
imicpe/reco3D/angles.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def ang2rot(ang):
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
Calcule la matrice de rotation 3x3 à partir de 3 angles (Euler Z–X–Y)
|
|
7
|
+
(ONERA/DTIM)
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
ang : array_like of shape (3,)
|
|
12
|
+
Angles des rotations autour des axes z, x, y (en radians)
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
R : ndarray of shape (3,3)
|
|
17
|
+
Matrice de rotation correspondante
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
ang = np.array(ang, dtype=float).flatten()
|
|
21
|
+
if ang.size != 3:
|
|
22
|
+
raise ValueError("ang doit être un vecteur de 3 éléments (z, x, y)")
|
|
23
|
+
|
|
24
|
+
cz, sz = np.cos(ang[0]), np.sin(ang[0])
|
|
25
|
+
cx, sx = np.cos(ang[1]), np.sin(ang[1])
|
|
26
|
+
cy, sy = np.cos(ang[2]), np.sin(ang[2])
|
|
27
|
+
|
|
28
|
+
# === Codage Euler z-x-y ===
|
|
29
|
+
Rz = np.array([
|
|
30
|
+
[cz, sz, 0],
|
|
31
|
+
[-sz, cz, 0],
|
|
32
|
+
[0, 0, 1]
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
Rx = np.array([
|
|
36
|
+
[1, 0, 0],
|
|
37
|
+
[0, cx, sx],
|
|
38
|
+
[0, -sx, cx]
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
Ry = np.array([
|
|
42
|
+
[cy, 0, -sy],
|
|
43
|
+
[0, 1, 0],
|
|
44
|
+
[sy, 0, cy]
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
R = Rz @ Rx @ Ry
|
|
48
|
+
return R
|
|
49
|
+
|
|
50
|
+
def rot2ang(matR):
|
|
51
|
+
"""
|
|
52
|
+
Conversion d'une matrice de rotation 3x3 en angles d'Euler (z, x, y)
|
|
53
|
+
Codage : Euler Z–X–Y
|
|
54
|
+
Retourne : ang (3x2) — deux solutions possibles
|
|
55
|
+
(ONERA/DTIM)
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
R : ndarray (3,3)
|
|
60
|
+
Matrice de rotation correspondante
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
ang : array_like (3,2)
|
|
65
|
+
Angles des rotations autour des axes z, x, y (en radians)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
# Sécurité : s'assurer que matR est un array numpy 3x3
|
|
69
|
+
matR = np.array(matR, dtype=float).reshape(3, 3)
|
|
70
|
+
|
|
71
|
+
# Cas général
|
|
72
|
+
if abs(matR[2, 1]) != 1:
|
|
73
|
+
ang = np.zeros((3, 2)) # (3x2) comme MATLAB après transposition finale
|
|
74
|
+
|
|
75
|
+
# rotation angle around x axis
|
|
76
|
+
ang[1, 0] = np.arcsin(-matR[2, 1])
|
|
77
|
+
ang[1, 1] = np.sign(ang[1, 0]) * np.pi - ang[1, 0]
|
|
78
|
+
|
|
79
|
+
# rotation angle around y axis (colonne 3 MATLAB → ligne 2 ici avant transpose)
|
|
80
|
+
ang[2, 0] = np.arctan2(matR[2, 0] / np.cos(ang[1, 0]),
|
|
81
|
+
matR[2, 2] / np.cos(ang[1, 0]))
|
|
82
|
+
ang[2, 1] = np.arctan2(matR[2, 0] / np.cos(ang[1, 1]),
|
|
83
|
+
matR[2, 2] / np.cos(ang[1, 1]))
|
|
84
|
+
|
|
85
|
+
# rotation angle around z axis (colonne 1 MATLAB → ligne 0 ici avant transpose)
|
|
86
|
+
ang[0, 0] = np.arctan2(matR[0, 1] / np.cos(ang[1, 0]),
|
|
87
|
+
matR[1, 1] / np.cos(ang[1, 0]))
|
|
88
|
+
ang[0, 1] = np.arctan2(matR[0, 1] / np.cos(ang[1, 1]),
|
|
89
|
+
matR[1, 1] / np.cos(ang[1, 1]))
|
|
90
|
+
|
|
91
|
+
return ang # déjà (3x2) comme après transpose MATLAB
|
|
92
|
+
|
|
93
|
+
# Cas dégénéré : matR[3,2] = ±1 → gimbal lock
|
|
94
|
+
else:
|
|
95
|
+
ang = np.zeros(3)
|
|
96
|
+
# variable "roll" inexistante en MATLAB original → non utilisée ici
|
|
97
|
+
# on garde simplement les 3 angles comme définis par le cas particulier
|
|
98
|
+
|
|
99
|
+
if matR[2, 1] == 1:
|
|
100
|
+
ang[1] = -np.pi / 2
|
|
101
|
+
ang[2] = np.arctan2(-matR[0, 2], -matR[1, 2]) - ang[0]
|
|
102
|
+
if ang[2] > np.pi:
|
|
103
|
+
ang[2] -= 2 * np.pi
|
|
104
|
+
elif ang[2] < -np.pi:
|
|
105
|
+
ang[2] += 2 * np.pi
|
|
106
|
+
|
|
107
|
+
else: # matR[2,1] == -1
|
|
108
|
+
ang[1] = np.pi / 2
|
|
109
|
+
ang[2] = ang[0] - np.arctan2(matR[0, 2], matR[1, 2])
|
|
110
|
+
if ang[2] > np.pi:
|
|
111
|
+
ang[2] -= 2 * np.pi
|
|
112
|
+
elif ang[2] < -np.pi:
|
|
113
|
+
ang[2] += 2 * np.pi
|
|
114
|
+
|
|
115
|
+
# Le MATLAB retourne ang = ang'
|
|
116
|
+
return ang.reshape(3, 1)
|
|
117
|
+
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
from .angles import ang2rot, rot2ang
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy.linalg import svd
|
|
4
|
+
from scipy.optimize import least_squares
|
|
5
|
+
# ===============================================
|
|
6
|
+
# === Fonction principale : affinageRT ==========
|
|
7
|
+
# ===============================================
|
|
8
|
+
|
|
9
|
+
def affinageRT(anginit, Tinit, matK, pos3D, pos2D):
|
|
10
|
+
"""
|
|
11
|
+
Affine rotation et translation par minimisation de l'erreur de reprojection
|
|
12
|
+
Entrées :
|
|
13
|
+
anginit : angles initiaux (3x1)
|
|
14
|
+
Tinit : translation initiale (3x1)
|
|
15
|
+
matK : matrice intrinsèque 3x3
|
|
16
|
+
pos3D : points 3D (Nx3)
|
|
17
|
+
pos2D : points 2D (Nx2)
|
|
18
|
+
Sortie :
|
|
19
|
+
angfin : angles affinés (3x1)
|
|
20
|
+
Tfin : translation affinée (3x1)
|
|
21
|
+
rmsInit : RMS initial
|
|
22
|
+
rmsFin : RMS final
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# ===============================================
|
|
26
|
+
# === Fonctions de rotation =====================
|
|
27
|
+
# ===============================================
|
|
28
|
+
|
|
29
|
+
# def ang2rot(ang):
|
|
30
|
+
# """Conversion angles (3x1) → matrice de rotation 3x3 (ordre Rx*Ry*Rz typique MATLAB)"""
|
|
31
|
+
# cx, cy, cz = np.cos(ang)
|
|
32
|
+
# sx, sy, sz = np.sin(ang)
|
|
33
|
+
|
|
34
|
+
# Rx = np.array([[1, 0, 0],
|
|
35
|
+
# [0, cx, -sx],
|
|
36
|
+
# [0, sx, cx]])
|
|
37
|
+
# Ry = np.array([[ cy, 0, sy],
|
|
38
|
+
# [ 0, 1, 0],
|
|
39
|
+
# [-sy, 0, cy]])
|
|
40
|
+
# Rz = np.array([[cz, -sz, 0],
|
|
41
|
+
# [sz, cz, 0],
|
|
42
|
+
# [ 0, 0, 1]])
|
|
43
|
+
|
|
44
|
+
# # MATLAB fait généralement R = Rz * Ry * Rx
|
|
45
|
+
# R = Rz @ Ry @ Rx
|
|
46
|
+
# return R
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ===============================================
|
|
50
|
+
# === Fonction de projection ====================
|
|
51
|
+
# ===============================================
|
|
52
|
+
|
|
53
|
+
def fct_proj_ck(d, X, K):
|
|
54
|
+
"""
|
|
55
|
+
Projection 3D → 2D avec paramètres caméra.
|
|
56
|
+
Équivalent MATLAB de fct_proj_ck
|
|
57
|
+
Entrées :
|
|
58
|
+
d : vecteur [T; ang] de taille (6,)
|
|
59
|
+
X : points 3D (3xN)
|
|
60
|
+
K : matrice intrinsèque (3x3)
|
|
61
|
+
Sortie :
|
|
62
|
+
x : projections 2D (2xN)
|
|
63
|
+
"""
|
|
64
|
+
alx = K[0, 0]
|
|
65
|
+
aly = K[1, 1]
|
|
66
|
+
x0 = K[0, 2]
|
|
67
|
+
y0 = K[1, 2]
|
|
68
|
+
|
|
69
|
+
R = ang2rot(d[3:6])
|
|
70
|
+
T = d[0:3].reshape(3, 1)
|
|
71
|
+
|
|
72
|
+
# Projection
|
|
73
|
+
D = R @ (X - T) # même que R*(X - repmat(T,1,N)) en MATLAB
|
|
74
|
+
x1 = alx * D[0, :] / D[2, :] + x0
|
|
75
|
+
y1 = aly * D[1, :] / D[2, :] + y0
|
|
76
|
+
|
|
77
|
+
x = np.vstack((x1, y1))
|
|
78
|
+
return x
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ===============================================
|
|
82
|
+
# === Fonction d'évaluation =====================
|
|
83
|
+
# ===============================================
|
|
84
|
+
|
|
85
|
+
def eval_fct_proj_ck(d, xref, X, K):
|
|
86
|
+
"""
|
|
87
|
+
Évalue la fonction de reprojection et retourne les résidus
|
|
88
|
+
Entrées :
|
|
89
|
+
d : [T; ang] (6,)
|
|
90
|
+
xref : 2xN (positions 2D de référence)
|
|
91
|
+
X : 3xN (points 3D)
|
|
92
|
+
K : matrice intrinsèque
|
|
93
|
+
Sortie :
|
|
94
|
+
res : vecteur des résidus (2N,)
|
|
95
|
+
x : projections (2xN)
|
|
96
|
+
"""
|
|
97
|
+
x = fct_proj_ck(d, X, K)
|
|
98
|
+
res = x - xref
|
|
99
|
+
res = res.flatten(order='F') # même ordre colonne que MATLAB
|
|
100
|
+
return res, x
|
|
101
|
+
|
|
102
|
+
nbPts = pos3D.shape[0]
|
|
103
|
+
if nbPts != pos2D.shape[0]:
|
|
104
|
+
print("Erreur : dimensions incompatibles entre pos3D et pos2D")
|
|
105
|
+
return None, None, None, None
|
|
106
|
+
|
|
107
|
+
# --- Calcul du résidu initial ---
|
|
108
|
+
Rinit = ang2rot(anginit.flatten())
|
|
109
|
+
Mproj3 = Rinit @ pos3D.T - Rinit @ Tinit.reshape(3, 1).repeat(nbPts, axis=1)
|
|
110
|
+
aux = matK @ Mproj3
|
|
111
|
+
mproj3 = aux[:2, :] / aux[2, :]
|
|
112
|
+
mproj3 = mproj3.T
|
|
113
|
+
|
|
114
|
+
residu3 = (mproj3.flatten() - pos2D.flatten())
|
|
115
|
+
rmsInit = np.sqrt(np.mean(residu3 ** 2))
|
|
116
|
+
|
|
117
|
+
# --- Optimisation non linéaire ---
|
|
118
|
+
dinit = np.concatenate((Tinit.flatten(), anginit.flatten()))
|
|
119
|
+
|
|
120
|
+
def fun(d):
|
|
121
|
+
res, _ = eval_fct_proj_ck(d, pos2D.T, pos3D.T, matK)
|
|
122
|
+
return res
|
|
123
|
+
|
|
124
|
+
res_lsq = least_squares(fun, dinit, method='lm', verbose=0)
|
|
125
|
+
dfin = res_lsq.x
|
|
126
|
+
|
|
127
|
+
# --- Résidu final ---
|
|
128
|
+
resfin, _ = eval_fct_proj_ck(dfin, pos2D.T, pos3D.T, matK)
|
|
129
|
+
rmsFin = np.sqrt(np.mean(resfin ** 2))
|
|
130
|
+
|
|
131
|
+
Tfin = dfin[0:3].reshape(3, 1)
|
|
132
|
+
angfin = dfin[3:6].reshape(3, 1)
|
|
133
|
+
|
|
134
|
+
# --- Résidu image final (comme MATLAB) ---
|
|
135
|
+
Rfin = ang2rot(angfin.flatten())
|
|
136
|
+
Mproj4 = Rfin @ pos3D.T - Rfin @ Tfin.repeat(nbPts, axis=1)
|
|
137
|
+
aux = matK @ Mproj4
|
|
138
|
+
mproj4 = aux[:2, :] / aux[2, :]
|
|
139
|
+
mproj4 = mproj4.T
|
|
140
|
+
|
|
141
|
+
residu4 = (mproj4.flatten() - pos2D.flatten())
|
|
142
|
+
rmsFin = np.sqrt(np.mean(residu4 ** 2))
|
|
143
|
+
return angfin.squeeze(), Tfin.squeeze(), rmsInit, rmsFin
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def calculePose3D2D(pts3D, pts2D, matK, nbPts, AFFINAGE=False):
|
|
147
|
+
"""
|
|
148
|
+
Calcule la pose d’une caméra (rotation et translation) à partir de correspondances 3D-2D.
|
|
149
|
+
|
|
150
|
+
Utilise la méthode DLT (Direct Linear Transform) avec une résolution par SVD pour estimer
|
|
151
|
+
la matrice de projection, puis extrait la rotation et la translation. Un affinage optionnel
|
|
152
|
+
permet d'améliorer la précision via une minimisation de l'erreur de reprojection.
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
pts3D : ndarray of shape (nbPts, 3)
|
|
157
|
+
Coordonnées des points 3D dans le repère monde.
|
|
158
|
+
|
|
159
|
+
pts2D : ndarray of shape (nbPts, 2)
|
|
160
|
+
Coordonnées des points 2D correspondants dans l'image (en pixels).
|
|
161
|
+
|
|
162
|
+
matK : ndarray of shape (3, 3)
|
|
163
|
+
Matrice de calibration intrinsèque de la caméra.
|
|
164
|
+
|
|
165
|
+
nbPts : int
|
|
166
|
+
Nombre de points de correspondance 3D-2D utilisés pour le calcul.
|
|
167
|
+
|
|
168
|
+
AFFINAGE : bool, optional
|
|
169
|
+
Si True, active un affinage de la pose par minimisation de l'erreur de reprojection.
|
|
170
|
+
Par défaut à False.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
ang : ndarray of shape (3,)
|
|
175
|
+
Angles d'Euler (rx, ry, rz) en radians, représentant la rotation de la caméra.
|
|
176
|
+
|
|
177
|
+
T : ndarray of shape (3,)
|
|
178
|
+
Vecteur de translation de la caméra dans le repère monde.
|
|
179
|
+
|
|
180
|
+
rms_reproj : float
|
|
181
|
+
Erreur RMS (Root Mean Square) de reprojection entre les points 2D projetés et les
|
|
182
|
+
observations réelles.
|
|
183
|
+
|
|
184
|
+
Notes
|
|
185
|
+
-----
|
|
186
|
+
- La rotation est estimée à partir de la matrice de projection, et corrigée
|
|
187
|
+
pour s'assurer qu'elle appartient au groupe SO(3).
|
|
188
|
+
- Le vecteur d'angles `ang` est sélectionné comme la solution avec la norme minimale.
|
|
189
|
+
- Si `AFFINAGE` est activé, une fonction interne est utilisée pour optimiser
|
|
190
|
+
les paramètres `(R, T)` en minimisant l'erreur de reprojection.
|
|
191
|
+
|
|
192
|
+
Examples
|
|
193
|
+
--------
|
|
194
|
+
>>> ang, T, rms = calculePose3D2D(pts3D, pts2D, matK, len(pts3D), AFFINAGE=True)
|
|
195
|
+
>>> print("Angles (deg):", np.degrees(ang))
|
|
196
|
+
>>> print("Translation:", T)
|
|
197
|
+
>>> print("RMS reprojection error:", rms)
|
|
198
|
+
"""
|
|
199
|
+
ROTPROJ=False
|
|
200
|
+
|
|
201
|
+
fx = matK[0, 0]
|
|
202
|
+
fy = matK[1, 1]
|
|
203
|
+
x0 = matK[0, 2]
|
|
204
|
+
y0 = matK[1, 2]
|
|
205
|
+
# --- Constitution du système ---
|
|
206
|
+
Kinv = np.linalg.inv(matK)
|
|
207
|
+
posN = Kinv @ np.vstack((pts2D.T, np.ones((1, nbPts))))
|
|
208
|
+
posN = posN.T
|
|
209
|
+
M1 = np.hstack([
|
|
210
|
+
-pts3D, -np.ones((nbPts, 1)),
|
|
211
|
+
np.zeros((nbPts, 4)),
|
|
212
|
+
(posN[:, [0]] * pts3D), posN[:, [0]]
|
|
213
|
+
])
|
|
214
|
+
|
|
215
|
+
M2 = np.hstack([
|
|
216
|
+
np.zeros((nbPts, 4)),
|
|
217
|
+
-pts3D, -np.ones((nbPts, 1)),
|
|
218
|
+
(posN[:, [1]] * pts3D), posN[:, [1]]
|
|
219
|
+
])
|
|
220
|
+
|
|
221
|
+
M = np.vstack([M1, M2])
|
|
222
|
+
|
|
223
|
+
# --- Résolution SVD ---
|
|
224
|
+
U, S, Vt = svd(M)
|
|
225
|
+
pest = Vt[-1, :] # dernière ligne
|
|
226
|
+
pest = pest / np.linalg.norm(pest[8:11])
|
|
227
|
+
Pest = pest.reshape(4,3, order='F').T # comme reshape(...,4,3)' en MATLAB
|
|
228
|
+
|
|
229
|
+
if np.linalg.det(Pest[:, :3]) < 0:
|
|
230
|
+
Pest = -Pest
|
|
231
|
+
|
|
232
|
+
# --- R/T et angles ---
|
|
233
|
+
if ROTPROJ:
|
|
234
|
+
Ur, Sr, Vr = svd(Pest[:, :3])
|
|
235
|
+
Rest = Ur @ Vr
|
|
236
|
+
else:
|
|
237
|
+
Rest = Pest[:3, :3]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
angcandidat = rot2ang(Rest)
|
|
241
|
+
# Sélection du plus proche angle (ici, le plus faible)
|
|
242
|
+
imin = np.argmin(np.sum(angcandidat**2, axis=0))
|
|
243
|
+
ang = angcandidat[:, imin]
|
|
244
|
+
T = -Rest.T @ Pest[:, 3]
|
|
245
|
+
# --- Affinage optionnel ---
|
|
246
|
+
if AFFINAGE:
|
|
247
|
+
angfin, Tfin, rmsInit, rmsFin = affinageRT(ang, T, matK, pts3D, pts2D)
|
|
248
|
+
mvtfin = np.concatenate((angfin.flatten(), Tfin.flatten()))
|
|
249
|
+
ang = angfin
|
|
250
|
+
T = Tfin
|
|
251
|
+
else:
|
|
252
|
+
rmsInit = 0
|
|
253
|
+
rmsFin = 0
|
|
254
|
+
|
|
255
|
+
# --- Erreur de reprojection ---
|
|
256
|
+
|
|
257
|
+
Xw = pts3D.T
|
|
258
|
+
R = ang2rot(ang.flatten())
|
|
259
|
+
# Xc = R @ (Xw - T.reshape(3, 1))
|
|
260
|
+
# xn = Xc[:2, :] / Xc[2, :]
|
|
261
|
+
# x = np.vstack([fx * xn[0, :] + x0, fy * xn[1, :] + y0]).T
|
|
262
|
+
# res = (x - pts2D).flatten()
|
|
263
|
+
# rms_reproj = np.sqrt(np.mean(res**2))
|
|
264
|
+
# Transformation 3D → caméra
|
|
265
|
+
Xc = R @ (Xw - np.tile(T.reshape(3, 1), (1, nbPts))) # (3 x nbPts)
|
|
266
|
+
|
|
267
|
+
# Normalisation en coordonnées homogènes
|
|
268
|
+
xn = Xc[0:2, :] / np.tile(Xc[2, :], (2, 1)) # (2 x nbPts)
|
|
269
|
+
|
|
270
|
+
# Projection avec les paramètres intrinsèques
|
|
271
|
+
x = np.vstack((fx * xn[0, :] + x0, fy * xn[1, :] + y0)) # (2 x nbPts)
|
|
272
|
+
x = x.T # (nbPts x 2)
|
|
273
|
+
|
|
274
|
+
# Calcul du résidu et RMS
|
|
275
|
+
res = x.reshape(-1) - pts2D.reshape(-1)
|
|
276
|
+
rms_reproj = np.sqrt(np.mean(res**2))
|
|
277
|
+
# --- Affichage résultats ---
|
|
278
|
+
print(f"ang = [{ang[0]*180/np.pi:.2f}, {ang[1]*180/np.pi:.2f}, {ang[2]*180/np.pi:.2f}] deg")
|
|
279
|
+
print(f"T = [{T[0]:.4f}, {T[1]:.4f}, {T[2]:.4f}]")
|
|
280
|
+
if AFFINAGE:
|
|
281
|
+
print(f"nbPts={nbPts} --- RMS : avant={rmsInit:.4f}, après={rms_reproj:.4f}")
|
|
282
|
+
else:
|
|
283
|
+
print(f"nbPts={nbPts} --- RMS={rms_reproj:.4f}")
|
|
284
|
+
return ang, T, rms_reproj
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
def evalTrajecto(repBase, traj_pos, traj_ang, carte=None):
|
|
2
|
+
"""
|
|
3
|
+
Évalue une trajectoire estimée par rapport à une trajectoire de référence.
|
|
4
|
+
|
|
5
|
+
Cette fonction compare une trajectoire estimée (positions et angles) avec une solution
|
|
6
|
+
de référence stockée dans des fichiers `.npz`. Elle calcule différents scores d'erreur
|
|
7
|
+
(distance, position, orientation) et affiche une visualisation 3D comparative.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
repBase : str
|
|
12
|
+
Chemin vers le dossier contenant les fichiers `solution.npz` et `info_seq.npz`.
|
|
13
|
+
|
|
14
|
+
traj_pos : ndarray of shape (3, N)
|
|
15
|
+
Positions estimées de la trajectoire. Chaque colonne représente une position 3D
|
|
16
|
+
(X, Y, Z) pour une image donnée.
|
|
17
|
+
|
|
18
|
+
traj_ang : ndarray of shape (3, N)
|
|
19
|
+
Angles d'Euler estimés (rx, ry, rz) pour chaque position. Les angles sont en radians.
|
|
20
|
+
|
|
21
|
+
carte : optional
|
|
22
|
+
Paramètre optionnel (non utilisé dans la fonction actuelle).
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
score_pos1 : float
|
|
27
|
+
Pourcentage d'erreur relative entre la distance totale estimée et la distance réelle.
|
|
28
|
+
|
|
29
|
+
score_pos2 : float
|
|
30
|
+
Pourcentage maximal d'erreur relative entre les distances inter-images estimées et
|
|
31
|
+
celles de la trajectoire de référence.
|
|
32
|
+
|
|
33
|
+
score_ang : float
|
|
34
|
+
Erreur angulaire normalisée (en degrés par mètre), calculée entre la rotation finale
|
|
35
|
+
estimée et la rotation finale réelle.
|
|
36
|
+
|
|
37
|
+
score_posint : list of float
|
|
38
|
+
Intégrale des erreurs de position (X, Y, Z), calculées comme l'aire sous la courbe
|
|
39
|
+
des différences de position au cours du temps.
|
|
40
|
+
|
|
41
|
+
delta_pos : ndarray of shape (3, N-1)
|
|
42
|
+
Différences de position entre la solution de référence et la trajectoire estimée
|
|
43
|
+
pour chaque image (sauf la dernière).
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
- Cette fonction utilise la bibliothèque Plotly pour afficher une figure 3D comparant
|
|
48
|
+
les trajectoires de référence et estimée.
|
|
49
|
+
- Les fichiers `solution.npz` doivent contenir les clés ``sol_pos`` et ``sol_ang``.
|
|
50
|
+
- Le fichier `info_seq.npz` doit contenir la clé ``Kleft``, même si elle n'est pas utilisée
|
|
51
|
+
dans cette fonction.
|
|
52
|
+
- Les angles d'Euler sont interprétés dans l'ordre ZYX pour la conversion en matrices de rotation.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> score_pos1, score_pos2, score_ang, score_posint, delta_pos = evalTrajecto(
|
|
57
|
+
... repBase='data/test_seq',
|
|
58
|
+
... traj_pos=estimated_positions,
|
|
59
|
+
... traj_ang=estimated_angles
|
|
60
|
+
... )
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
import numpy as np
|
|
64
|
+
import plotly.graph_objects as go
|
|
65
|
+
import os
|
|
66
|
+
|
|
67
|
+
def ang2rot(angles):
|
|
68
|
+
"""
|
|
69
|
+
Convert Euler angles (rx, ry, rz) to rotation matrix.
|
|
70
|
+
Angles are assumed in radians.
|
|
71
|
+
"""
|
|
72
|
+
rx, ry, rz = angles
|
|
73
|
+
Rx = np.array([[1, 0, 0],
|
|
74
|
+
[0, np.cos(rx), -np.sin(rx)],
|
|
75
|
+
[0, np.sin(rx), np.cos(rx)]])
|
|
76
|
+
Ry = np.array([[np.cos(ry), 0, np.sin(ry)],
|
|
77
|
+
[0, 1, 0],
|
|
78
|
+
[-np.sin(ry), 0, np.cos(ry)]])
|
|
79
|
+
Rz = np.array([[np.cos(rz), -np.sin(rz), 0],
|
|
80
|
+
[np.sin(rz), np.cos(rz), 0],
|
|
81
|
+
[0, 0, 1]])
|
|
82
|
+
return Rz @ Ry @ Rx
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def rodrigues(R):
|
|
86
|
+
"""
|
|
87
|
+
Convert rotation matrix to rotation vector (axis-angle) or vice versa.
|
|
88
|
+
"""
|
|
89
|
+
if R.shape == (3,):
|
|
90
|
+
# Rotation vector -> matrix
|
|
91
|
+
theta = np.linalg.norm(R)
|
|
92
|
+
if theta < 1e-12:
|
|
93
|
+
return np.eye(3)
|
|
94
|
+
omega = R / theta
|
|
95
|
+
K = np.array([[0, -omega[2], omega[1]],
|
|
96
|
+
[omega[2], 0, -omega[0]],
|
|
97
|
+
[-omega[1], omega[0], 0]])
|
|
98
|
+
return np.eye(3) + np.sin(theta)*K + (1-np.cos(theta))*(K@K)
|
|
99
|
+
elif R.shape == (3, 3):
|
|
100
|
+
# Matrix -> rotation vector
|
|
101
|
+
U, _, Vt = np.linalg.svd(R)
|
|
102
|
+
R = U @ Vt # project onto SO(3)
|
|
103
|
+
theta = np.arccos(np.clip((np.trace(R)-1)/2, -1, 1))
|
|
104
|
+
if theta < 1e-12:
|
|
105
|
+
return np.zeros(3)
|
|
106
|
+
else:
|
|
107
|
+
return (theta/(2*np.sin(theta))) * np.array([R[2,1]-R[1,2],
|
|
108
|
+
R[0,2]-R[2,0],
|
|
109
|
+
R[1,0]-R[0,1]])
|
|
110
|
+
else:
|
|
111
|
+
raise ValueError("Input must be 3-vector or 3x3 matrix")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# Chargement des données MATLAB
|
|
115
|
+
# solution = loadmat(f"{repBase}/solution.mat")
|
|
116
|
+
# info_seq = loadmat(f"{repBase}/info_seq.mat")
|
|
117
|
+
solution = np.load(os.path.join(repBase, 'solution.npz'))
|
|
118
|
+
info_seq = np.load(os.path.join(repBase, 'info_seq.npz'))
|
|
119
|
+
sol_pos = solution['sol_pos']
|
|
120
|
+
sol_ang = solution['sol_ang']
|
|
121
|
+
matK = info_seq['Kleft']
|
|
122
|
+
|
|
123
|
+
nbIma = sol_pos.shape[1]
|
|
124
|
+
interdist_est = []
|
|
125
|
+
delta_pos = []
|
|
126
|
+
|
|
127
|
+
# Calcul des distances et différences de positions
|
|
128
|
+
for cpt in range(nbIma-1):
|
|
129
|
+
interdist_est.append(np.linalg.norm(traj_pos[:,cpt+1]-traj_pos[:,cpt]))
|
|
130
|
+
delta_pos.append(sol_pos[:,cpt] - traj_pos[:,cpt])
|
|
131
|
+
|
|
132
|
+
interdist_est = np.array(interdist_est)
|
|
133
|
+
delta_pos = np.array(delta_pos).T # 3 x (nbIma-1)
|
|
134
|
+
|
|
135
|
+
totaldist_est = np.sum(interdist_est)
|
|
136
|
+
totaldist = np.sum(np.linalg.norm(np.diff(sol_pos, axis=1), axis=0))
|
|
137
|
+
interdist = np.linalg.norm(np.diff(sol_pos, axis=1), axis=0)
|
|
138
|
+
|
|
139
|
+
score_pos1 = 100 * np.abs(totaldist_est - totaldist) / totaldist
|
|
140
|
+
score_pos2 = 100 * np.max(np.abs(interdist_est - interdist) / interdist)
|
|
141
|
+
|
|
142
|
+
# Calcul de l'erreur angulaire
|
|
143
|
+
Rest = ang2rot(traj_ang[:, -1])
|
|
144
|
+
Rtrue = ang2rot(sol_ang[:, -1])
|
|
145
|
+
Rerr = Rest.T @ Rtrue
|
|
146
|
+
rodri = rodrigues(Rerr)
|
|
147
|
+
score_ang = np.linalg.norm(rodri) * 180 / (np.pi * totaldist)
|
|
148
|
+
|
|
149
|
+
score_posint = [np.trapezoid(delta_pos[0,:]), np.trapezoid(delta_pos[1,:]), np.trapezoid(delta_pos[2,:])]
|
|
150
|
+
|
|
151
|
+
print(f"score_pos1 = {score_pos1:.2f}")
|
|
152
|
+
print(f"score_pos2 = {score_pos2:.2f}")
|
|
153
|
+
print(f"score_ang = {score_ang:.2f}")
|
|
154
|
+
print(f"integral Delta pos X = {score_posint[0]:.2f}")
|
|
155
|
+
print(f"integral Delta pos Y = {score_posint[1]:.2f}")
|
|
156
|
+
print(f"integral Delta pos Z = {score_posint[2]:.2f}")
|
|
157
|
+
|
|
158
|
+
# ==== Affichage comparatif ====
|
|
159
|
+
fig = go.Figure()
|
|
160
|
+
fig.add_trace(go.Scatter3d(x=sol_pos[0,:], y=sol_pos[1,:], z=sol_pos[2,:], mode='lines', line=dict(color='red'), name='ref'))
|
|
161
|
+
fig.add_trace(go.Scatter3d(x=traj_pos[0,:], y=traj_pos[1,:], z=traj_pos[2,:], mode='lines', line=dict(color='blue'), name='estim'))
|
|
162
|
+
fig.update_layout(title='Comparaison estimation de la trajectoire 3D (rouge=ref)',
|
|
163
|
+
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
|
|
164
|
+
legend=dict(x=0, y=1))
|
|
165
|
+
fig.show()
|
|
166
|
+
|
|
167
|
+
return score_pos1, score_pos2, score_ang, score_posint, delta_pos
|
imicpe/reco3D/vue3D.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
|
|
4
|
+
def vue3D(fig, pos3D, matK, sizeim, attitude, position, camColor, operation):
|
|
5
|
+
"""
|
|
6
|
+
Affiche une vue 3D interactive de nuages de points et de caméras avec Plotly.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
fig : plotly.graph_objects.Figure
|
|
11
|
+
Objet figure Plotly dans lequel la scène 3D est affichée ou mise à jour.
|
|
12
|
+
pos3D : ndarray
|
|
13
|
+
Coordonnées 3D des points dans le repère monde (array de forme (3, N)).
|
|
14
|
+
matK : ndarray
|
|
15
|
+
Matrice des paramètres intrinsèques de la caméra (3x3).
|
|
16
|
+
sizeim : tuple of int
|
|
17
|
+
Dimensions de l'image (hauteur, largeur), utilisées pour le champ de vision.
|
|
18
|
+
attitude : ndarray
|
|
19
|
+
Angles d'attitude de la caméra (angles d'Euler ou matrice de rotation).
|
|
20
|
+
position : ndarray
|
|
21
|
+
Position de la caméra dans le repère monde (vecteur de taille 3).
|
|
22
|
+
camColor : str or tuple
|
|
23
|
+
Couleur utilisée pour représenter la caméra dans la scène.
|
|
24
|
+
operation : {'create', 'addcam', 'addpts'}
|
|
25
|
+
Type d'opération à effectuer :
|
|
26
|
+
- 'create' : crée une nouvelle scène 3D,
|
|
27
|
+
- 'addcam' : ajoute une caméra à la scène,
|
|
28
|
+
- 'addpts' : ajoute un nuage de points à la scène.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
fig : plotly.graph_objects.Figure
|
|
33
|
+
Objet figure Plotly dans lequel la scène 3D est affichée ou mise à jour.
|
|
34
|
+
|
|
35
|
+
Notes
|
|
36
|
+
-----
|
|
37
|
+
Cette fonction permet de visualiser des scènes 3D de manière interactive avec Plotly.
|
|
38
|
+
Elle peut être utilisée pour représenter des caméras virtuelles, des trajectoires,
|
|
39
|
+
ou des nuages de points 3D dans un repère monde, avec une gestion dynamique
|
|
40
|
+
des éléments affichés selon l'opération demandée.
|
|
41
|
+
|
|
42
|
+
Example
|
|
43
|
+
-------
|
|
44
|
+
>>> from imicpe.reco3D import vue3D
|
|
45
|
+
>>> import plotly.graph_objects as go
|
|
46
|
+
>>> fig = go.Figure()
|
|
47
|
+
>>> vue3D(fig, pos3D, matK, (480, 640), attitude, position, 'blue', 'create')
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# """
|
|
51
|
+
# 3D view function for displaying point clouds and cameras.
|
|
52
|
+
|
|
53
|
+
# :param fig: The figure handler
|
|
54
|
+
# :param pos3D: 3D positions of the points in world coordinates (3xN array)
|
|
55
|
+
# :param matK: Intrinsic camera parameters
|
|
56
|
+
# :param sizeim: Image size (height, width)
|
|
57
|
+
# :param attitude: Camera attitude angles (Euler angles)
|
|
58
|
+
# :param position: Camera position in world coordinates
|
|
59
|
+
# :param camColor: Camera display color
|
|
60
|
+
# :param operation: Display operation, 'create', 'addcam', or 'addpts'
|
|
61
|
+
# """
|
|
62
|
+
seuil_dist = 30 # threshold for deleting points that are too far away
|
|
63
|
+
# Check for correct number of arguments
|
|
64
|
+
if fig is not None and type(fig) is not go.Figure:
|
|
65
|
+
print('First argument must be a Plotly figure')
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if len(attitude) == 0 or len(position) == 0:
|
|
69
|
+
print('Attitude and position should be provided')
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if (pos3D.shape[0] > 3):
|
|
73
|
+
pos3D=pos3D.T
|
|
74
|
+
print('warning : le tableau pos3D doit etre 3xN')
|
|
75
|
+
|
|
76
|
+
# Prepare params
|
|
77
|
+
params = {
|
|
78
|
+
'fig': fig,
|
|
79
|
+
'pt3DStyle': 'blue',
|
|
80
|
+
'camColor': camColor,
|
|
81
|
+
'holdon': False if operation == 'create' else True,
|
|
82
|
+
'tail': 1,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
nb_points = pos3D.shape[1]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Calculate distance between camera and 3D points
|
|
89
|
+
if len(position) == 0:
|
|
90
|
+
tensP = attitude
|
|
91
|
+
vec = pos3D - np.repeat(tensP[:, 3].reshape(3, 1), nb_points, axis=1)
|
|
92
|
+
dist = np.linalg.norm(vec, axis=0)
|
|
93
|
+
else:
|
|
94
|
+
Rloc = _euler_to_rot(attitude, 'zxy')
|
|
95
|
+
tensP = np.hstack([Rloc, -np.dot(Rloc, position.reshape(-1, 1))])
|
|
96
|
+
vec = pos3D - np.repeat(position.reshape(3, 1), nb_points, axis=1)
|
|
97
|
+
dist = np.linalg.norm(vec, axis=0)
|
|
98
|
+
|
|
99
|
+
# Filter points that are too far away
|
|
100
|
+
indOK = np.where(dist < seuil_dist)[0]
|
|
101
|
+
pos3D_filtered = pos3D[:, indOK]
|
|
102
|
+
if fig is None:
|
|
103
|
+
# Create the plotly figure
|
|
104
|
+
fig = go.Figure()
|
|
105
|
+
|
|
106
|
+
# Operation: 'addpts' means just add 3D points
|
|
107
|
+
if operation == 'addpts':
|
|
108
|
+
fig.add_trace(go.Scatter3d(
|
|
109
|
+
x=pos3D_filtered[0, :],
|
|
110
|
+
y=pos3D_filtered[1, :],
|
|
111
|
+
z=pos3D_filtered[2, :],
|
|
112
|
+
mode='markers',
|
|
113
|
+
marker=dict(color='red', size=5)
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
# Operation: 'addcam' means add the camera only
|
|
117
|
+
elif operation == 'addcam':
|
|
118
|
+
_plot_camera(tensP, matK, sizeim, fig, camColor)
|
|
119
|
+
|
|
120
|
+
# Operation: 'create' means both camera and 3D points
|
|
121
|
+
else:
|
|
122
|
+
fig.add_trace(go.Scatter3d(
|
|
123
|
+
x=pos3D_filtered[0, :],
|
|
124
|
+
y=pos3D_filtered[1, :],
|
|
125
|
+
z=pos3D_filtered[2, :],
|
|
126
|
+
mode='markers',
|
|
127
|
+
marker=dict(color='blue', size=5)
|
|
128
|
+
))
|
|
129
|
+
_plot_camera(tensP, matK, sizeim, fig, camColor)
|
|
130
|
+
|
|
131
|
+
# Set figure layout
|
|
132
|
+
fig.update_layout(
|
|
133
|
+
scene=dict(
|
|
134
|
+
xaxis_title='X',
|
|
135
|
+
yaxis_title='Y',
|
|
136
|
+
zaxis_title='Z',
|
|
137
|
+
),
|
|
138
|
+
title=f"3D View",
|
|
139
|
+
width=800,
|
|
140
|
+
height=800,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return fig
|
|
144
|
+
|
|
145
|
+
def _plot_camera(tensP, matK, sizeim, fig, camColor):
|
|
146
|
+
"""
|
|
147
|
+
Plots the camera as a cone shape in the 3D space.
|
|
148
|
+
"""
|
|
149
|
+
if len(tensP.shape) < 3:
|
|
150
|
+
tensP = tensP[:,:,np.newaxis]
|
|
151
|
+
nb_cam = tensP.shape[2]
|
|
152
|
+
|
|
153
|
+
imasize = [sizeim[1], sizeim[0]]#np.fliplr(sizeim).squeeze() # Flip size to [width, height]
|
|
154
|
+
for cptC in range(nb_cam):
|
|
155
|
+
current_tensP = tensP[:, :, cptC]
|
|
156
|
+
cone_vertices = np.zeros((3, 5))
|
|
157
|
+
vtmp = np.array([
|
|
158
|
+
[1, imasize[0], imasize[0], 1],
|
|
159
|
+
[1, 1, imasize[1], imasize[1]],
|
|
160
|
+
[1, 1, 1, 1]
|
|
161
|
+
])
|
|
162
|
+
|
|
163
|
+
matK_inv = np.linalg.inv(matK)
|
|
164
|
+
cone_vertices[:, 1:] = np.dot(matK_inv, vtmp) * 2 # Use tail length as scaling factor
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
cVWorld = np.dot(current_tensP[:, :3].T, (cone_vertices - current_tensP[:, 3].reshape(3, 1)))
|
|
169
|
+
|
|
170
|
+
# Add the cone edges to the plot
|
|
171
|
+
for i in range(4):
|
|
172
|
+
fig.add_trace(go.Scatter3d(
|
|
173
|
+
x=[cVWorld[0, 0], cVWorld[0, i + 1]],
|
|
174
|
+
y=[cVWorld[1, 0], cVWorld[1, i + 1]],
|
|
175
|
+
z=[cVWorld[2, 0], cVWorld[2, i + 1]],
|
|
176
|
+
mode='lines',
|
|
177
|
+
line=dict(color=camColor, width=2),
|
|
178
|
+
showlegend=False # Exclude from legend
|
|
179
|
+
))
|
|
180
|
+
|
|
181
|
+
fig.add_trace(go.Scatter3d(
|
|
182
|
+
x=[cVWorld[0, i + 1], cVWorld[0, (i + 1) % 4 + 1]], # Cycle through the vertices
|
|
183
|
+
y=[cVWorld[1, i + 1], cVWorld[1, (i + 1) % 4 + 1]],
|
|
184
|
+
z=[cVWorld[2, i + 1], cVWorld[2, (i + 1) % 4 + 1]],
|
|
185
|
+
mode='lines',
|
|
186
|
+
line=dict(color=camColor, width=2),
|
|
187
|
+
showlegend=False # Exclude from legend
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
def _euler_to_rot(euler_angles, euler_order='xyz'):
|
|
191
|
+
"""
|
|
192
|
+
Convert Euler angles to rotation matrix using the specified order.
|
|
193
|
+
"""
|
|
194
|
+
cx, cy, cz = np.cos(np.radians(euler_angles.squeeze()))
|
|
195
|
+
sx, sy, sz = np.sin(np.radians(euler_angles.squeeze()))
|
|
196
|
+
|
|
197
|
+
R_x = np.array([[1, 0, 0],
|
|
198
|
+
[0, cx, -sx],
|
|
199
|
+
[0, sx, cx]])
|
|
200
|
+
|
|
201
|
+
R_y = np.array([[cy, 0, sy],
|
|
202
|
+
[0, 1, 0],
|
|
203
|
+
[-sy, 0, cy]])
|
|
204
|
+
|
|
205
|
+
R_z = np.array([[cz, -sz, 0],
|
|
206
|
+
[sz, cz, 0],
|
|
207
|
+
[0, 0, 1]])
|
|
208
|
+
|
|
209
|
+
if euler_order == 'xyz':
|
|
210
|
+
return np.dot(R_z, np.dot(R_y, R_x))
|
|
211
|
+
elif euler_order == 'zyx':
|
|
212
|
+
return np.dot(R_x, np.dot(R_y, R_z))
|
|
213
|
+
# Other orders can be added as needed
|
|
214
|
+
|
|
215
|
+
return np.eye(3) # Default to identity if no valid order is found
|
|
216
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: imicpe
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8.1
|
|
4
4
|
Summary: Toolbox for Maths,Signal,Image Teaching @ CPE
|
|
5
5
|
Author-email: Marion Foare <marion.foare@cpe.fr>, Eric Van Reeth <eric.vanreeth@cpe.fr>, Arthur Gautheron <arthur.gautheron@cpe.fr>
|
|
6
6
|
License: MIT License
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
imicpe/__init__.py,sha256=WjDmvecyDIyJLYp4rCV9vsSYbQDc4L1EpYqORvEXliI,33
|
|
2
|
-
imicpe/_version.py,sha256=
|
|
2
|
+
imicpe/_version.py,sha256=rOzzGNw92mdz5AMtbKUX-5NQT20wf29BvR0nSb395AY,22
|
|
3
3
|
imicpe/cs/__init__.py,sha256=fLe1o5jNvrB4qEVBE6amEV9i3NFwM3E_uGiiiHXrph0,339
|
|
4
4
|
imicpe/cs/l1.py,sha256=5bTNM8JG8J8srTamh-gl3ShhFoJjqCvVwlteN_M6Uak,3236
|
|
5
5
|
imicpe/cs/masks.py,sha256=5ehQ-QnYY4trBt7BIx0Pp8DUa3x5KApkc7TPp9LA9Pc,2030
|
|
@@ -13,8 +13,13 @@ imicpe/optim/operators.py,sha256=eSiY9X6ZI3dWsMjkWkR3rsi06yfpNHpHFWTVPaNqeWg,125
|
|
|
13
13
|
imicpe/optim/pnnDataset.py,sha256=PFh5u0SXx761O6N6vVfWfEDmzmCm87eYzAL7mWHBRrw,3971
|
|
14
14
|
imicpe/optim/pnnTrainer.py,sha256=3ygh9XwFJN7jMPsEG3LHLa8sgBbDd9Yu_QWg4ZtM_DM,4571
|
|
15
15
|
imicpe/optim/pnnUtils.py,sha256=9ImcZboIXWEzMC2_16BgzEImD9SqtjfU_xlDmPr2mMk,1600
|
|
16
|
-
imicpe
|
|
17
|
-
imicpe
|
|
18
|
-
imicpe
|
|
19
|
-
imicpe
|
|
20
|
-
imicpe
|
|
16
|
+
imicpe/reco3D/__init__.py,sha256=YzgaV2l0hAwQG-sMStEppCN7oAIhyXCnlGy2yayJQ7A,140
|
|
17
|
+
imicpe/reco3D/angles.py,sha256=lv9I0_YBJTCWoesu7dHuAFHqRpY6gmlj9CmDskAri4E,3448
|
|
18
|
+
imicpe/reco3D/calculPose.py,sha256=QHqBESjWa6JpTQycRSGOZEu6BWRfFF5o7O96IsPPruw,9262
|
|
19
|
+
imicpe/reco3D/evalTrajecto.py,sha256=ICdXBlDBJmLC6uFPvFFd5qmWm2F7STx7i-bD1w4RB-w,6770
|
|
20
|
+
imicpe/reco3D/vue3D.py,sha256=_5upl5rJ-LYRbk4Dk2G6qZYnmPvKLaJYgL3wnU3it6o,7525
|
|
21
|
+
imicpe-1.0.8.1.dist-info/licenses/LICENSE,sha256=ACwmltkrXIz5VsEQcrqljq-fat6ZXAMepjXGoe40KtE,1069
|
|
22
|
+
imicpe-1.0.8.1.dist-info/METADATA,sha256=7UaN3kwzv-PQguP6UUEbmQSRswcgdjRDfrDHS--zP5I,1341
|
|
23
|
+
imicpe-1.0.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
imicpe-1.0.8.1.dist-info/top_level.txt,sha256=6_gSXCYolzjXHaIDeAsZ_M3nLXdqrMKt48XCz3reJc0,7
|
|
25
|
+
imicpe-1.0.8.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|