/*========================================================================= Program: Visualization Toolkit Module: vtkLandmarkTransform.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkLandmarkTransform.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkPoints.h" vtkStandardNewMacro(vtkLandmarkTransform); //------------------------------------------------------------------------------ vtkLandmarkTransform::vtkLandmarkTransform() { this->Mode = VTK_LANDMARK_SIMILARITY; this->SourceLandmarks = nullptr; this->TargetLandmarks = nullptr; } //------------------------------------------------------------------------------ vtkLandmarkTransform::~vtkLandmarkTransform() { if (this->SourceLandmarks) { this->SourceLandmarks->Delete(); } if (this->TargetLandmarks) { this->TargetLandmarks->Delete(); } } //------------------------------------------------------------------------------ void vtkLandmarkTransform::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << "Mode: " << this->GetModeAsString() << "\n"; os << "SourceLandmarks: " << this->SourceLandmarks << "\n"; if (this->SourceLandmarks) { this->SourceLandmarks->PrintSelf(os, indent.GetNextIndent()); } os << "TargetLandmarks: " << this->TargetLandmarks << "\n"; if (this->TargetLandmarks) { this->TargetLandmarks->PrintSelf(os, indent.GetNextIndent()); } } //------------------------------------------------------------------------------ // Update the 4x4 matrix. Updates are only done as necessary. void vtkLandmarkTransform::InternalUpdate() { vtkIdType i; int j; if (this->SourceLandmarks == nullptr || this->TargetLandmarks == nullptr) { this->Matrix->Identity(); return; } // --- compute the necessary transform to match the two sets of landmarks --- /* The solution is based on Berthold K. P. Horn (1987), "Closed-form solution of absolute orientation using unit quaternions," Journal of the Optical Society of America A, 4:629-642 */ // Original python implementation by David G. Gobbi const vtkIdType N_PTS = this->SourceLandmarks->GetNumberOfPoints(); if (N_PTS != this->TargetLandmarks->GetNumberOfPoints()) { vtkErrorMacro("Update: Source and Target Landmarks contain a different number of points"); return; } // -- if no points, stop here if (N_PTS == 0) { this->Matrix->Identity(); return; } // -- find the centroid of each set -- double source_centroid[3] = { 0, 0, 0 }; double target_centroid[3] = { 0, 0, 0 }; double p[3]; for (i = 0; i < N_PTS; i++) { this->SourceLandmarks->GetPoint(i, p); source_centroid[0] += p[0]; source_centroid[1] += p[1]; source_centroid[2] += p[2]; this->TargetLandmarks->GetPoint(i, p); target_centroid[0] += p[0]; target_centroid[1] += p[1]; target_centroid[2] += p[2]; } source_centroid[0] /= N_PTS; source_centroid[1] /= N_PTS; source_centroid[2] /= N_PTS; target_centroid[0] /= N_PTS; target_centroid[1] /= N_PTS; target_centroid[2] /= N_PTS; // -- if only one point, stop right here if (N_PTS == 1) { this->Matrix->Identity(); this->Matrix->Element[0][3] = target_centroid[0] - source_centroid[0]; this->Matrix->Element[1][3] = target_centroid[1] - source_centroid[1]; this->Matrix->Element[2][3] = target_centroid[2] - source_centroid[2]; return; } // -- build the 3x3 matrix M -- double M[3][3]; double AAT[3][3]; for (i = 0; i < 3; i++) { AAT[i][0] = M[i][0] = 0.0F; // fill M with zeros AAT[i][1] = M[i][1] = 0.0F; AAT[i][2] = M[i][2] = 0.0F; } vtkIdType pt; double a[3], b[3]; double sa = 0.0F, sb = 0.0F; for (pt = 0; pt < N_PTS; pt++) { // get the origin-centred point (a) in the source set this->SourceLandmarks->GetPoint(pt, a); a[0] -= source_centroid[0]; a[1] -= source_centroid[1]; a[2] -= source_centroid[2]; // get the origin-centred point (b) in the target set this->TargetLandmarks->GetPoint(pt, b); b[0] -= target_centroid[0]; b[1] -= target_centroid[1]; b[2] -= target_centroid[2]; // accumulate the products a*T(b) into the matrix M for (i = 0; i < 3; i++) { M[i][0] += a[i] * b[0]; M[i][1] += a[i] * b[1]; M[i][2] += a[i] * b[2]; // for the affine transform, compute ((a.a^t)^-1 . a.b^t)^t. // a.b^t is already in M. here we put a.a^t in AAT. if (this->Mode == VTK_LANDMARK_AFFINE) { AAT[i][0] += a[i] * a[0]; AAT[i][1] += a[i] * a[1]; AAT[i][2] += a[i] * a[2]; } } // accumulate scale factors (if desired) sa += a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; sb += b[0] * b[0] + b[1] * b[1] + b[2] * b[2]; } // if source or destination is degenerate then only report // translation if (sa == 0.0 || sb == 0.0) { this->Matrix->Identity(); this->Matrix->Element[0][3] = target_centroid[0] - source_centroid[0]; this->Matrix->Element[1][3] = target_centroid[1] - source_centroid[1]; this->Matrix->Element[2][3] = target_centroid[2] - source_centroid[2]; return; } if (this->Mode == VTK_LANDMARK_AFFINE) { // AAT = (a.a^t)^-1 vtkMath::Invert3x3(AAT, AAT); // M = (a.a^t)^-1 . a.b^t vtkMath::Multiply3x3(AAT, M, M); // this->Matrix = M^t for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { this->Matrix->Element[i][j] = M[j][i]; } } } else { // compute required scaling factor (if desired) double scale = (double)sqrt(sb / sa); // -- build the 4x4 matrix N -- double Ndata[4][4]; double* N[4]; for (i = 0; i < 4; i++) { N[i] = Ndata[i]; N[i][0] = 0.0F; // fill N with zeros N[i][1] = 0.0F; N[i][2] = 0.0F; N[i][3] = 0.0F; } // on-diagonal elements N[0][0] = M[0][0] + M[1][1] + M[2][2]; N[1][1] = M[0][0] - M[1][1] - M[2][2]; N[2][2] = -M[0][0] + M[1][1] - M[2][2]; N[3][3] = -M[0][0] - M[1][1] + M[2][2]; // off-diagonal elements N[0][1] = N[1][0] = M[1][2] - M[2][1]; N[0][2] = N[2][0] = M[2][0] - M[0][2]; N[0][3] = N[3][0] = M[0][1] - M[1][0]; N[1][2] = N[2][1] = M[0][1] + M[1][0]; N[1][3] = N[3][1] = M[2][0] + M[0][2]; N[2][3] = N[3][2] = M[1][2] + M[2][1]; // -- eigen-decompose N (is symmetric) -- double eigenvectorData[4][4]; double *eigenvectors[4], eigenvalues[4]; eigenvectors[0] = eigenvectorData[0]; eigenvectors[1] = eigenvectorData[1]; eigenvectors[2] = eigenvectorData[2]; eigenvectors[3] = eigenvectorData[3]; vtkMath::JacobiN(N, 4, eigenvalues, eigenvectors); // the eigenvector with the largest eigenvalue is the quaternion we want // (they are sorted in decreasing order for us by JacobiN) double w, x, y, z; // first: if points are collinear, choose the quaternion that // results in the smallest rotation. if (eigenvalues[0] == eigenvalues[1] || N_PTS == 2) { double s0[3], t0[3], s1[3], t1[3]; this->SourceLandmarks->GetPoint(0, s0); this->TargetLandmarks->GetPoint(0, t0); this->SourceLandmarks->GetPoint(1, s1); this->TargetLandmarks->GetPoint(1, t1); double ds[3], dt[3]; double rs = 0, rt = 0; for (i = 0; i < 3; i++) { ds[i] = s1[i] - s0[i]; // vector between points rs += ds[i] * ds[i]; dt[i] = t1[i] - t0[i]; rt += dt[i] * dt[i]; } // normalize the two vectors rs = sqrt(rs); ds[0] /= rs; ds[1] /= rs; ds[2] /= rs; rt = sqrt(rt); dt[0] /= rt; dt[1] /= rt; dt[2] /= rt; // take dot & cross product w = ds[0] * dt[0] + ds[1] * dt[1] + ds[2] * dt[2]; x = ds[1] * dt[2] - ds[2] * dt[1]; y = ds[2] * dt[0] - ds[0] * dt[2]; z = ds[0] * dt[1] - ds[1] * dt[0]; double r = sqrt(x * x + y * y + z * z); double theta = atan2(r, w); // construct quaternion w = cos(theta / 2); if (r != 0) { r = sin(theta / 2) / r; x = x * r; y = y * r; z = z * r; } else // rotation by 180 degrees: special case { // rotate around a vector perpendicular to ds vtkMath::Perpendiculars(ds, dt, nullptr, 0); r = sin(theta / 2); x = dt[0] * r; y = dt[1] * r; z = dt[2] * r; } } else // points are not collinear { w = eigenvectors[0][0]; x = eigenvectors[1][0]; y = eigenvectors[2][0]; z = eigenvectors[3][0]; } // convert quaternion to a rotation matrix double ww = w * w; double wx = w * x; double wy = w * y; double wz = w * z; double xx = x * x; double yy = y * y; double zz = z * z; double xy = x * y; double xz = x * z; double yz = y * z; this->Matrix->Element[0][0] = ww + xx - yy - zz; this->Matrix->Element[1][0] = 2.0 * (wz + xy); this->Matrix->Element[2][0] = 2.0 * (-wy + xz); this->Matrix->Element[0][1] = 2.0 * (-wz + xy); this->Matrix->Element[1][1] = ww - xx + yy - zz; this->Matrix->Element[2][1] = 2.0 * (wx + yz); this->Matrix->Element[0][2] = 2.0 * (wy + xz); this->Matrix->Element[1][2] = 2.0 * (-wx + yz); this->Matrix->Element[2][2] = ww - xx - yy + zz; if (this->Mode != VTK_LANDMARK_RIGIDBODY) { // add in the scale factor (if desired) for (i = 0; i < 3; i++) { this->Matrix->Element[i][0] *= scale; this->Matrix->Element[i][1] *= scale; this->Matrix->Element[i][2] *= scale; } } } // the translation is given by the difference in the transformed source // centroid and the target centroid double sx, sy, sz; sx = this->Matrix->Element[0][0] * source_centroid[0] + this->Matrix->Element[0][1] * source_centroid[1] + this->Matrix->Element[0][2] * source_centroid[2]; sy = this->Matrix->Element[1][0] * source_centroid[0] + this->Matrix->Element[1][1] * source_centroid[1] + this->Matrix->Element[1][2] * source_centroid[2]; sz = this->Matrix->Element[2][0] * source_centroid[0] + this->Matrix->Element[2][1] * source_centroid[1] + this->Matrix->Element[2][2] * source_centroid[2]; this->Matrix->Element[0][3] = target_centroid[0] - sx; this->Matrix->Element[1][3] = target_centroid[1] - sy; this->Matrix->Element[2][3] = target_centroid[2] - sz; // fill the bottom row of the 4x4 matrix this->Matrix->Element[3][0] = 0.0; this->Matrix->Element[3][1] = 0.0; this->Matrix->Element[3][2] = 0.0; this->Matrix->Element[3][3] = 1.0; this->Matrix->Modified(); } //------------------------------------------------------------------------------ vtkMTimeType vtkLandmarkTransform::GetMTime() { vtkMTimeType result = this->vtkLinearTransform::GetMTime(); vtkMTimeType mtime; if (this->SourceLandmarks) { mtime = this->SourceLandmarks->GetMTime(); if (mtime > result) { result = mtime; } } if (this->TargetLandmarks) { mtime = this->TargetLandmarks->GetMTime(); if (mtime > result) { result = mtime; } } return result; } //------------------------------------------------------------------------------ void vtkLandmarkTransform::SetSourceLandmarks(vtkPoints* source) { if (this->SourceLandmarks == source) { return; } if (this->SourceLandmarks) { this->SourceLandmarks->Delete(); } source->Register(this); this->SourceLandmarks = source; this->Modified(); } //------------------------------------------------------------------------------ void vtkLandmarkTransform::SetTargetLandmarks(vtkPoints* target) { if (this->TargetLandmarks == target) { return; } if (this->TargetLandmarks) { this->TargetLandmarks->Delete(); } target->Register(this); this->TargetLandmarks = target; this->Modified(); } //------------------------------------------------------------------------------ void vtkLandmarkTransform::Inverse() { vtkPoints* tmp1 = this->SourceLandmarks; vtkPoints* tmp2 = this->TargetLandmarks; this->TargetLandmarks = tmp1; this->SourceLandmarks = tmp2; this->Modified(); } //------------------------------------------------------------------------------ vtkAbstractTransform* vtkLandmarkTransform::MakeTransform() { return vtkLandmarkTransform::New(); } //------------------------------------------------------------------------------ void vtkLandmarkTransform::InternalDeepCopy(vtkAbstractTransform* transform) { vtkLandmarkTransform* t = (vtkLandmarkTransform*)transform; this->SetMode(t->Mode); this->SetSourceLandmarks(t->SourceLandmarks); this->SetTargetLandmarks(t->TargetLandmarks); this->Modified(); }