728x90
1. 개요
카메라를 정의하는데 필요한 속성 정보는 이전에 설명한 내용과 같고 이를 설명하는데 있어 개념적으로 헷갈릴 수 있는 부분을 보충하기 위한 내용을 작성한다.
[VTK] Camera 제어하기 — 로봇을 품은 개발자 이야기 (tistory.com)
카메라의 속성에서 가장 중요한 것은 Position 과 Focal point이다. 이는 Camera가 바라보는 방향 벡터를 정의한다.
그리고 Viewup 을 통해서 카메라의 orientation을 정의한다.
그리고 카메라에 그려지는 영역은 Clipping Range가 정의하고 Parallel 이 on이면 Scale 값 Off이면 View angle 에 의해서 카메라에 보여지는 영역이 정의가 된다.
즉, focal point의 위치에 있는 prop 이 그려지는 것이 아닌다..(난 이걸 잘못 이해했다..)
2. 카메라 비교
아래의 이미지에서 좌측은 카메라를 통해 보는 image plane이고 오른쪽은 Camera actor 의 영역과 image outline을 함께 그린 투시뷰이다.
Camera Parallel View
Camera Eye View
3. 구현코드
[TestFixture]
public class TestCameraViewInfo : TestRenderWindow
{
public string DirPath = VisualStudioProvider.GetPathInSolution(@"TestData");
private vtkImageMapToWindowLevelColors imageMap = vtkImageMapToWindowLevelColors.New();
private vtkImageSliceMapper sliceMapper = vtkImageSliceMapper.New();
private vtkInteractorStyleImage imageStyle = vtkInteractorStyleImage.New();
private vtkImageActor imageActor = vtkImageActor.New();
private vtkRenderer perspRenderer = vtkRenderer.New();
[Test]
public void TestCameraModelComparison()
{
EnableDrawAxes = false;
// Multi viewport 구성
renderWindow.SetSize(800, 400);
renderer.SetViewport(0, 0, 0.5, 1);
perspRenderer.SetViewport(0.5, 0, 1, 1);
perspRenderer.SetBackground(VtkColorUtils.GetNamedColor3d("SkyBlue").ToIntPtr());
renderWindow.AddRenderer(perspRenderer);
// 데이터 로드
var filePath = Path.Combine(DirPath, "head.vti");
var reader = vtkXMLImageDataReader.New();
reader.SetFileName(filePath);
reader.Update();
// Image data, mapper, actor 구성
var imageData = reader.GetOutput();
imageMap.SetLevel(100);
imageMap.SetWindow(250);
imageMap.SetInputData(imageData);
sliceMapper.SetInputConnection(imageMap.GetOutputPort());
sliceMapper.SetSliceNumber(0);
imageActor.SetMapper(sliceMapper);
renderer.AddActor(imageActor);
perspRenderer.AddActor(imageActor);
// Image data outline 그리기
vtkOutlineFilter outline = vtkOutlineFilter.New();
outline.SetInputData(imageData);
var outlineMapper = vtkPolyDataMapper.New();
outlineMapper.SetInputConnection(outline.GetOutputPort());
var outlineActor = vtkActor.New();
outlineActor.SetMapper(outlineMapper);
renderer.AddActor(outlineActor);
perspRenderer.AddActor(outlineActor);
// camera model 정의
var rendererCam = renderer.GetActiveCamera();
renderer.ResetCamera();
rendererCam.ParallelProjectionOff();
// 메인 renderer의 camera actor 생성
var camera = vtkCamera.New();
var cameraActor = vtkCameraActor.New();
cameraActor.SetCamera(camera);
cameraActor.GetProperty().SetColor(colors.GetColor3d("Black").ToIntPtr());
camera.DeepCopy(rendererCam);
// perspective renderer 의 camera 설정
var perspCam = perspRenderer.GetActiveCamera();
perspRenderer.AddActor(cameraActor);
perspRenderer.ResetCamera();
perspCam.SetPosition(0, 0, 1000);
perspCam.SetFocalPoint(0, 0, 0);
perspCam.SetViewUp(0, 1, 0);
perspCam.Azimuth(30);
perspCam.Elevation(30);
perspRenderer.ResetCamera();
// 이벤트 정의
renderer.StartEvt += Renderer_StartEvt;
imageStyle.LeftButtonPressEvt += ImageStyle_LeftButtonPressEvt;
imageStyle.LeftButtonReleaseEvt += ImageStyle_LeftButtonReleaseEvt;
imageStyle.MouseMoveEvt += ImageStyle_MouseMoveEvt;
imageStyle.CharEvt += ImageStyle_CharEvt;
imageStyle.MouseWheelForwardEvt += ImageStyle_MouseWheelForwardEvt;
imageStyle.MouseWheelBackwardEvt += ImageStyle_MouseWheelBackwardEvt;
interactor.SetInteractorStyle(imageStyle);
// Annotation
annotation.SetText((int)VtkTextPosition.UpperRight, "<window>\n<level>");
annotation.SetText((int)VtkTextPosition.LowerRight, "<image>\n<slice>");
StringBuilder sb = new StringBuilder();
sb.AppendLine("[CameraInfo]");
sb.AppendLine($"Clipping : {rendererCam.GetClippingRange().ToStringJoin(",", "F2")}");
sb.AppendLine($"FocalPoint: {rendererCam.GetFocalPoint().ToStringJoin(",", "F2")}");
sb.AppendLine($"Position : {rendererCam.GetPosition().ToStringJoin(",", "F2")}");
sb.AppendLine($"Parallel : {rendererCam.GetParallelProjection() == 1}, Scale: {rendererCam.GetParallelScale():F0}");
annotation.SetText((int) VtkTextPosition.UpperLeft, sb.ToString());
}
private void Renderer_StartEvt(vtkObject sender, vtkObjectEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"SliceOrigin: {imageActor.GetMapper().GetSlicePlane().GetOrigin().ToStringJoin(",", "F2")}");
annotation.SetText((int) VtkTextPosition.LowerEdge, sb.ToString());
}
private void ImageStyle_MouseWheelBackwardEvt(vtkObject sender, vtkObjectEventArgs e)
{
sliceMapper.SetSliceNumber(sliceMapper.GetSliceNumber() - 1);
// renderer.ResetCamera();
renderWindow.Render();
}
private void ImageStyle_MouseWheelForwardEvt(vtkObject sender, vtkObjectEventArgs e)
{
sliceMapper.SetSliceNumber(sliceMapper.GetSliceNumber() + 1);
// renderer.ResetCamera();
renderWindow.Render();
}
private void ImageStyle_CharEvt(vtkObject sender, vtkObjectEventArgs e)
{
var style = (vtkInteractorStyleImage) sender;
var keySym = style.GetInteractor().GetKeySym();
if (keySym == "r")
{
imageMap.SetWindow(255);
imageMap.SetLevel(127);
imageMap.Update();
renderWindow.Render();
}
}
private bool winLevelControl = false;
private int[] eventPos;
private void ImageStyle_LeftButtonPressEvt(vtkObject sender, vtkObjectEventArgs e)
{
var style = (vtkInteractorStyleImage) sender;
eventPos = style.GetInteractor().GetEventPosition();
winLevelControl = true;
}
private void ImageStyle_LeftButtonReleaseEvt(vtkObject sender, vtkObjectEventArgs e)
{
winLevelControl = false;
}
private void ImageStyle_MouseMoveEvt(vtkObject sender, vtkObjectEventArgs e)
{
var style = (vtkInteractorStyleImage) sender;
var curPos = style.GetInteractor().GetEventPosition();
if (winLevelControl)
{
var befWindow = imageMap.GetWindow();
var befLevel = imageMap.GetLevel();
var scale = 0.2;
var deltaWindow = (curPos[0] - eventPos[0]) * scale;
var deltaLevel = (curPos[1] - eventPos[1]) * scale;
if (Math.Abs(deltaLevel) >= Math.Abs(deltaWindow))
{
deltaWindow = 0;
imageMap.SetLevel(befLevel + deltaLevel);
}
else
{
deltaLevel = 0;
imageMap.SetWindow(befWindow + deltaWindow);
}
imageMap.Update();
renderWindow.Render();
}
}
728x90
728x90