add new dither methods

This commit is contained in:
deng
2026-03-22 14:50:17 +08:00
parent 87eb022933
commit 45dbf7efc9
3 changed files with 232 additions and 106 deletions

View File

@ -4,13 +4,13 @@ version = "0.1.0"
description = "Process images for Waveshare E6 display" description = "Process images for Waveshare E6 display"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = ["pillow>=12.1.1"] dependencies = ["numpy>=2.0", "pillow>=12.1.1"]
[dependency-groups] [dependency-groups]
dev = ["ruff"] dev = ["ruff"]
[tool.ruff] [tool.ruff]
line-length = 150 line-length = 180
target-version = "py313" target-version = "py313"
[tool.ruff.lint] [tool.ruff.lint]

View File

@ -6,127 +6,199 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
import numpy as np
from PIL import Image, ImageEnhance, ImageOps from PIL import Image, ImageEnhance, ImageOps
# E Ink Spectra 6 six basic ink colors: Black, White, Red, Green, Blue, Yellow
SPECTRA6_PALETTE = [
0, 0, 0,
255, 255, 255,
255, 0, 0,
0, 255, 0,
0, 0, 255,
255, 255, 0
]
# Pad the palette to 256 colors (required by PIL), fill the rest with 0 class EPaperConverter:
SPECTRA6_PALETTE += [0] * (256 * 3 - len(SPECTRA6_PALETTE)) """Converts photos for Waveshare E-Paper (E Ink Spectra 6) display output."""
# E Ink Spectra 6 palette: [R, G, B] triplets
SPECTRA6_COLORS = [
[0, 0, 0], # Black
[255, 255, 255], # White
[255, 0, 0], # Red
[0, 255, 0], # Green
[0, 0, 255], # Blue
[255, 255, 0], # Yellow
]
def create_spectra_palette(): BW_COLORS = [
"""Create a reference image with the Spectra 6 palette.""" [0, 0, 0], # Black
pal_img = Image.new('P', (1, 1)) [255, 255, 255], # White
pal_img.putpalette(SPECTRA6_PALETTE) ]
return pal_img
# Optimization presets: {enhancement_attr: multiplier}
ENHANCE_PRESETS = {
'normal': {'brightness': 1.1, 'color': 1.5, 'contrast': 1.2, 'sharpness': 1.0},
'portrait': {'brightness': 1.2, 'color': 1.2, 'contrast': 1.05, 'sharpness': 1.0},
'landscape': {'brightness': 1.0, 'color': 1.8, 'contrast': 1.4, 'sharpness': 2.0},
}
def apply_epaper_gamma_correction(img, gamma=1.2): # Error diffusion kernels: list of (dy, dx, weight) tuples
""" DITHER_KERNELS = {
Apply E-paper specific Gamma correction. 'atkinson': [
Since e-paper is non-emissive, dark areas tend to blend together. # * 1/8 1/8
This function brightens the midtones to rescue dark details. # 1/8 1/8 1/8
""" # 1/8
# Convert Gamma value to a lookup table for the PIL point function # Only diffuses 75% of error → preserves highlights, cleaner look
(0, 1, 1 / 8), (0, 2, 1 / 8),
(1, -1, 1 / 8), (1, 0, 1 / 8), (1, 1, 1 / 8),
(2, 0, 1 / 8),
],
'stucki': [
# * 8/42 4/42
# 2/42 4/42 8/42 4/42 2/42
# 1/42 2/42 4/42 2/42 1/42
# Largest diffusion area → smoothest gradients
(0, 1, 8 / 42), (0, 2, 4 / 42),
(1, -2, 2 / 42), (1, -1, 4 / 42), (1, 0, 8 / 42), (1, 1, 4 / 42), (1, 2, 2 / 42),
(2, -2, 1 / 42), (2, -1, 2 / 42), (2, 0, 4 / 42), (2, 1, 2 / 42), (2, 2, 1 / 42),
],
}
def __init__(self, width=800, height=480, color='rgb', mode='normal', dither='floyd-steinberg'):
self.width = width
self.height = height
self.color = color
self.mode = mode
self.dither = dither
# -- Image enhancement ---------------------------------------------------
def _apply_gamma_correction(self, img, gamma=1.3):
"""Brighten midtones via gamma correction to compensate for e-paper's non-emissive display."""
inv_gamma = 1.0 / gamma inv_gamma = 1.0 / gamma
table = [int((i / 255.0) ** inv_gamma * 255) for i in range(256)] lut = [int((i / 255.0) ** inv_gamma * 255) for i in range(256)]
# Separate RGB channels and apply correction
if img.mode == 'RGB':
r, g, b = img.split() r, g, b = img.split()
r = r.point(table) return Image.merge('RGB', (r.point(lut), g.point(lut), b.point(lut)))
g = g.point(table)
b = b.point(table)
return Image.merge('RGB', (r, g, b))
return img.point(table)
def optimize_image(img, mode):
"""Apply extreme color compensation based on the selected mode to offset physical e-paper attenuation."""
# Apply base Gamma brightening for all color images before conversion
img = apply_epaper_gamma_correction(img, gamma=1.3)
if mode == 'normal':
# Normal mode: Boost saturation and contrast globally
img = ImageEnhance.Color(img).enhance(1.5) # Significantly increase vibrance
img = ImageEnhance.Contrast(img).enhance(1.2) # Strengthen light/dark contrast
img = ImageEnhance.Brightness(img).enhance(1.1) # Slightly brighten overall
elif mode == 'portrait':
# Portrait mode: Protect skin tones. Brighten faces, but limit saturation.
img = ImageEnhance.Brightness(img).enhance(1.2) # Faces must be bright enough
img = ImageEnhance.Color(img).enhance(1.2) # Moderately increase color
img = ImageEnhance.Contrast(img).enhance(1.05) # Maintain soft transitions
elif mode == 'landscape':
# Landscape mode: Maximize greens, blues, and 3D depth
img = ImageEnhance.Color(img).enhance(1.8) # Extreme vibrance for mountains and skies
img = ImageEnhance.Contrast(img).enhance(1.4) # Strong contrast for ridges and clouds
img = ImageEnhance.Sharpness(img).enhance(2.0) # Sharpen edge details
def _optimize(self, img):
"""Apply gamma correction and preset-based enhancement for e-paper output."""
img = self._apply_gamma_correction(img)
preset = self.ENHANCE_PRESETS[self.mode]
for attr, factor in preset.items():
if factor != 1.0:
enhancer_cls = getattr(ImageEnhance, attr.capitalize())
img = enhancer_cls(img).enhance(factor)
return img return img
# -- Dithering -----------------------------------------------------------
def main(): def _build_pil_palette(self, colors):
parser = argparse.ArgumentParser(description='Spectra 6 E-Paper Image Conversion Engine') """Build a PIL palette image from a list of [R, G, B] triplets."""
parser.add_argument('--img_path', type=str, required=True, help='Path to the original photo (e.g., my_photo.jpg)') flat = [v for rgb in colors for v in rgb]
parser.add_argument( flat += [0] * (256 * 3 - len(flat))
'--color', type=str, choices=['rgb', 'bw'], required=True, help="Color mode: 'rgb' (Full color Spectra 6) or 'bw' (Pure black and white)" pal_img = Image.new('P', (1, 1))
) pal_img.putpalette(flat)
parser.add_argument( return pal_img
'--mode',
type=str,
choices=['normal', 'portrait', 'landscape'],
default='normal',
help="Optimization mode: 'normal', 'portrait', 'landscape'. Ignored if color is 'bw'.",
)
parser.add_argument('--width', type=int, default=800, help='E-paper width (default: 800)')
parser.add_argument('--height', type=int, default=480, help='E-paper height (default: 480)')
parser.add_argument('--out', type=str, default=None, help='Custom output filename (default: auto-generated)')
args = parser.parse_args() def _error_diffusion_dither(self, img, palette_colors):
"""Apply custom error diffusion dithering (atkinson / stucki) using NumPy."""
kernel = self.DITHER_KERNELS[self.dither]
palette = np.array(palette_colors, dtype=np.float64)
pixels = np.array(img, dtype=np.float64)
h, w, _ = pixels.shape
img_path = Path(args.img_path) for y in range(h):
for x in range(w):
old_pixel = pixels[y, x].copy()
distances = np.sum((palette - old_pixel) ** 2, axis=1)
new_pixel = palette[np.argmin(distances)]
pixels[y, x] = new_pixel
error = old_pixel - new_pixel
for dy, dx, weight in kernel:
ny, nx = y + dy, x + dx
if 0 <= ny < h and 0 <= nx < w:
pixels[ny, nx] += error * weight
return Image.fromarray(np.clip(pixels, 0, 255).astype(np.uint8), 'RGB')
def _dither_rgb(self, img):
"""Quantize an RGB image to the Spectra 6 palette."""
if self.dither == 'floyd-steinberg':
pal_img = self._build_pil_palette(self.SPECTRA6_COLORS)
return img.quantize(palette=pal_img, dither=Image.FLOYDSTEINBERG).convert('RGB')
return self._error_diffusion_dither(img, self.SPECTRA6_COLORS)
def _dither_bw(self, img):
"""Convert an image to black and white with dithering."""
img = ImageEnhance.Contrast(img).enhance(1.5)
if self.dither == 'floyd-steinberg':
return img.convert('1').convert('RGB')
return self._error_diffusion_dither(img, self.BW_COLORS)
# -- Public API ----------------------------------------------------------
def convert(self, img_path, output_path=None):
"""
Main conversion pipeline: load → resize → enhance → dither → save.
Args:
img_path: Path to the source image.
output_path: Optional output path. Auto-generated if None.
Returns:
Path to the saved output file.
"""
img_path = Path(img_path)
if not img_path.exists(): if not img_path.exists():
raise FileNotFoundError(f'Error: Image file not found: {img_path}') raise FileNotFoundError(f'Image file not found: {img_path}')
print(f'Processing: {img_path}') print(f'Processing: {img_path}')
img = Image.open(img_path).convert('RGB') img = Image.open(img_path).convert('RGB')
# 1. Smart crop and resize to fill the frame # Resize to fit the e-paper frame
img = ImageOps.fit(img, (args.width, args.height), Image.Resampling.LANCZOS) img = ImageOps.fit(img, (self.width, self.height), Image.Resampling.LANCZOS)
# Generate automatic filename if not provided # Enhance and dither
output_name = Path(args.out) if args.out else img_path.with_name(f'{img_path.stem}_{args.color}_{args.mode}.bmp') print(f'Color: {self.color} | Mode: {self.mode} | Dither: {self.dither}')
if self.color == 'rgb':
img = self._optimize(img)
final_img = self._dither_rgb(img)
else:
final_img = self._dither_bw(img)
# 2. Core conversion logic # Save as 24-bit RGB BMP (required by Waveshare e-paper frames)
if args.color == 'rgb': if output_path is None:
print(f'Applying color optimization mode: {args.mode}') output_path = img_path.with_name(f'{img_path.stem}_{self.color}_{self.mode}_{self.dither}.bmp')
img_optimized = optimize_image(img, args.mode) else:
pal_img = create_spectra_palette() output_path = Path(output_path)
# Convert and apply Floyd-Steinberg dithering, then back to 24-bit RGB
# Waveshare e-paper frames only recognize 24-bit RGB BMP files
final_img = img_optimized.quantize(palette=pal_img, dither=Image.FLOYDSTEINBERG).convert('RGB')
elif args.color == 'bw': final_img.save(output_path)
print(f'Applying pure black and white mode (ignoring optimization mode: {args.mode})') print(f'Saved: {output_path}')
# Black and white mode only needs basic contrast stretching return output_path
img_bw = ImageEnhance.Contrast(img).enhance(1.5)
final_img = img_bw.convert('1').convert('RGB')
# 3. Save file (24-bit RGB BMP, required by Waveshare e-paper frames)
final_img.save(output_name) # ---------------------------------------------------------------------------
print(f'Success! Image saved as: {output_name}') # CLI
# ---------------------------------------------------------------------------
def build_parser():
"""Build and return the argument parser."""
parser = argparse.ArgumentParser(description='Spectra 6 E-Paper Image Conversion Engine')
parser.add_argument('--img_path', type=str, required=True, help='Path to the original photo')
parser.add_argument('--color', type=str, choices=['rgb', 'bw'], required=True, help="'rgb' (Spectra 6) or 'bw' (pure black and white)")
parser.add_argument('--mode', type=str, choices=['normal', 'portrait', 'landscape'], default='normal', help='Optimization preset (default: normal)')
parser.add_argument('--dither', type=str, choices=['floyd-steinberg', 'atkinson', 'stucki'], default='floyd-steinberg', help='Dithering algorithm (default: floyd-steinberg)')
parser.add_argument('--width', type=int, default=800, help='E-paper width (default: 800)')
parser.add_argument('--height', type=int, default=480, help='E-paper height (default: 480)')
parser.add_argument('--out', type=str, default=None, help='Custom output filename')
return parser
def main():
args = build_parser().parse_args()
converter = EPaperConverter(
width=args.width,
height=args.height,
color=args.color,
mode=args.mode,
dither=args.dither,
)
converter.convert(args.img_path, output_path=args.out)
if __name__ == '__main__': if __name__ == '__main__':

