这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【墨水屏电子书】进阶任务——x相对高阶的抖动算法实现

共4条 1/1 1 跳转至

【墨水屏电子书】进阶任务——x相对高阶的抖动算法实现

助工
2024-08-11 11:48:29     打赏

前言

   之前实现了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女神图建立数据,图片如下:

IEEE.png

受限于文字量,无法粘贴,导出的数据就不提供了,大家可以按照简单抖动算法实现中的方法自行导出。

效果验证

原图

1.jpgbayer算法

1.jpgfloyd-steinberg算法

1.jpg







关键词: 墨水     电子书     进阶     任务     抖动     算法    

专家
2024-08-11 14:32:38     打赏
2楼

效果不错啊。


专家
2024-08-11 17:33:45     打赏
3楼

效果不错


高工
2024-08-16 10:48:35     打赏
4楼

水平很高啊


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]