3D 이미지 나뭇결무늬를 제거하자 - 1

2018, Sep 11    

이전 볼륨 렌더링 이미지는 나뭇결 무늬가 심했다. 이를 완화시키는 방법을 소개하고 적용해보자.

먼저 interpolation 방법이다. 이산적인 값으로 이루어져 있는 복셀값을 가중치를 이용하여 연속적인 값을 구하는 방법이다.

Volume.cpp

float Volume::GetVoxel(float x, float y, float z)
{
    /// tri - linear interploation
    int x_minus = x;
    int y_minus = y;
    int z_minus = z;
    int x_plus = x_minus + 1;
    int y_plus = y_minus + 1;
    int z_plus = z_minus + 1;
    float x_weight = x - static_cast<float>(x_minus);
    float y_weight = y - static_cast<float>(y_minus);
    float z_weight = z - static_cast<float>(z_minus);

    unsigned char coord_000 = GetVoxel(x_minus, y_minus, z_minus);
    unsigned char coord_100 = GetVoxel(x_plus,  y_minus, z_minus);
    unsigned char coord_010 = GetVoxel(x_minus,  y_plus, z_minus);
    unsigned char coord_110 = GetVoxel(x_plus,   y_plus, z_minus);
    unsigned char coord_001 = GetVoxel(x_minus, y_minus,  z_plus);
    unsigned char coord_101 = GetVoxel(x_plus,  y_minus,  z_plus);
    unsigned char coord_011 = GetVoxel(x_minus,  y_plus,  z_plus);
    unsigned char coord_111 = GetVoxel(x_plus,   y_plus,  z_plus);

    float inter000_100 = (1.f-x_weight)*coord_000 + x_weight*coord_100;
    float inter010_110 = (1.f-x_weight)*coord_010 + x_weight*coord_110;
    float inter2d_1 = (1.f-y_weight)*inter000_100 + y_weight*inter010_110;

    float inter001_101 = (1.f-x_weight)*coord_001 + x_weight*coord_101;
    float inter011_111 = (1.f-x_weight)*coord_011 + x_weight*coord_111;
    float inter2d_2 = (1.f-y_weight)*inter001_101 + y_weight*inter011_111;

    float inter3d_1 = (1.f - z_weight)*inter2d_1 + z_weight*inter2d_2;
    return inter3d_1;
}

이를 적용해 렌더링 해보면 아래 그림과 같다. 이전보다 나아졌다고 볼 수 없다.

bighead-tri-linear-interploation-with1D-pallette

나뭇결 무늬를 보다 효과적으로 제거하기 위해 기존의 1d Pallette 대신 2d Pallette 를 적용해보겠다.

기존에는 하나의 intensity 에 하나의 값을 가진 팔렛이었다면 2d Pallette 는 이전 intensity 와 현재 intensity 를 interpolation 하여

보다 경계를 부드럽게 만든 pallette 라고 볼 수 있다.

TransferFunction.cpp

void TransferFunction::SetColorAlpha2DPallette(int start_color[3], 
    int end_color[3], int start_alpha, int end_alpha)
{
    /// 클래스 멤버 변수 m_PalletteC2D[3], m_PalletteA2D 를 미리 선언함.
    for(int i=0; i<3; i++)
    {
        if(!m_PalletteC2D[i])
        {
            m_PalletteC2D[i] = shared_ptr<float>(new float[MAX_INTENSITY*MAX_INTENSITY]);
            memset(m_PalletteC2D[i], 0, sizeof(float)*MAX_INTENSITY*MAX_INTENSITY);
        }
    }

    if(m_PalletteA2D)
    {
        m_PalletteA2D = shared_ptr<float>(new float[MAX_INTENSITY * MAX_INTENSITY]);
        memset(m_PalletteA2D, 0, sizeof(float)*MAX_INTENSITY*MAX_INTENSITY);
    }

    for(int j=0; j<MAX_INTENSITY; j++)
    {
        for(int i=0; i<MAX_INTENSITY; i++)
        {
            float diff = 0.f;
            float color[3] = {0.f};
            float alpha = 0.f;

            if( i > j )
            {
                diff = i - j;
                for(int k=j; k<i; k++)
                {
                    float cur_blue = GetPalletteCValue(0,k);
                    float cur_green = GetPalletteCValue(1,k);
                    float cur_red = GetPalletteCValue(2,k);
                    float cur_alpha = GetPalleteAValue(k);

                    color[0] += cur_blue;
                    color[1] += cur_green;
                    color[2] += cur_red;
                    alpha += cur_alpha;
                }
            }
            else if(j > i)
            {
                diff = j - i;
                for(int k=i; k< j; k++)
                {
                    float cur_blue = GetPalletteCValue(0,k);
                    float cur_green = GetPalletteCValue(1,k);
                    float cur_red = GetPalletteCValue(2,k);
                    float cur_alpha = GetPalleteAValue(k);

                    color[0] += cur_blue;
                    color[1] += cur_green;
                    color[2] += cur_red;
                    alpha += cur_alpha;
                }
            }
            else
            {
                diff = 1.f;

                float cur_blue = GetPalletteCValue(0,k);
                float cur_green = GetPalletteCValue(1,k);
                float cur_red = GetPalletteCValue(2,k);
                float cur_alpha = GetPalleteAValue(k);
                
                color[0] = cur_blue;
                color[1] = cur_green;
                color[2] = cur_red;
                alpha = cur_alpha;
            }

            m_PalletteC2D[0].get()[MAX_INTENSITY*j + i] = color[0] / diff;
            m_PalletteC2D[0].get()[MAX_INTENSITY*j + i] = color[0] / diff;
            m_PalletteC2D[0].get()[MAX_INTENSITY*j + i] = color[0] / diff;
            m_PalletteA2D.get()[MAX_INTENSITY*j + i] = alpha / diff;
        }
    }
}