56
uv.lock generated
View File

@ -2,6 +2,56 @@ version = 1
revision = 3 revision = 3
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]]
name = "numpy"
version = "2.4.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b6/d0/1fe47a98ce0df229238b77611340aff92d52691bcbc10583303181abf7fc/numpy-2.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3", size = 16665297, upload-time = "2026-03-09T07:56:52.296Z" },
{ url = "https://files.pythonhosted.org/packages/27/d9/4e7c3f0e68dfa91f21c6fb6cf839bc829ec920688b1ce7ec722b1a6202fb/numpy-2.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9", size = 14691853, upload-time = "2026-03-09T07:56:54.992Z" },
{ url = "https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee", size = 5198435, upload-time = "2026-03-09T07:56:57.184Z" },
{ url = "https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f", size = 6546347, upload-time = "2026-03-09T07:56:59.531Z" },
{ url = "https://files.pythonhosted.org/packages/bf/ec/7971c4e98d86c564750393fab8d7d83d0a9432a9d78bb8a163a6dc59967a/numpy-2.4.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f", size = 15664626, upload-time = "2026-03-09T07:57:01.385Z" },
{ url = "https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc", size = 16608916, upload-time = "2026-03-09T07:57:04.008Z" },
{ url = "https://files.pythonhosted.org/packages/df/58/2a2b4a817ffd7472dca4421d9f0776898b364154e30c95f42195041dc03b/numpy-2.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476", size = 17015824, upload-time = "2026-03-09T07:57:06.347Z" },
{ url = "https://files.pythonhosted.org/packages/4a/ca/627a828d44e78a418c55f82dd4caea8ea4a8ef24e5144d9e71016e52fb40/numpy-2.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92", size = 18334581, upload-time = "2026-03-09T07:57:09.114Z" },
{ url = "https://files.pythonhosted.org/packages/cd/c0/76f93962fc79955fcba30a429b62304332345f22d4daec1cb33653425643/numpy-2.4.3-cp313-cp313-win32.whl", hash = "sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687", size = 5958618, upload-time = "2026-03-09T07:57:11.432Z" },
{ url = "https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd", size = 12312824, upload-time = "2026-03-09T07:57:13.586Z" },
{ url = "https://files.pythonhosted.org/packages/58/ce/3d07743aced3d173f877c3ef6a454c2174ba42b584ab0b7e6d99374f51ed/numpy-2.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d", size = 10221218, upload-time = "2026-03-09T07:57:16.183Z" },
{ url = "https://files.pythonhosted.org/packages/62/09/d96b02a91d09e9d97862f4fc8bfebf5400f567d8eb1fe4b0cc4795679c15/numpy-2.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875", size = 14819570, upload-time = "2026-03-09T07:57:18.564Z" },
{ url = "https://files.pythonhosted.org/packages/b5/ca/0b1aba3905fdfa3373d523b2b15b19029f4f3031c87f4066bd9d20ef6c6b/numpy-2.4.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070", size = 5326113, upload-time = "2026-03-09T07:57:21.052Z" },
{ url = "https://files.pythonhosted.org/packages/c0/63/406e0fd32fcaeb94180fd6a4c41e55736d676c54346b7efbce548b94a914/numpy-2.4.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73", size = 6646370, upload-time = "2026-03-09T07:57:22.804Z" },
{ url = "https://files.pythonhosted.org/packages/b6/d0/10f7dc157d4b37af92720a196be6f54f889e90dcd30dce9dc657ed92c257/numpy-2.4.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368", size = 15723499, upload-time = "2026-03-09T07:57:24.693Z" },
{ url = "https://files.pythonhosted.org/packages/66/f1/d1c2bf1161396629701bc284d958dc1efa3a5a542aab83cf11ee6eb4cba5/numpy-2.4.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22", size = 16657164, upload-time = "2026-03-09T07:57:27.676Z" },
{ url = "https://files.pythonhosted.org/packages/1a/be/cca19230b740af199ac47331a21c71e7a3d0ba59661350483c1600d28c37/numpy-2.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a", size = 17081544, upload-time = "2026-03-09T07:57:30.664Z" },
{ url = "https://files.pythonhosted.org/packages/b9/c5/9602b0cbb703a0936fb40f8a95407e8171935b15846de2f0776e08af04c7/numpy-2.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349", size = 18380290, upload-time = "2026-03-09T07:57:33.763Z" },
{ url = "https://files.pythonhosted.org/packages/ed/81/9f24708953cd30be9ee36ec4778f4b112b45165812f2ada4cc5ea1c1f254/numpy-2.4.3-cp313-cp313t-win32.whl", hash = "sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c", size = 6082814, upload-time = "2026-03-09T07:57:36.491Z" },
{ url = "https://files.pythonhosted.org/packages/e2/9e/52f6eaa13e1a799f0ab79066c17f7016a4a8ae0c1aefa58c82b4dab690b4/numpy-2.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26", size = 12452673, upload-time = "2026-03-09T07:57:38.281Z" },
{ url = "https://files.pythonhosted.org/packages/c4/04/b8cece6ead0b30c9fbd99bb835ad7ea0112ac5f39f069788c5558e3b1ab2/numpy-2.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02", size = 10290907, upload-time = "2026-03-09T07:57:40.747Z" },
{ url = "https://files.pythonhosted.org/packages/70/ae/3936f79adebf8caf81bd7a599b90a561334a658be4dcc7b6329ebf4ee8de/numpy-2.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4", size = 16664563, upload-time = "2026-03-09T07:57:43.817Z" },
{ url = "https://files.pythonhosted.org/packages/9b/62/760f2b55866b496bb1fa7da2a6db076bef908110e568b02fcfc1422e2a3a/numpy-2.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168", size = 14702161, upload-time = "2026-03-09T07:57:46.169Z" },
{ url = "https://files.pythonhosted.org/packages/32/af/a7a39464e2c0a21526fb4fb76e346fb172ebc92f6d1c7a07c2c139cc17b1/numpy-2.4.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b", size = 5208738, upload-time = "2026-03-09T07:57:48.506Z" },
{ url = "https://files.pythonhosted.org/packages/29/8c/2a0cf86a59558fa078d83805589c2de490f29ed4fb336c14313a161d358a/numpy-2.4.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950", size = 6543618, upload-time = "2026-03-09T07:57:50.591Z" },
{ url = "https://files.pythonhosted.org/packages/aa/b8/612ce010c0728b1c363fa4ea3aa4c22fe1c5da1de008486f8c2f5cb92fae/numpy-2.4.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd", size = 15680676, upload-time = "2026-03-09T07:57:52.34Z" },
{ url = "https://files.pythonhosted.org/packages/a9/7e/4f120ecc54ba26ddf3dc348eeb9eb063f421de65c05fc961941798feea18/numpy-2.4.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24", size = 16613492, upload-time = "2026-03-09T07:57:54.91Z" },
{ url = "https://files.pythonhosted.org/packages/2c/86/1b6020db73be330c4b45d5c6ee4295d59cfeef0e3ea323959d053e5a6909/numpy-2.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0", size = 17031789, upload-time = "2026-03-09T07:57:57.641Z" },
{ url = "https://files.pythonhosted.org/packages/07/3a/3b90463bf41ebc21d1b7e06079f03070334374208c0f9a1f05e4ae8455e7/numpy-2.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0", size = 18339941, upload-time = "2026-03-09T07:58:00.577Z" },
{ url = "https://files.pythonhosted.org/packages/a8/74/6d736c4cd962259fd8bae9be27363eb4883a2f9069763747347544c2a487/numpy-2.4.3-cp314-cp314-win32.whl", hash = "sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a", size = 6007503, upload-time = "2026-03-09T07:58:03.331Z" },
{ url = "https://files.pythonhosted.org/packages/48/39/c56ef87af669364356bb011922ef0734fc49dad51964568634c72a009488/numpy-2.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc", size = 12444915, upload-time = "2026-03-09T07:58:06.353Z" },
{ url = "https://files.pythonhosted.org/packages/9d/1f/ab8528e38d295fd349310807496fabb7cf9fe2e1f70b97bc20a483ea9d4a/numpy-2.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7", size = 10494875, upload-time = "2026-03-09T07:58:08.734Z" },
{ url = "https://files.pythonhosted.org/packages/e6/ef/b7c35e4d5ef141b836658ab21a66d1a573e15b335b1d111d31f26c8ef80f/numpy-2.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657", size = 14822225, upload-time = "2026-03-09T07:58:11.034Z" },
{ url = "https://files.pythonhosted.org/packages/cd/8d/7730fa9278cf6648639946cc816e7cc89f0d891602584697923375f801ed/numpy-2.4.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7", size = 5328769, upload-time = "2026-03-09T07:58:13.67Z" },
{ url = "https://files.pythonhosted.org/packages/47/01/d2a137317c958b074d338807c1b6a383406cdf8b8e53b075d804cc3d211d/numpy-2.4.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093", size = 6649461, upload-time = "2026-03-09T07:58:15.912Z" },
{ url = "https://files.pythonhosted.org/packages/5c/34/812ce12bc0f00272a4b0ec0d713cd237cb390666eb6206323d1cc9cedbb2/numpy-2.4.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a", size = 15725809, upload-time = "2026-03-09T07:58:17.787Z" },
{ url = "https://files.pythonhosted.org/packages/25/c0/2aed473a4823e905e765fee3dc2cbf504bd3e68ccb1150fbdabd5c39f527/numpy-2.4.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611", size = 16655242, upload-time = "2026-03-09T07:58:20.476Z" },
{ url = "https://files.pythonhosted.org/packages/f2/c8/7e052b2fc87aa0e86de23f20e2c42bd261c624748aa8efd2c78f7bb8d8c6/numpy-2.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720", size = 17080660, upload-time = "2026-03-09T07:58:23.067Z" },
{ url = "https://files.pythonhosted.org/packages/f3/3d/0876746044db2adcb11549f214d104f2e1be00f07a67edbb4e2812094847/numpy-2.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5", size = 18380384, upload-time = "2026-03-09T07:58:25.839Z" },
{ url = "https://files.pythonhosted.org/packages/07/12/8160bea39da3335737b10308df4f484235fd297f556745f13092aa039d3b/numpy-2.4.3-cp314-cp314t-win32.whl", hash = "sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0", size = 6154547, upload-time = "2026-03-09T07:58:28.289Z" },
{ url = "https://files.pythonhosted.org/packages/42/f3/76534f61f80d74cc9cdf2e570d3d4eeb92c2280a27c39b0aaf471eda7b48/numpy-2.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b", size = 12633645, upload-time = "2026-03-09T07:58:30.384Z" },
{ url = "https://files.pythonhosted.org/packages/1f/b6/7c0d4334c15983cec7f92a69e8ce9b1e6f31857e5ee3a413ac424e6bd63d/numpy-2.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e", size = 10565454, upload-time = "2026-03-09T07:58:33.031Z" },
]
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "12.1.1" version = "12.1.1"
@ -90,6 +140,7 @@ name = "waveshare-e6-image-processor"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "numpy" },
{ name = "pillow" }, { name = "pillow" },
] ]
@ -99,7 +150,10 @@ dev = [
] ]
[package.metadata] [package.metadata]
requires-dist = [{ name = "pillow", specifier = ">=12.1.1" }] requires-dist = [
{ name = "numpy", specifier = ">=2.0" },
{ name = "pillow", specifier = ">=12.1.1" },
]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [{ name = "ruff" }] dev = [{ name = "ruff" }]