抖动法显示灰度图像(Qt 实现)

有时,我们要在只能显示黑白两种颜色的显示设备上显示一副灰度图像。这时就要采用所谓的抖动法(Dithering)来模拟出灰度效果来。
比如下面这幅图:
这里写图片描述

如果只是做个简单的二值化处理,得到的结果是这样的(以 128 为阈值)
这里写图片描述

虽然还能看出这个图像的内容,但是效果并不好。

一种直观的想法就是在做二值化时引入随机数,对于所有值为 x 的像素,以 x/256 为概率将这些像素点点亮。下面的代码就可以实现这个功能。

QImage RandomDithering(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = image.copy();

    for(int i = 0; i < height; i ++)
    {
        uchar * p = ret.scanLine(i);
        for(int j = 0; j < width; j ++)
        {
            int x = rand() % 256;
            p[j] = ( x <= p[j] ?  255 : 0 );
        }
    }
    return ret;
}

实际实验一下,这样做的效果并不太好。
这里写图片描述

原因嘛,就是引入了太多的噪声。其实我们可以抖动的不这么厉害。下面的代码就可以控制抖动的大小。

QImage RandomDithering(QImage &image, int dither)
{
    int width = image.width();
    int height = image.height();
    QImage ret = image.copy();

    for(int i = 0; i < height; i ++)
    {
        uchar * p = ret.scanLine(i);
        for(int j = 0; j < width; j ++)
        {
            int x = rand() % (2 * dither) - dither;
            p[j] = (p[j] + x >= 128 ? 255 : 0);
        }
    }
    return ret;
}

这个代码,当 dither 值为 128 时就和上面随机抖动代码的结果相同了。如果 dither 值为 0, 那就退化成了简单的二值化。

当 dither = 50 时,是下面的效果:
这里写图片描述

dither = 10 时:
这里写图片描述

可以看出,通过调整 dither ,可以达到不错的效果。

除了这种随机抖动法,还有些确定性的抖动处理方法,其中最常用的应该算是 Floyd–Steinberg dithering。

这种算法的介绍可以参考: https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering

下面我也给个我写的一个实现。

QImage FloydSteinbergDithering(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = image.copy();
    for(int i = 0; i < height - 1; i ++)
    {
        uchar * p = ret.scanLine(i);
        uchar *pNext = ret.scanLine(i + 1);
        for(int j = 0; j < width - 1; j ++)
        {
            int diff;
            if( p[j] > 128 )
            {
                diff = p[j] - 255;
                p[j] = 255;
            }
            else
            {
                diff = p[j];
                p[j] = 0;
            }
            pNext[j] = qBound(0, pNext[j] + diff * 3 / 16, 255);
            p[j + 1] = qBound(0, p[j + 1] + diff * 3 / 16, 255);
            pNext[j + 1] = qBound(0, pNext[j + 1] + diff * 1 / 4, 255);
        }
        p[width - 1] = (p[width - 1] >= 128 ? 255 : 0);
    }
    uchar * p = ret.scanLine(height - 1);
    for(int j = 0; j < width; j ++)
    {
        p[j] = (p[j] >= 128 ? 255 : 0);
    }
    return ret;
}

用这个算法得到的图像是这样的:
这里写图片描述

效果很不错,这也是大家都喜欢用它的原因。

当然 Floyd–Steinberg dithering 的参数可以有不同的设置,得到的结果也不同。感兴趣的同学可以自己做做实验。

  • 16
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值