Chris Sprance

Character Technical Director

Dilation in C++

This is a follow-up to another blog I did just recently in which I made a dilate texture function inside of Python. The speed of the python dilate was really, really slow, so I decided to remake this in C++ and see what kind of speed I'd get. I am using C++ and OpenCV to do most of the work.

file.cpp
// Arg parsed variables
const string input_file = parser.get<string>("i");
const string output_file = parser.get<string>("o");
const int iterations = parser.get<int>("n");
const string extension = get_extension(output_file);

// Read File (PNG, Tiff, JPG)
Mat_<Vec4b> img = imread(input_file, IMREAD_UNCHANGED);
// Clone it
Mat dst = img.clone();
// Split in to R,G,B,A
Mat rgbchannel[4];
split(img, rgbchannel);
Mat og_alpha = rgbchannel[3];

// For each iteration
for (int iter = 0; iter < iterations; iter++) {
    // For every pixel (row/col)
    for (int row = 0; row < img.rows; row++) {
        for (int col = 0; col < img.cols; col++) {
            // Get Pixel at row col
            auto pixel = img.at<Vec4b>(row, col);
            // if alpha of pixel is less then 255 we want to search for a neighbor with alpha
            if (pixel[3] < 255) {
                for (auto & neighbor : neighbors) {
                    // make sure our index doesn't go out of bounds
                    int n_row = row + neighbor[0];
                    int n_col = col + neighbor[1];
                    if (row_col_in_bounds(n_row, n_col, img)) {
                        // find neighbor pixels
                        auto n_pixel = img.at<Vec4b>(n_row, n_col);
                        // find the first non 0 neighbor
                        if (n_pixel[3] > 0) {
                            // Replace the color in the dest image with this color
                            dst.at<Vec4b>(row, col) = Vec4b(n_pixel[0], n_pixel[1], n_pixel[2], 255);
                            // Bail out of the loop
                            break;
                        }
                    }
                }
            }
        }
    }
    // Set the img to be the dest so on the next iteration
    // the img being sampled is the previously dilated image
    img = dst;
}

// split the channels in to RGBA
Mat dest_rgb_channel[4];
split(dst, dest_rgb_channel);
// Assign RGB channels to vars
const Mat red = dest_rgb_channel[0],
    green = dest_rgb_channel[1],
    blue = dest_rgb_channel[2];

// If extension is TGA (swap RGB -> BGR for tga)
if (extension == "tga") {
    dest_rgb_channel[0] = blue; // Blue
    dest_rgb_channel[1] = green; // Green
    dest_rgb_channel[2] = red; // Red
}
else {
    dest_rgb_channel[0] = red; // Red
    dest_rgb_channel[1] = green; // Green
    dest_rgb_channel[2] = blue; // Blue
}

// merge the original alpha back in to the image
dest_rgb_channel[3] = og_alpha;

// Merge all the channels back in to the dst image they came from
merge(dest_rgb_channel, 4, dst);

As I'm sure you can guess the performance on the C++ version is significantly faster. It takes about .2 seconds to do 512 iterations on a 2048x2048 image which is lightning fast compared to the python version that struggled to come in under a minute on 512x512 images doing only 32 iterations.

I plan to polish this one and release it on gumroad at some point soon.

That's it hope you enjoyed!