NVidia 텍스처 툴에 컨벌루션 필터링을 추가하는 방법

김포프 2011-05-21

몇 달 전부터 올릴려고 했던 글인데... 회사 법무팀의 허락을 받느라 좀 지체되었다. 드디어 올려도 된다는 허락을 받았으니.. 아싸 -_-;

배경
NVidia 텍스처 툴(NVTT)에 컨벌루션 영상처리 기법을 추가하게 된 계기는 회사에서 아티스트들의 요청 때문이었다. NVTT 1.0은 밉맵에 샤프닝(sharpening) 필터를 적용하는 옵션이 있었는데, NVTT 2.0에서는 삭제... (추후 추가할 예정이었으니 1년이 넘도록 추가되지 않음... -_-)  NVTT가 오픈소스 프로젝트가 된 이후로는 개발도 뜸해서 이 기능이 추가되길 기다리기 보다는 직접 추가하기로 맘을 먹었다.

어차피 샤프닝 필터는 2D 커널을 이용한 컨벌루션에 지나지 않으니 차라리 보다 범용적인 컨벌루션 필터를 추가해서 아무 계수나 받는 게 낫다고 생각했다. 이러면 샤프닝 뿐만이 아니라 컨벌루션을 이용하는 영상처리 기법은 모두 돌릴 수 있으니....

NVTT 소스 코드 수정
자 그럼 본격적으로.. 실제 코드를 들여다 볼 시간.. 사실 별로 추가할 코드가 많진 않다. 6개 파일에 코드 몇줄만 추가하면 되니까... 

Step 1. NVidia 텍스처 툴 프로젝트 웹페이지에서 revision 1277을 받는다.
최근 revision에서 이 코드를 테스트해보진 않았는데 별 차이는 없을거라 생각한다. NVTT 개발자가 소스코드 전체를 뒤집이 엎진 않은 이상... -_-;

Step 2. src/nvimage/Filter.h 파일을 열고 다음의 생성자를 추가한다.

Kernel2(uint width, const float * data);

Step 3. src/nvimage/Filter.cpp 파일 안에 다음의 함수를 추가한다.
Kernel2::Kernel2(uint ws, const float* data) : m_windowSize(ws)
{
    m_data = new float[m_windowSize * m_windowSize];

    memcpy(m_data, data, sizeof(float) * m_windowSize * m_windowSize);
}

Step 4. src/nvimage/FloatImage.h 파일 안에 다음 함수를 선언한다.
NVIMAGE_API void doConvolution(uint size, const float* data);

Step 5. src/nvimage/FloatImage.cpp 파일 안에 다음 함수를 구현한다.
void FloatImage::doConvolution(uint size, const float* data)
{
        Kernel2 k(size, data);
        AutoPtr tmpImage = clone();

        for(uint y = 0; y < height(); y++)
        {
            for(uint x = 0; x < width(); x++)
            {
            for (uint c = 0; c < 4; ++c )
            {
                pixel(x, y, c) = tmpImage->applyKernel(&k, x, y, c, WrapMode_Clamp);
            }
        }
    }
}

Step 6. src/nvtt/nvtt.h 파일 안에서 struct TexImage를 찾아 다음의 함수를 선언한다.
NVTT_API void doConvolution(unsigned int size, const float* data);

Step 7. src/nvtt/TexImage.cpp 파일 안에 다음 함수를 구현한다.
void TexImage::doConvolution(unsigned int size, const float* data)
{
    if (m->image == NULL) return;

    detach();

    m->image->doConvolution(size, data);
}

사용법
새로 추가된 컨벌루션 필터를 사용하는 법은 매우 간단하다. 이미 image 라는 TexImage 개체가 있다면 간다히 이렇게 사용한다.

const int kernelSize = 3;    // 3 x 3 커널을 사용한다.

// 대충 내 맘대로 찾아낸 샤프닝 계수.
const float sharpenKernel [] = 
{
    -1/16.0f, -2/16.0f,     -1/16.0f,
    -2/16.0f, 1 + 12/16.0f, -2/16.0f,
    -1/16.0f, -2/16.0f,     -1/16.0f,
};

image.doConvolution(kernelSize, sharpenKernel);


끝! (정말 간단하지? -_-)