前言
之前实现了PPT的抖动算法,发现效果差强人意,能支持的分辨率也有限,因此开始倒腾一些高阶的抖动算法。通过查询资料,发现了两个有趣的抖动算法。一个是bayer抖动,一个是floyd-steinberg算法。bayer算法具体使用场景暂未查到,floyd-steinberg的应用场景相对较大,单色打印机就是使用此算法实现抖动。
算法实现逻辑
bayer算法
这个算法,本质上就是把数据分割成8 * 8的色块,色块内的值标准化到0~63后与bayer矩阵对应位置的数据比较,大于数据,则画黑点,小于,则清除点处的颜色。之所以这么操作,是因为在小范围内,颜色的变化是渐变的,个人理解,这属于比简单抖动算法稍微复杂点的抖动实现,避免了简单抖动带来的分辨率不得不降低的问题。至于这个矩阵的获取方法,大家可以自行网上搜索计算。
其具体实现逻辑如下:
void dither_bayer_m3(unsigned char *image) {
uint16_t i = 0, j = 0;
unsigned char M3_Bayer[8][8] = {
{0, 32, 8, 40, 2, 34, 10, 42}, //
{48, 16, 56, 24, 50, 18, 58, 26}, //
{12, 44, 4, 36, 14, 46, 6, 38}, //
{60, 28, 52, 20, 62, 30, 54, 22}, //
{3, 35, 11, 43, 1, 33, 9, 41}, //
{51, 19, 59, 27, 49, 17, 57, 25}, //
{15, 47, 7, 39, 13, 45, 5, 37}, //
{63, 31, 55, 23, 61, 29, 53, 21}, //
};
for (i = 0; i < IMAGE_W - 1; i++) {
for (j = 0; j < IMAGE_H - 1; j++) {
if ((image[i * IMAGE_H + j] >> 2) > M3_Bayer[j & 7][i & 7])
image[i * IMAGE_H + j] = 255;//实际可以是1,这里设置成255,仅仅是为了和后面画图标准统一
else
image[i * IMAGE_H + j] = 0;
}
}
}floyd-steinberg
相比较于bayer算法,floyd-steinberg算法的实现思路就更加巧妙了,他的实现思路是:既然一个像素无法显示灰阶,那几个像素的和是否可以显示?而这个实现逻辑的核心,就是差值传递,也就是,实际使用的是像素点的差值作为参数,更新周围的像素点的值,使所有的像素点的值尽量往255和0两边靠,最后以128为分界线决定是否画点。
其实现逻辑如下:
void dither_floyd_steinberg(uint8_t *image) {
uint16_t i = 0, j = 0;
uint8_t oldPixel, newPixel, errPixel;
uint8_t add_flag = 0;
for (i = 0; i < IMAGE_W - 1; i++) {
for (j = 0; j < IMAGE_H - 1; j++) {
oldPixel = image[i * IMAGE_H + j];
if (oldPixel > 128) {
newPixel = 255;
errPixel = 255 - oldPixel;
add_flag = 0;
} else {
newPixel = 0;
errPixel = oldPixel;
add_flag = 1;
}
image[i * IMAGE_H + j] = newPixel;
if (add_flag) {
if (image[i * IMAGE_H + j + 1] > 255 - errPixel * 7 / 16) {
image[i * IMAGE_H + j + 1] = 255;
} else {
image[i * IMAGE_H + j + 1] = image[i * IMAGE_H + j + 1] + errPixel * 7 / 16;
}
if (image[(i + 1) * IMAGE_H + j] > 255 - errPixel * 5 / 16) {
image[(i + 1) * IMAGE_H + j] = 255;
} else {
image[(i + 1) * IMAGE_H + j] = image[(i + 1) * IMAGE_H + j] + errPixel * 5 / 16;
}
if (image[(i + 1) * IMAGE_H + j + 1] > 255 - errPixel * 1 / 16) {
image[(i + 1) * IMAGE_H + j + 1] = 255;
} else {
image[(i + 1) * IMAGE_H + j + 1] = image[(i + 1) * IMAGE_H + j + 1] + errPixel * 1 / 16;
}
if (j > 0) {
if (image[(i + 1) * IMAGE_H + j - 1] > 255 - errPixel * 3 / 16) {
image[(i + 1) * IMAGE_H + j - 1] = 255;
} else {
image[(i + 1) * IMAGE_H + j - 1] = image[(i + 1) * IMAGE_H + j - 1] + errPixel * 3 / 16;
}
}
} else {
if (image[i * IMAGE_H + j + 1] < errPixel * 7 / 16) {
image[i * IMAGE_H + j + 1] = 0;
} else {
image[i * IMAGE_H + j + 1] = image[i * IMAGE_H + j + 1] - errPixel * 7 / 16;
}
if (image[(i + 1) * IMAGE_H + j] < errPixel * 5 / 16) {
image[(i + 1) * IMAGE_H + j] = 0;
} else {
image[(i + 1) * IMAGE_H + j] = image[(i + 1) * IMAGE_H + j] - errPixel * 5 / 16;
}
if (image[(i + 1) * IMAGE_H + j + 1] < errPixel * 1 / 16) {
image[(i + 1) * IMAGE_H + j + 1] = 0;
} else {
image[(i + 1) * IMAGE_H + j + 1] = image[(i + 1) * IMAGE_H + j + 1] - errPixel * 1 / 16;
}
if (j > 0) {
if (image[(i + 1) * IMAGE_H + j - 1] < errPixel * 3 / 16) {
image[(i + 1) * IMAGE_H + j - 1] = 0;
} else {
image[(i + 1) * IMAGE_H + j - 1] = image[(i + 1) * IMAGE_H + j - 1] - errPixel * 3 / 16;
}
}
}
}
}
}编码验证
main.c
int main(void) {
system_init();
key_init();
epd_init();
DEV_Delay_ms(2000);
show_picture(0);
while (get_key() == 0) {
continue;
}
show_picture(1);
while (get_key() == 0) {
continue;
}
show_picture(0);
while (get_key() == 0) {
continue;
}
show_picture(2);
while (true) {
}
return 0;
}my_epd.c
void show_picture(char ditherEn) {
unsigned char *pDitherImage;
int idxX, idxY;
char level;
int x = 0, y = 0;
pDitherImage = (unsigned char *)malloc(sizeof(gImage_raw));
My_memcpy(pDitherImage, (unsigned char *)gImage_raw, sizeof(gImage_raw));
if (ditherEn == 1) {
dither_floyd_steinberg(pDitherImage);
} else if (ditherEn == 2) {
dither_bayer_m3(pDitherImage);
}
for (idxX = 0; idxX < IMAGE_W; idxX++) {
for (idxY = 0; idxY < IMAGE_H; idxY++) {
if (pDitherImage[idxX * IMAGE_H + idxY] / 128)
draw_fb_point(x + idxX, y + idxY);
else
clear_fb_point(x + idxX, y + idxY);
}
}
free(pDitherImage);
updata_to_epd(DISPLAY_ALL);
}数据准备
既然分变率不再像简单算法那么受限,那显示可选的图片就多了。而图像效果验证最经典的图片莫过于IEEE女神了,因此这里也直接采用IEEE女神图建立数据,图片如下:

受限于文字量,无法粘贴,导出的数据就不提供了,大家可以按照简单抖动算法实现中的方法自行导出。
效果验证
原图
bayer算法
floyd-steinberg算法

我要赚赏金
