Android Floyd-Steinberg-Dithering、Stucki-dither 抖动处理


I. Floyd-Steinberg-Dithering

Github: Native-Floyd-Steinberg-Dithering
该作者对Floyd-Steinberg-Dithering 的处理中,使用的是静态数组,容易栈溢出。
这里,给出两种实现来避免。当然,若一次处理的位图信息过大,也是会堆溢出,OOM的。

I.i. 动态数组实现

cpp 代码如下。

void floydSteinberg1(AndroidBitmapInfo *info, void *pixels) {
    uint32_t *pixelLine;
    void *p = pixels;
    int w = info->width;
    int h = info->height;
//    uint32_t ary[h][w];
    uint32_t **ary = new uint32_t *[h];
    for (int y = 0; y < h; ++y) {
        ary[y] = new uint32_t[w];
        pixelLine = (uint32_t *) p;
        for (int x = 0; x < w; ++x) {
            //extract the RGB values from the pixel
            ary[y][x] = 0xFF000000 | (pixelLine[x] & 0x00FFFFFF);
        }
        p = (char *) p + info->stride;
    }

    for (int y = 0; y < h; ++y) {
        pixelLine = (uint32_t *) pixels;
        for (int x = 0; x < w; ++x) {
            uint32_t oldPixel = ary[y][x];
//            uint32_t newPixel = find_closest_palette_color(oldPixel);
            uint32_t newPixel = (int) oldPixel >= 128 ? 0xFFFFFFFF : 0xFF000000;
            pixelLine[x] = newPixel;

            uint32_t err = oldPixel - newPixel;

            if (x + 1 < w)
                ary[y][x + 1] += (int) (err * (7. / 16));
            if (x > 0 && y + 1 < h)
                ary[y + 1][x - 1] += (int) (err * (3. / 16));
            if (y + 1 < h)
                ary[y + 1][x] += (int) (err * (5. / 16));
            if (x + 1 < w && y + 1 < h)
                ary[y + 1][x + 1] += (int) (err * (1. / 16));
        }
        pixels = (char *) pixels + info->stride;
    }

    for (int y = 0; y < h; ++y) {
        delete ary[y];
    }
    delete[] ary;
}

I.ii. 指针实现

void floydSteinberg2(AndroidBitmapInfo *info, void *pixels) {
    int w = info->width;
    int h = info->height;
    int mid = 128;
    uint32_t min = 0xFF000000;
    uint32_t max = 0xFFFFFFFF;
//    uint32_t minWhite = 0xFFFF0000;
    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
            uint32_t pixel = *((uint32_t *) pixels + y * w + x);
//            if (pixel >= minWhite && pixel <= max) {//接近白色不处理
//                continue;
//            }
            uint32_t err = (int) pixel >= mid ? pixel - max : pixel - min;
            if (x + 1 < w) {
                *((uint32_t *) pixels + y * w + (x + 1)) += (int) (err * (7. / 16));
            }
            if (x >= 1 && y + 1 < h) {
                *((uint32_t *) pixels + (y + 1) * w + (x - 1)) += (int) (err * (3. / 16));
            }
            if (y + 1 < h) {
                *((uint32_t *) pixels + (y + 1) * w + x) += (int) (err * (5. / 16));
            }
            if (x + 1 < w && y + 1 < h) {
                *((uint32_t *) pixels + (y + 1) * w + (x + 1)) += (int) (err * (1. / 16));
            }
            pixel = (int) pixel >= mid ? max : min;
            *((uint32_t *) pixels + y * w + x) = pixel;
        }
    }
}


II. Stucki

本算法,是从一段c#代码中移植出来的。

void stucki(AndroidBitmapInfo *info, void *pixels) {
    int w = info->width;
    int h = info->height;
    int mid = 128;
    uint32_t max = 0xFF000000;
    uint32_t min = 0xFFFFFFFF;
    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
            uint32_t pixel = *((uint32_t *) pixels + y * w + x);
            uint32_t err = (int) pixel >= mid ? pixel - min : pixel - max;
            if (x + 1 < w)
                *((uint32_t *) pixels + y * w + (x + 1)) += (int) (err * (8. / 42));
            if (x + 2 < w)
                *((uint32_t *) pixels + y * w + (x + 2)) += (int) (err * (4. / 42));

            if (x >= 2 && y + 1 < h)
                *((uint32_t *) pixels + (y + 1) * w + (x - 2)) += (int) (err * (2. / 42));
            if (x >= 1 && y + 1 < h)
                *((uint32_t *) pixels + (y + 1) * w + (x - 1)) += (int) (err * (4. / 42));
            if (y + 1 < h)
                *((uint32_t *) pixels + (y + 1) * w + (x)) += (int) (err * (8. / 42));
            if (x + 1 < w && y + 1 < h)
                *((uint32_t *) pixels + (y + 1) * w + (x + 1)) += (int) (err * (4. / 42));
            if (x + 2 < w && y + 1 < h)
                *((uint32_t *) pixels + (y + 1) * w + (x + 2)) += (int) (err * (2. / 42));

            if (x >= 2 && y + 2 < h)
                *((uint32_t *) pixels + (y + 2) * w + (x - 2)) += (int) (err * (1. / 42));
            if (x >= 1 && y + 2 < h)
                *((uint32_t *) pixels + (y + 2) * w + (x - 1)) += (int) (err * (2. / 42));
            if (y + 2 < h)
                *((uint32_t *) pixels + (y + 2) * w + (x)) += (int) (err * (4. / 42));
            if (x + 1 < w && y + 2 < h)
                *((uint32_t *) pixels + (y + 2) * w + (x + 1)) += (int) (err * (2. / 42));
            if (x + 2 < w && y + 2 < h)
                *((uint32_t *) pixels + (y + 2) * w + (x + 2)) += (int) (err * (1. / 42));

            pixel = (int) pixel >= mid ? min : max;
            *((uint32_t *) pixels + y * w + x) = pixel;
        }
    }
}

III. 示例

完整示例


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值