Floyd-Steinberg抖动算法

废话不多说,先贴上变换的代码。

void applyDithering(Image& image, int width, int height) {
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // 原像素值
            int oldPixel = image[y][x];
            
            // 量化为0或255(即黑与白)
            int newPixel = oldPixel < 128 ? 0 : 255;
            image[y][x] = newPixel;

            // 计算误差
            int error = oldPixel - newPixel;

            // 误差扩散到相邻像素
            if (x + 1 < width) {
                image[y][x + 1] += error * 7 / 16;
                clamp(image[y][x + 1]);
            }
            if (x - 1 >= 0 && y + 1 < height) {
                image[y + 1][x - 1] += error * 3 / 16;
                clamp(image[y + 1][x - 1]);
            }
            if (y + 1 < height) {
                image[y + 1][x] += error * 5 / 16;
                clamp(image[y + 1][x]);
            }
            if (x + 1 < width && y + 1 < height) {
                image[y + 1][x + 1] += error * 1 / 16;
                clamp(image[y + 1][x + 1]);
            }
        }
    }
}

解释一下什么是误差扩散。顾名思义,就是要将我们计算得到的误差扩散到相邻的像素去。同时,我们遵循以下原则:

右侧像素的误差为 7/16;
左下方像素的误差为 3/16;
正下方像素的误差为 5/16;
右下方像素的误差为 1/16。


具体到代码(取其中一段为例子):

            if (x + 1 < width) {
                image[y][x + 1] += error * 7 / 16;
                clamp(image[y][x + 1]);
            }

我们就将右侧的像素加上之前计算得到误差的7/16。至于之后是如何判断得到左下/下方/右下像素的,相信一眼就能看明白。我们依次按照系数进行如上计算即可。