blob: 64ec51a6629dd8a6fb072813381d3a22bfe45dc3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pierre Allard,
* Regent L'Archeveque - initial API and implementation
*
* SPDX-License-Identifier: EPL-1.0
*
*******************************************************************************/
package org.eclipse.apogy.common.geometry.data3d.impl;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.vecmath.Matrix4d;
import org.eclipse.apogy.common.geometry.data3d.ApogyCommonGeometryData3DFactory;
import org.eclipse.apogy.common.geometry.data3d.ApogyCommonGeometryData3DPackage;
import org.eclipse.apogy.common.geometry.data3d.CartesianCoordinatesSet;
import org.eclipse.apogy.common.geometry.data3d.CartesianPositionCoordinates;
import org.eclipse.apogy.common.geometry.data3d.PositionMarker;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EObjectResolvingEList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import Jama.Matrix;
import Jama.SingularValueDecomposition;
public class RigidBodyPoseTrackerCustomImpl extends RigidBodyPoseTrackerImpl {
private static final Logger Logger = LoggerFactory.getLogger(RigidBodyPoseTrackerImpl.class);
@Override
public EList<PositionMarker> getPositionMarkersAtOrigin() {
if (this.positionMarkersAtOrigin == null) {
this.positionMarkersAtOrigin = new EObjectResolvingEList<PositionMarker>(PositionMarker.class, this,
ApogyCommonGeometryData3DPackage.RIGID_BODY_POSE_TRACKER__POSITION_MARKERS_AT_ORIGIN);
}
EList<PositionMarker> localCopy = this.positionMarkersAtOrigin;
return localCopy;
}
public Matrix4d computeTransformation(List<PositionMarker> listOfMarkerPositionsToTrack) throws Exception {
Matrix4d output = new Matrix4d();
if (listOfMarkerPositionsToTrack.size() < 3) {
throw new IllegalArgumentException("At least 3 position markers to track are needed");
}
if (this.positionMarkersAtOrigin.size() < 3) {
throw new IllegalArgumentException("At least 3 position markers at origin are needed");
}
CartesianCoordinatesSet markerPositionsAtOrigin = ApogyCommonGeometryData3DFactory.eINSTANCE
.createCartesianCoordinatesSet();
CartesianCoordinatesSet markerPositionsToTrack = ApogyCommonGeometryData3DFactory.eINSTANCE
.createCartesianCoordinatesSet();
CartesianPositionCoordinates tmpPos1 = ApogyCommonGeometryData3DFactory.eINSTANCE
.createCartesianPositionCoordinates();
CartesianPositionCoordinates tmpPos2 = ApogyCommonGeometryData3DFactory.eINSTANCE
.createCartesianPositionCoordinates();
for (int i = 0; i < this.positionMarkersAtOrigin.size(); i++) {
for (int j = 0; j < listOfMarkerPositionsToTrack.size(); j++) {
if (this.positionMarkersAtOrigin.get(i).getIdentifier() == listOfMarkerPositionsToTrack.get(j)
.getIdentifier()) {
tmpPos1 = ApogyCommonGeometryData3DFactory.eINSTANCE.createCartesianPositionCoordinates();
tmpPos1.setX(this.positionMarkersAtOrigin.get(i).getX());
tmpPos1.setY(this.positionMarkersAtOrigin.get(i).getY());
tmpPos1.setZ(this.positionMarkersAtOrigin.get(i).getZ());
markerPositionsAtOrigin.getPoints().add(tmpPos1);
tmpPos2 = ApogyCommonGeometryData3DFactory.eINSTANCE.createCartesianPositionCoordinates();
tmpPos2.setX(listOfMarkerPositionsToTrack.get(j).getX());
tmpPos2.setY(listOfMarkerPositionsToTrack.get(j).getY());
tmpPos2.setZ(listOfMarkerPositionsToTrack.get(j).getZ());
markerPositionsToTrack.getPoints().add(tmpPos2);
}
}
}
Matrix homogenousTransformationMatrix = compute3DRigidBodyTransformationBetweenTwo3DDatasets(
markerPositionsAtOrigin, markerPositionsToTrack);
// Convert Matrix to Matrix4d for output
for (int i = 0; i <= 3; i++) {
for (int j = 0; j <= 3; j++) {
output.setElement(i, j, homogenousTransformationMatrix.get(i, j));
}
}
return output;
}
public void addPositionMarkers(List<PositionMarker> marker) throws Exception {
if (this.positionMarkersAtOrigin == null) {
this.positionMarkersAtOrigin = new EObjectResolvingEList<PositionMarker>(PositionMarker.class, this,
ApogyCommonGeometryData3DPackage.RIGID_BODY_POSE_TRACKER__POSITION_MARKERS_AT_ORIGIN);
}
for (int i = 0; i < marker.size(); i++) {
this.positionMarkersAtOrigin.add(marker.get(i));
}
// throw new UnsupportedOperationException();
}
public void removePositionMarkers(List<PositionMarker> markers) {
List<PositionMarker> localCopyMarker = markers;
for (int i = 0; i < localCopyMarker.size(); i++) {
for (int j = 0; j < this.positionMarkersAtOrigin.size(); j++) {
if (localCopyMarker.get(i).getIdentifier() == this.positionMarkersAtOrigin.get(j).getIdentifier()) {
this.positionMarkersAtOrigin.remove(j);
}
}
}
throw new UnsupportedOperationException();
}
private static Matrix compute3DRigidBodyTransformationBetweenTwo3DDatasets(CartesianCoordinatesSet p_0,
CartesianCoordinatesSet p) {
Matrix homogenousTransformationMatrix = new Matrix(4, 4);
// Compute the centroid of datasets
Matrix p_centroid = computeCentroidDataSet(p);
Matrix p_0_centroid = computeCentroidDataSet(p_0);
// Compute Rotation
Matrix rotationMatrix = computeRotationMatrixUsingSVD(p_0, p_0_centroid, p, p_centroid);
// Compute Translation
Matrix translationMatrix = computeTranslationMatrix(rotationMatrix, p_0_centroid, p_centroid);
// Pack rotation and translation components in an homogenous matrix
homogenousTransformationMatrix.setMatrix(0, 2, 0, 2, rotationMatrix);
homogenousTransformationMatrix.setMatrix(0, 2, 3, 3, translationMatrix);
homogenousTransformationMatrix.set(3, 3, 1.0);
return homogenousTransformationMatrix;
}
private static Matrix computeTranslationMatrix(Matrix rotationMatrix, Matrix p_0_centroid, Matrix p_centroid) {
Matrix translationMatrix = new Matrix(3, 1);
translationMatrix = p_centroid.minus(rotationMatrix.times(p_0_centroid));
return translationMatrix;
}
private static Matrix computeRotationMatrixUsingSVD(CartesianCoordinatesSet p_0, Matrix P_0_centroid,
CartesianCoordinatesSet p, Matrix P_centroid) {
// Convert input from CartesianCoordinatePosition to Matrix 3x1 and remove
// centroid
java.util.List<Matrix> mP_0 = new ArrayList<Matrix>();
java.util.List<Matrix> mP = new ArrayList<Matrix>();
Matrix tmpMat3x1 = new Matrix(3, 1);
for (int i = 0; i < p_0.getPoints().size(); i++) {
tmpMat3x1 = convertCartesianCoordinatePositionToMatrix3x1(p_0.getPoints().get(i)); // convert to Matrix
tmpMat3x1 = tmpMat3x1.minus(P_0_centroid); // offset the centroid
mP_0.add(tmpMat3x1);
tmpMat3x1 = convertCartesianCoordinatePositionToMatrix3x1(p.getPoints().get(i)); // convert to Matrix
tmpMat3x1 = tmpMat3x1.minus(P_centroid); // offset the centroid
mP.add(tmpMat3x1);
}
// Correlation Matrix H
Matrix H = new Matrix(3, 3);
Matrix tmp3x3 = new Matrix(3, 3);
for (int i = 0; i < mP.size(); i++) {
tmp3x3 = mP_0.get(i).times(mP.get(i).transpose());
H = H.plus(tmp3x3);
}
// SVD computation
SingularValueDecomposition svg = H.svd();
Matrix rotationMatrix = new Matrix(3, 3);
Matrix U = new Matrix(3, 3);
Matrix V = new Matrix(3, 3);
U = svg.getU();
V = svg.getV();
rotationMatrix = V.times(U.transpose());
double tol = 1e-4;
// if det(rotationMatrix)== -1, it means the dataset is planar or the amount of
// noise is large
if (Math.abs(rotationMatrix.det() + 1) < tol) {
Matrix V_prime = new Matrix(3, 3);
V_prime = V;
V_prime.set(0, 2, -1 * V.get(0, 2));
V_prime.set(1, 2, -1 * V.get(1, 2));
V_prime.set(2, 2, -1 * V.get(2, 2));
rotationMatrix = V_prime.times(U.transpose());
}
return rotationMatrix;
}
private static Matrix computeCentroidDataSet(CartesianCoordinatesSet p) {
Matrix centroid = new Matrix(3, 1);
double sumX = 0;
double sumY = 0;
double sumZ = 0;
for (int i = 0; i < p.getPoints().size(); i++) {
sumX += p.getPoints().get(i).getX();
sumY += p.getPoints().get(i).getY();
sumZ += p.getPoints().get(i).getZ();
}
centroid.set(0, 0, sumX / p.getPoints().size());
centroid.set(1, 0, sumY / p.getPoints().size());
centroid.set(2, 0, sumZ / p.getPoints().size());
return centroid;
}
private static Matrix convertCartesianCoordinatePositionToMatrix3x1(CartesianPositionCoordinates P) {
Matrix mP = new Matrix(3, 1);
mP.set(0, 0, P.getX());
mP.set(1, 0, P.getY());
mP.set(2, 0, P.getZ());
return mP;
}
@Override
@SuppressWarnings("unchecked")
public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException {
switch (operationID) {
case ApogyCommonGeometryData3DPackage.RIGID_BODY_POSE_TRACKER___COMPUTE_TRANSFORMATION__ELIST:
try {
return computeTransformation((EList<PositionMarker>) arguments.get(0));
} catch (Exception e) {
Logger.error(e.getMessage(), e);
}
case ApogyCommonGeometryData3DPackage.RIGID_BODY_POSE_TRACKER___ADD_POSITION_MARKERS__LIST:
try {
addPositionMarkers((List<PositionMarker>) arguments.get(0));
} catch (Exception e) {
Logger.error(e.getMessage(), e);
}
return null;
case ApogyCommonGeometryData3DPackage.RIGID_BODY_POSE_TRACKER___REMOVE_POSITION_MARKERS__LIST:
removePositionMarkers((List<PositionMarker>) arguments.get(0));
return null;
}
return super.eInvoke(operationID, arguments);
}
} // RigidBodyPoseTrackerImpl