How to Add Generic Convolution Filter to NVTT

Pope Kim May 21, 2011

A few month ago, I said I would write a post about how to add a generic convolution filter to NVidia Texture Tool. once I get a clearance from our legal team. And finally they got back to me.

Background

The reason why I added this feature at work was because our artists wanted a sharpening filter on mipmaps. This feature was present with the original NVTT 1, but removed from NVTT 2. Given that sharpening filter is a simple 3x3 or 5x5 convolution filter, I've decided to add a generic convolution filter support which can take any arbitrary coefficients. With this approach, anyone can run almost every image processing algorithms based on on convolution.

NVTT Modification

So here's how. It requires only a few lines of change on 6 files. So I'll just walk you through.

Step 1. Get revision 1277 from NVidia Texture Tools project page.

I haven't tested this on later revisions, but I think it should work unless there were major changes in that source code.

Step 2. Open up src/nvimage/Filter.h and add this constructor.

Kernel2(uint width, const float * data);

Step 3. Open up src/nvimage/Filter.cpp and add this function.

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. Open up src/nvimage/FloatImage.h and add this function prototype.

NVIMAGE_API void doConvolution(uint size, const float* data);

Step 5. Open up src/nvimage/FloatImage.cpp and add this function implementation.

void FloatImage::doConvolution(uint size, const float* data)
{
        Kernel2 k(size, data);
        AutoPtr<floatimage> tmpImage = clone();</floatimage>

        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. Open up src/nvtt/nvtt.h and add this function prototype under struct TexImage.

NVTT_API void doConvolution(unsigned int size, const float* data);

Step 7. Open up src/nvtt/TexImage.cpp and add this function implementation.

void TexImage::doConvolution(unsigned int size, const float* data)
{
    if (m->image == NULL) return;

    detach();

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

How to Use

How to use this is very straight forward. Assuming you already have a TexImage object named image, you can do this.

const int kernelSize = 3;    // let's use 3 x 3 kernel

// Some random coefficients I found working great for sharpening.
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);

YAY!

p.s. I've also emailed the patch file to Ignacio, the creator/maintainer of NVTT project. Let's see if it ever makes into the codebase. :)