pallette 를 모두 만들었으니 2D pallette 를 사용하는 함수를 만들어보자.

매개변수로 2개의 intensity 가 들어오고 2개의 값을 interpolation 해서 가중값을 반환한다.

float TransferFunction::GetPalletteC2DValue(int color, float prev_intensity, float intensity) { if(intensity +1.f>= MAX_INTENSITY) return -1.f; if(prev_intensity +1.f >= MAX_INTENSITY) return -1.f;

float coord_00 = m_PalletteC2D[color].get()[MAX_INTENSITY*static_cast<int>(prev_intensity) + static_cast<int>(intensity)];
float coord_10 = m_PalletteC2D[color].get()[MAX_INTENSITY*static_cast<int>(prev_intensity+1) + static_cast<int>(intensity)];
float coord_01 = m_PalletteC2D[color].get()[MAX_INTENSITY*static_cast<int>(prev_intensity) + static_cast<int>(intensity+1)];
float coord_11 = m_PalletteC2D[color].get()[MAX_INTENSITY*static_cast<int>(prev_intensity+1) + static_cast<int>(intensity+1)];

float weight[2] = {prev_intensity - static_cast<int>(prev_intensity),
    intensity - static_cast<int>(intensity)};

float inter_0010 = (1.f - weight[0]) * coord_00 + weight[0]*coord_10;
float inter_0111 = (1.f - weight[0]) * coord_01 + weight[0]*coord_11;

float res = (1.f - weight[1]) * inter_0010 + weight[1] * inter_0111;
return res; }

float TransferFunction::GetPalletteA2DValue(float prev_intensity, float intensity) { if(prev_intensity + 1.f >= MAX_INTENSITY) return -1.f; if(intensity + 1.f >= MAX_INTENSITY) return -1.f;

float coord_00 = m_PalletteA2D.get()[MAX_INTENSITY*static_cast<int>(prev_intensity) + static_cast<int>(intensity)];
float coord_10 = m_PalletteA2D.get()[MAX_INTENSITY*static_cast<int>(prev_intensity+1) + static_cast<int>(intensity)];
float coord_01 = m_PalletteA2D.get()[MAX_INTENSITY*static_cast<int>(prev_intensity) + static_cast<int>(intensity+1)];
float coord_11 = m_PalletteA2D.get()[MAX_INTENSITY*static_cast<int>(prev_intensity+1) + static_cast<int>(intensity+1)];

float weight[2] = {prev_intensity - static_cast<int>(prev_intensity),
    intensity - static_cast<int>(intensity)};
float inter0010 = (1.f - weight[0]) * coord_00 + weight[0] * coord_10;
float inter0111 = (1.f - weight[0]) * coord_01 + weight[0] * coord_11;

float res = (1.f - weight[1]) * inter0010 + weight[1]* inter0111;
return res; }

이후 이를 사용하는 부분을 다듬어야 한다.

Renderer.cpp

bool Renderer::RenderVRAnyDirection(unsigned char* image,
    const int img_width, cons int img_height, int DirKey)
{
    ...
    /// prev_intensity 를 가져온다.
    float prev_intensity = m_pVolume->GetVoxel(adv_coord.x - view_vector.x,
        adv_coord.y - view_vector.y, adv_coord.z - view_vector.z);
    /// intensity 를 가져온다.
    float intensity  = m_pVolume->GetVoxel(adv_coord.x, adv_coord.y, adv_coord.z);

    float cur_blue = m_pTF->GetPalletteC2DValue(0,prev_intensity, intensity);
    float cur_green = m_pTF->GetPalletteC2DValue(1,prev_intensity, intensity);
    float cur_red = m_pTF->GetPalletteC2DValue(2,prev_intensity, intensity);
    float cur_alpha = m_pTF->GetPalletteA2DValue(prev_intensity, intensity);

    ...
}

이를 적용하여 volume rendering 하면 아래와 같이 나온다. bighead-2dpallet