抖动法显示灰度图像(Qt 实现)
2015-08-12 16:06
686 查看
有时,我们要在只能显示黑白两种颜色的显示设备上显示一副灰度图像。这时就要采用所谓的抖动法(Dithering)来模拟出灰度效果来。
比如下面这幅图:
如果只是做个简单的二值化处理,得到的结果是这样的(以 128 为阈值)
虽然还能看出这个图像的内容,但是效果并不好。
一种直观的想法就是在做二值化时引入随机数,对于所有值为 x 的像素,以 x/256 为概率将这些像素点点亮。下面的代码就可以实现这个功能。
实际实验一下,这样做的效果并不太好。
原因嘛,就是引入了太多的噪声。其实我们可以抖动的不这么厉害。下面的代码就可以控制抖动的大小。
这个代码,当 dither 值为 128 时就和上面随机抖动代码的结果相同了。如果 dither 值为 0, 那就退化成了简单的二值化。
当 dither = 50 时,是下面的效果:
dither = 10 时:
可以看出,通过调整 dither ,可以达到不错的效果。
除了这种随机抖动法,还有些确定性的抖动处理方法,其中最常用的应该算是 Floyd–Steinberg dithering。
这种算法的介绍可以参考: https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering
下面我也给个我写的一个实现。
用这个算法得到的图像是这样的:
效果很不错,这也是大家都喜欢用它的原因。
当然 Floyd–Steinberg 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 的参数可以有不同的设置,得到的结果也不同。感兴趣的同学可以自己做做实验。
相关文章推荐
- Qt 常用类(18)——QStandardItemModel
- Qt全局宏和变量
- QT小技巧
- 进阶~Qt程序启动画面
- Qt零基础教程(四) QWidget详解篇
- 在qt中碰到了suze
- 《转》Qt 经典出错信息之”Basic XLib functionality test failed!”(Z..z..)
- Qt 图片缩放
- Qt之日常积累
- Qt 5.x 中文翻译缺失的一种解决办法
- Qt打开外部程序的三种方法(两种阻塞,一种不阻塞)
- 基于QT-widget的电子词典实现
- Qt5.5.0+VS2013+win64配置
- Qt浅谈之三十二二维码条形码解析
- qt 插件开发
- 基于qt和mplayer的影音播放器
- Qt 5.3 下OpenCV 2.4.11 开发(15)滤波函数
- QSqlTableModel和QTableView设置复选框
- QT显示图片
- Qt dxlib 调用与简单应用