3차원 공간에서의 좌표계 변환을 하기 위해서는 4x4 Matrix를 사용하게 된다. 이를 Transformation 에서는 Homogeneous Transformation 이라고 명칭한다.
VTK 에서 vtkHomogeneousTransform과 vtkLinearTransform 을 abstract 클래스로 아래와 같이 정의하고 있다.
vtkHomogeneousTransform을 상속한 하위 클래스들은 각 클래스의 명칭에 맞게 그 기능들이 최적화 되어 있다.
vtkMatrixToHomogeneousTransform / TestMatrixToLinearTransform
- vtkMatrix4x4를 SetInput으로 입력받아 Transform 객체를 생성한다.
- 두개의 클래스는 각각의 하위 클래스에 입력데이터로 사용 될 수 있다.
- 또는 vtkTransformPolyDataFilter 에 proxy 데이터로 사용하기 위한 목적으로도 사용될 수 있다.
vtkMatrix4x4 mat = vtkMatrix4x4.New();
mat.Identity();
mat.SetElement(0, 3, 100);
mat.SetElement(1, 3, 200);
mat.SetElement(2, 3, 300);
// LinearTransform 생성
vtkMatrixToHomogeneousTransform matToHmXfm = vtkMatrixToHomogeneousTransform.New();
matToHmXfm.SetInput(mat);
// LinearTransform 생성
vtkMatrixToLinearTransform matToLinXfm = vtkMatrixToLinearTransform.New();
matToLinXfm.SetInput(mat);
vtkSphereSource source = vtkSphereSource.New();
source.SetRadius(10);
// PolyDataFilter의 변환 Transform 데이터로 적용
vtkTransformPolyDataFilter xfmPdFilter = vtkTransformPolyDataFilter.New();
xfmPdFilter.SetInputConnection(source.GetOutputPort());
xfmPdFilter.SetTransform(matToLinXfm);
// HomogeneousTransform -> Perspecetive Transform 입력
vtkPerspectiveTransform perspXfm = vtkPerspectiveTransform.New();
perspXfm.SetInput(matToHmXfm);
// LinearTransform -> Transform 입력
vtkTransform xfm = vtkTransform.New();
xfm.SetInput(matToLinXfm);
vtkHomogeneousTransform vs. vtkLinearTransform
두개의 Transform 은 각각의 구현되어 있는 기능이 다르며 구현 클래스에 따라 내부 계산이 최적화되어 진다.
TransformVector 는 Rotation matrix 성분을 이용하여 vector의 변환을 계산한다.
TransformNormal 은 Rotation matrix의 normal 성분을 이용하여 vector를 변환한다.
TransformPoints 는 Homogeneous Matrix를 이용하여 Point 위치 값을 변환한다.
vtkTransform vs. vtkPerspectiveTransform
두개의 Transform 은 Matrix 곱을 구현하기 위해 내부적으로 concatenate를 구현하여 순차적으로 입력되는 Translate, Rotate, Scale 명령을 matrix로 저장하여 계산한다.
Concatenated matrix 들은 PreMultiply 를 수행하도록 하지만 PostMultiply로 변경할 수도 있다.
double[] trnl = new double[] {0, 10, 0};
// A.matmul(B) = A*B
// 상대 좌표계 HomogenousTransformation
vtkTransform xfm1 = vtkTransform.New();
xfm1.Identity();
xfm1.Translate(trnl.ToIntPtr());
xfm1.RotateX(180);
xfm1.GetPosition()[1].ShouldBe(10);
// A.matmul(B) = B*A
// 절대 좌표계 HomogenousTransformation
vtkTransform xfm2 = vtkTransform.New();
xfm2.PostMultiply();
xfm2.Identity();
xfm2.Translate(trnl.ToIntPtr());
xfm2.RotateX(180);
xfm2.GetPosition()[1].ShouldBe(-10);
vtkIdentityTransform
클래스 이름 그대로 Identity Matrix를 수행한다.
vtkLandmarkTransform
Source points, Target points 간의 matching 되는 transformation 계산한다.
var colors = vtkNamedColors.New() ;
// An aid to orient the view of the created data.
var linesPolyData = vtkPolyData.New();
VtkCoordUtils.AxesLinesPolyData(linesPolyData);
var sourcePoints = vtkPoints.New() ;
var sourcePoint1 = new[] { 1.0, 0.0, 0.0 };
sourcePoints.InsertNextPoint(sourcePoint1.ToFloatIntPtr());
var sourcePoint2 = new[] { 0.0, 1.0, 0.0 };
sourcePoints.InsertNextPoint(sourcePoint2.ToFloatIntPtr());
var sourcePoint3 = new[]{ 0.0, 0.0, 1.0 };
sourcePoints.InsertNextPoint(sourcePoint3.ToFloatIntPtr());
var targetPoints = vtkPoints.New() ;
var targetPoint1 = new[] { 0.0, 0.0, 1.1 };
targetPoints.InsertNextPoint(targetPoint1.ToFloatIntPtr());
var targetPoint2 = new[] { 0.0, 1.02, 0.0 };
targetPoints.InsertNextPoint(targetPoint2.ToFloatIntPtr());
var targetPoint3 = new[] { -1.11, 0.0, 0.0 };
targetPoints.InsertNextPoint(targetPoint3.ToFloatIntPtr());
// Setup the transform
var landmarkTransform = vtkLandmarkTransform.New();
landmarkTransform.SetSourceLandmarks(sourcePoints);
landmarkTransform.SetTargetLandmarks(targetPoints);
landmarkTransform.SetModeToRigidBody();
landmarkTransform.Update(); // should this be here?
var source = vtkPolyData.New();
source.SetPoints(sourcePoints);
var target = vtkPolyData.New();
target.SetPoints(targetPoints);
var sourceGlyphFilter = vtkVertexGlyphFilter.New() ;
sourceGlyphFilter.SetInputData(source);
sourceGlyphFilter.Update();
var targetGlyphFilter = vtkVertexGlyphFilter.New() ;
targetGlyphFilter.SetInputData(target);
targetGlyphFilter.Update();
var transformFilter = vtkTransformPolyDataFilter.New() ;
transformFilter.SetInputConnection(sourceGlyphFilter.GetOutputPort());
transformFilter.SetTransform(landmarkTransform);
transformFilter.Update();
// Display the transformation matrix that was computed
vtkMatrix4x4 mat = landmarkTransform.GetMatrix();
TestContext.WriteLine($"Matrix: {mat}");
// Visualize
// Map the points to spheres
var sourceActor = VtkGlyphUtils.PointToGlyph(sourceGlyphFilter.GetOutput().GetPoints(), 0.03);
sourceActor.GetProperty().SetColor(colors.GetColor3d("Red").ToIntPtr());
var targetActor = VtkGlyphUtils.PointToGlyph(targetGlyphFilter.GetOutput().GetPoints(), 0.03);
targetActor.GetProperty().SetColor(colors.GetColor3d("Lime").ToIntPtr());
var solutionActor = VtkGlyphUtils.PointToGlyph(transformFilter.GetOutput().GetPoints(), 0.03);
solutionActor.GetProperty().SetColor(colors.GetColor3d("Blue").ToIntPtr());
var axesMapper = vtkPolyDataMapper.New();
axesMapper.SetInputData(linesPolyData);
var axesActor = vtkActor.New();
axesActor.SetMapper(axesMapper);
axesActor.GetProperty().SetLineWidth(1);
// Create a renderer, render window, and interactor
var renderer = vtkRenderer.New() ;
var renderWindow = vtkRenderWindow.New() ;
renderWindow.AddRenderer(renderer);
var renderWindowInteractor = vtkRenderWindowInteractor.New() ;
renderWindowInteractor.SetRenderWindow(renderWindow);
renderWindow.SetWindowName("LandmarkTransform");
// Add the actor to the scene
renderer.AddActor(sourceActor);
renderer.AddActor(targetActor);
renderer.AddActor(solutionActor);
renderer.AddActor(axesActor);
renderer.SetBackground(colors.GetColor3d("SlateGray").ToIntPtr());
// Render and interact
renderWindow.Render();
renderWindowInteractor.Start();