This guide explains how to measure the actual RGB values of your e-paper display for accurate dithering. E-paper displays are reflective, making their colors significantly darker than pure RGB values:
- Pure RGB assumption: White=(255,255,255), Red=(255,0,0)
- Reality: White~(180-200), Red~(115-125) -- 30-87% darker
Using measured values produces much better dithering results.
- Camera with RAW support (smartphone RAW works fine, e.g. iPhone ProRAW)
- Two lights with controllable color temperature (e.g. Philips Hue Play bars)
- White paper sheet as a reference (plain printer paper)
- Photo editing software with eyedropper/color picker (Photoshop, GIMP, Digital Color Meter, etc.)
Use the included script to generate a patch image for your display:
# List available color schemes
python scripts/generate_patches.py --list
# Generate for your display (replace scheme and size)
python scripts/generate_patches.py --scheme BWGBRY --size 800x480 -o patches.png
python scripts/generate_patches.py --scheme BWR --size 296x128 -o patches.pngThe script automatically picks a grid layout (e.g. 3x2 for 6 colors on a landscape display) that keeps patches as large as possible.
Display the generated image on your e-paper and wait for a full refresh to complete.
Place two lights at 45-degree angles on either side of the display. This eliminates glare and provides even illumination across the screen.
[Light] [Light]
\ 45° 45° /
\ /
\ /
+-----------------+
| e-paper |
| display |
+-----------------+
Set both lights to 6500K (daylight white point). On Hue lights this is 154 mireds. The exact color temperature matters less than consistency -- what's important is that both lights match and the scene is evenly lit.
Make sure there are no other light sources (overhead lights, windows) that could add a color cast.
- Place a white paper sheet next to the display -- this is your reference for normalizing the measurements
- Shoot in RAW format (not JPEG -- RAW preserves the actual sensor values without white balance or tone curve adjustments)
- Frame the shot to include both the display patches and the paper reference
- Keep the camera perpendicular to the display
- Avoid casting shadows with your body or the camera
Open the RAW file in your photo editor. Sample the average RGB value from the center of each color patch and from the white paper reference.
Record the raw values:
Paper reference: (215, 217, 218)
Black: (22, 11, 30)
White: (156, 172, 175)
Red: (102, 8, 0)
Yellow: (170, 157, 0)
Blue: (0, 59, 119)
Green: (34, 70, 49)
These are raw camera values and depend on your specific lighting setup. The paper reference is what makes them comparable across setups.
The raw values are relative to your lighting conditions. Normalize them against the paper reference so that pure white paper maps to (255, 255, 255):
normalized = raw x (255 / paper_channel)
Applied per channel (R, G, B independently):
Paper reference: (215, 217, 218)
Scale factors: (255/215, 255/217, 255/218) = (1.186, 1.175, 1.170)
Raw Black: (22, 11, 30)
Normalized: (22 x 1.186, 11 x 1.175, 30 x 1.170)
= (26, 13, 35)
Raw Red: (102, 8, 0)
Normalized: (102 x 1.186, 8 x 1.175, 0 x 1.170)
= (121, 9, 0)
Round to the nearest integer. Clamp to [0, 255] if any value exceeds the range.
Use the normalized values to create a ColorPalette:
from epaper_dithering import ColorPalette
my_display = ColorPalette(
colors={
'black': (26, 13, 35),
'white': (185, 202, 205),
'yellow': (202, 184, 0),
'red': (121, 9, 0),
'blue': (0, 69, 139),
'green': (40, 82, 57),
},
accent='red'
)Then use it for dithering:
from epaper_dithering import dither_image, DitherMode
result = dither_image(image, my_display, DitherMode.FLOYD_STEINBERG)Important
Color names and order must match the corresponding ColorScheme.
Reordering colors will break palette encoding.
Check the reference order for your scheme:
from epaper_dithering import ColorScheme
scheme = ColorScheme.BWGBRY
print(list(scheme.palette.colors.keys()))
# ['black', 'white', 'yellow', 'red', 'blue', 'green']Your ColorPalette must use the same key order.
To contribute your measurements:
-
Add a constant to
src/epaper_dithering/palettes.py:MY_DISPLAY_BWR = ColorPalette( colors={ 'black': (5, 5, 5), 'white': (185, 190, 180), 'red': (120, 15, 5), }, accent='red' )
-
Export in
__init__.py -
Submit a PR -- measurements for any display model help the community!
- esp32-photoframe by @aitjcize for measuring actual e-paper display colors, camera calibration methodology