template-processor: use triangle/bilinear resize for M3 and use box resize for ours.

This commit is contained in:
Lemmy
2026-02-08 09:33:00 -05:00
parent 6e4a302f31
commit 642980347b
2 changed files with 21 additions and 10 deletions
+12 -6
View File
@@ -234,7 +234,7 @@ def _sample_jpeg_colors(data: bytes, width: int, height: int) -> list[RGB]:
return pixels if pixels else [(128, 128, 128)]
def _read_image_imagemagick(path: Path) -> list[RGB]:
def _read_image_imagemagick(path: Path, resize_filter: str = "Triangle") -> list[RGB]:
"""
Read image using ImageMagick's convert command.
@@ -249,7 +249,8 @@ def _read_image_imagemagick(path: Path) -> list[RGB]:
# ppm: output as PPM format (easy to parse)
# Resize to 112x112 to match matugen's color extraction
# Use -filter Triangle (bilinear) to match matugen's FilterType::Triangle default
# Use -filter Triangle (bilinear) for M3 schemes to match matugen's FilterType::Triangle default
# Use -filter Box for k-means schemes (sharper, preserves distinct color regions)
# Use -depth 8 -colorspace sRGB -strip to reduce variance between HDRI/non-HDRI builds
resize_spec = "112x112!"
@@ -257,14 +258,14 @@ def _read_image_imagemagick(path: Path) -> list[RGB]:
# Try 'magick' first (ImageMagick 7+), fallback to 'convert' (ImageMagick 6)
try:
result = subprocess.run(
['magick', str(path), '-filter', 'Triangle', '-resize', resize_spec,
['magick', str(path), '-filter', resize_filter, '-resize', resize_spec,
'-depth', '8', '-colorspace', 'sRGB', '-strip', 'ppm:-'],
capture_output=True,
check=True
)
except FileNotFoundError:
result = subprocess.run(
['convert', str(path), '-filter', 'Triangle', '-resize', resize_spec,
['convert', str(path), '-filter', resize_filter, '-resize', resize_spec,
'-depth', '8', '-colorspace', 'sRGB', '-strip', 'ppm:-'],
capture_output=True,
check=True
@@ -341,18 +342,23 @@ def _parse_ppm(data: bytes) -> list[RGB]:
return pixels
def read_image(path: Path) -> list[RGB]:
def read_image(path: Path, resize_filter: str = "Triangle") -> list[RGB]:
"""
Read an image file and return its pixels as RGB tuples.
Uses ImageMagick for accurate color extraction from any format.
Falls back to native PNG parsing if ImageMagick is unavailable.
Args:
path: Path to the image file.
resize_filter: ImageMagick resize filter. "Triangle" for M3 schemes
(matches matugen), "Box" for k-means schemes.
"""
suffix = path.suffix.lower()
# Try ImageMagick first (works for any format)
try:
return _read_image_imagemagick(path)
return _read_image_imagemagick(path, resize_filter)
except ImageReadError:
# Fall back to native parsing for PNG
if suffix == '.png':
@@ -251,8 +251,16 @@ def main() -> int:
print(f"Error: Not a file: {args.image}", file=sys.stderr)
return 1
# Determine scheme type
scheme_type = args.scheme_type
# M3 schemes use Triangle filter (matches matugen), others use Box
# (sharper downscale preserves distinct color regions for k-means)
m3_schemes = {"tonal-spot", "content", "fruit-salad", "rainbow", "monochrome"}
resize_filter = "Triangle" if scheme_type in m3_schemes else "Box"
try:
pixels = read_image(args.image)
pixels = read_image(args.image, resize_filter)
except ImageReadError as e:
print(f"Error reading image: {e}", file=sys.stderr)
return 1
@@ -260,9 +268,6 @@ def main() -> int:
print(f"Unexpected error reading image: {e}", file=sys.stderr)
return 1
# Determine scheme type
scheme_type = args.scheme_type
# Extract palette based on scheme type:
# - M3 schemes (tonal-spot, fruit-salad, rainbow, content): Use Wu quantizer + Score
# This matches matugen's color extraction exactly