여러 방향에서 만드는 MIP 의 속도를 향상시켜보자.
2018, Jul 24
속도를 향상시키기 전에 현재 얼마나 걸리는 지 측정해보자.
VolumeRendererDoc.cpp
...
/// 다음의 변수를 선언한다.
LARGE_INTEGER Frequency;
LARGE_INTEGER BeginTime;
LARGE_INTEGER Endtime;
...
/// 성능을 측정하고자 하는 부분에 코드를 추가하고
/// 시간을 출력한다.
void CVolumeRendererDoc::OnMiprenderingAnydirection()
{
// TODO: 여기에 명령 처리기 코드를 추가합니다.
int img_width = 256;
int img_height = 256;
printf("MIP AnyDirection \n");
shared_ptr<unsigned char> image =
shared_ptr<unsigned char>(new unsigned char[img_width*img_height]);
memset(image.get(), 0, sizeof(unsigned char)*img_width*img_height);
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&BeginTime);
m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height);
QueryPerformanceCounter(&Endtime);
int elapsed = Endtime.QuadPart - BeginTime.QuadPart;
double duringtime = (double)elapsed / (double)Frequency.QuadPart;
printf("MIP AnyDirection time : %f\n", duringtime);
CVolumeRendererView* pView =
(CVolumeRendererView*)((CMainFrame*)(AfxGetApp()->m_pMainWnd))->GetActiveView();
pView->SetDrawImage(image.get(), img_width, img_height, 1);
pView->OnInitialUpdate();
printf("MIP AnyDirection End \n");
}
내 컴퓨터에서 시간은 약 14.04 초 로 나온다.
시간을 줄여보자 시간이 가장 많이 잡아먹는 부분은 함수 RenderMIPAnyDirection에서 3중반복문 안의 view_vector 만큼 진행되면서 1000번 반복하는 부분이다. 이 부분을 적당한 값만큼 반복하는 것으로 만들 수 있다면 걸리는 시간이 줄어들 것이다.
cur_coord = cur_coord + k*view_vector 이라면 k 값을 구하여 반복횟수를 줄일 수 있을 것이다.
- x,y,z 각각 cur_coord 가 volume에 접근하는 k 값을 구한다.
- x,y,z 각각 cur_coord 가 volume에서 나가는 k 값을 구한다.
- 접근할 때의 k 값은 1의 max 값이다.
- 나갈 때의 k 값은 2의 min 값이다.
이를 구현해보자.
Renderer.cpp
void Renderer::GetRayBound(float t[2], float3 start_coord, float3 view_vector)
{
const float EPS = 0.00001f;
/// 1 2를 구한다.
float kx[2] = { -20000, 20000 }, ky[2] = { -20000, 20000 }, kz[2] = { -20000, 20000 };
if (fabs((float)view_vector.x) > EPS)
{
kx[0] = (0.f - start_coord.x) / view_vector.x;
kx[1] = (m_pVolume->GetWidth() - start_coord.x) / view_vector.x;
if (kx[0] > kx[1])
{
/// in > out
float temp = kx[0];
kx[0] = kx[1];
kx[1] = temp;
}
}
if (fabs((float)view_vector.y) > EPS)
{
ky[0] = (0.f - start_coord.y) / view_vector.y;
ky[1] = (m_pVolume->GetHeight() - start_coord.y) / view_vector.y;
if (ky[0] > ky[1])
{
/// in > out
float temp = ky[0];
ky[0] = ky[1];
ky[1] = temp;
}
}
if (fabs((float)view_vector.z) > EPS)
{
kz[0] = (0.f - start_coord.z) / view_vector.z;
kz[1] = (m_pVolume->GetDepth() - start_coord.z) / view_vector.z;
if (kz[0] > kz[1])
{
/// in > out
float temp = kz[0];
kz[0] = kz[1];
kz[1] = temp;
}
}
t[0] = __max(__max(kx[0], ky[0]), kz[0]);
t[1] = __min(__min(kx[1], ky[1]), kz[1]);
t[0] = t[0] + 0.01f;
t[1] = t[1] - 0.01f;
}
RenderMIPAnyDirection 함수도 수정한다.
...
for (int j = 0; j < img_height; j++)
{
///시작 픽셀의 3차원 좌표 계산
float3 cur_coord = eye_coord +
x_vector * (i-img_width/2) + y_vector * (j-img_height/2);
/// volume 에 만나고 나가는 적절한 k값 범위를 구함
float t[2] = { 0.f, 0.f };
GetRayBound(t, cur_coord, view_vector);
unsigned char max_value = 0;
for (float k = t[0]; k < t[1]; k += 1.f)
{
/// 현재 좌표를 다시 구함
float3 adv_coord = cur_coord + view_vector * k;
///진행 픽셀의 현재 위치가 볼륨 바운더리 안에 들어왔다면
if (adv_coord.x >= 0.f && adv_coord.x < vol_width-1 &&
adv_coord.y >= 0.f && adv_coord.y < vol_height-1 &&
adv_coord.z >= 0.f && adv_coord.z < vol_depth-1)
{
///해당 위치에서의 볼륨 복셀을 가져옴
float voxel =
m_pVolume->GetVoxel(adv_coord.x, adv_coord.y, adv_coord.z);
...
실행하면 결과는 약 10.55 초 가 나온다. 약 4초 줄어든 것을 확인할 수 있다.