Showcase: Online Image (Dithering) Converter for E-Paper/LCD

Hi Arduino community!

I've been working on several e-paper display projects recently (monochrome, tri-color, and full-color variants) and encountered a common challenge: converting images for different displays. Existing tools couldn't handle my specific needs like:

  • Palette customization for monochrome/limited-color displays
  • Real-time comparison of different dithering algorithms
  • Direct array output for C/Python embedded systems

After struggling with manual Python + Pillow scripting, I built img2lcd - a pure web-based solution that provides:

  • Side-by-side dithering algorithm comparison (Floyd-Steinberg, Atkinson, etc.)
  • Custom? palette support (e-papers and RGB565)
  • One-click C/Python array generation

Code Sample: (with GxEPD2)

#include <GxEPD2_3C.h>

#include "image.h"  // genreate by img2lcd rgb566_bgr

GxEPD2_3C<GxEPD2_154_Z90c, GxEPD2_154_Z90c::HEIGHT> display(GxEPD2_154_Z90c(/*CS=5*/ 16, /*DC=*/ 15, /*RST=*/ 14, /*BUSY=*/ 13));
void setup() {
  Serial.begin(115200);

  SPI.begin(17, -1, 18);
  display.init(115200);

  display.setRotation(0);
  display.firstPage();
  do {
    display.drawRGBBitmap(0, 0, (uint16_t *)IMAGE, 200, 200);
  } while (display.nextPage());
}

Tested with:
OLED (SSD1306), Good-Display e-papers(1.54"/2.9"/7.5" Mono & BWR), Waveshare 7.3" 7-color e-paper, LVGL (ST7789/RGB565)

I'd Love Your Feedback!

  • Have you struggled with image optimization for displays?
  • What features would you add (e.g., new dithering modes, palette presets)?
  • Found a bug? Let me know!

This tool saved me hours of Python scripting – I hope it helps others too! If you test it, share your experience below or DM me. Happy hacking! :hammer_and_wrench:

1 Like

Hi ningh,

love that project and it's a shame that you got so little feedback here. Seeing the results of these different dithering algorithms side by side and being able to download a ready to use piece if code is just great.

For my own project, I will implement my own algorithm for ESP32, because I need dynamic conversion, but this has been a great inspiration and helped me choose my preference, which is Atkinson.

After having read up a bit about that algorithm, I wonder how you implemented it for >2 colors, particularly for 3-color B/W/R, as the original only needs to decide between B and W. I tried my own approach (well, frankly, I let chatgpt do the heavy lifting), and got a bit different results than yours, so would love to see your algorithm for that.

Jan

Hi Jan,

Thank you for your kind words! The img2lcd project started as my exploration of dithering algorithms, and it¡¯s still evolving. I¡¯m glad you found the side-by-side comparisons useful.

Regarding multi-color implementations (e.g., B/W/R):

  1. Color Matching: Euclidean distance in RGB space determines the closest palette color for each pixel, avoiding simple luminance thresholding.

  2. Value Clamping: Post-error accumulation values are clamped to 0-255 instead of redistributing overflows ¨C this might explain our differing results.

If you can share specific test images or code excerpts where our outputs diverge, I¡¯d be happy to analyze them after returning from my trip (currently away from my codebase).

Best,
Ning

Hi Ning,

thanks for the explanation. As mentioned, I chose not too dig into these algorithms as deep as you did, and let chatgpt do most of the implementation. As so often, it just needed a more precise prompt and came up with a three-color implementation that did things just fine for my purposes. If you want to have a look, I just put the project on github:

The Atkinson dithering happens in this function, which is a callback from PNG decoding, and thus needs to do the dithering on a line-by-line basis with a three line buffer:

Anyways, thanks again for your project, it gave me the inspiration to include a dithering option in this project.

Have a safe trip home,
Jan

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.