commit a313fc5ee107548bd635c55f8b4c3c6aa7661d7f Author: eightball Date: Wed Jan 7 00:05:25 2026 +0000 Add script.py Signed-off-by: eightball diff --git a/script.py b/script.py new file mode 100644 index 0000000..f95e28e --- /dev/null +++ b/script.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import argparse +import sys +from pathlib import Path +from typing import Optional + +try: + from PIL import Image +except ImportError: + print( + "Error: Pillow is not installed. Install it with 'pip install Pillow' or use the project venv.", + file=sys.stderr, + ) + sys.exit(1) + + +def to_bw_char(value: int, threshold: int, invert: bool) -> str: + is_black = value < threshold + if invert: + is_black = not is_black + return 'B' if is_black else 'W' + + +def convert_image( + input_path: Path, + output_path: Path, + width: Optional[int], + height: Optional[int], + threshold: int, + invert: bool, +) -> None: + # Load image + img = Image.open(input_path) + # Convert to grayscale (luminance) + img = img.convert('L') + + # Resize if requested + if width is not None or height is not None: + # Preserve aspect ratio if only one dimension is provided + if width is not None and height is not None: + target_size = (width, height) + else: + w, h = img.size + if width is not None and height is None: + height = round(h * (width / w)) + elif height is not None and width is None: + width = round(w * (height / h)) + target_size = (width, height) + img = img.resize(target_size, resample=Image.NEAREST) + + pixels = img.load() + w, h = img.size + + lines = [] + for y in range(h): + row_chars = [] + for x in range(w): + row_chars.append(to_bw_char(pixels[x, y], threshold, invert)) + lines.append(''.join(row_chars)) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text('\n'.join(lines), encoding='utf-8') + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Convert an image to bad_apple_frame.txt format (W=white, B=black)." + ) + # Input aliases: --input/-i and --image + p.add_argument( + '--input', '-i', dest='input', type=Path, required=False, + help='Path to input image (PNG, JPG, GIF, etc.)' + ) + p.add_argument( + '--image', dest='input', type=Path, required=False, + help='Alias for --input' + ) + # Output aliases: --output/-o and --out + p.add_argument( + '--output', '-o', dest='output', type=Path, required=False, + help='Path to output text file (defaults to _frame.txt)' + ) + p.add_argument( + '--out', dest='output', type=Path, required=False, + help='Alias for --output' + ) + p.add_argument('--width', type=int, help='Optional output width in pixels') + p.add_argument('--height', type=int, + help='Optional output height in pixels') + p.add_argument( + '--threshold', type=int, default=128, + help='Grayscale threshold 0..255 (default: 128)' + ) + p.add_argument('--invert', action='store_true', + help='Invert mapping (B/W flipped)') + args = p.parse_args() + + if args.input is None: + p.error("--input/--image is required") + + if args.output is None: + args.output = args.input.with_name(f"{args.input.stem}_frame.txt") + + return args + + +def main() -> None: + args = parse_args() + convert_image( + input_path=args.input, + output_path=args.output, + width=args.width, + height=args.height, + threshold=max(0, min(255, args.threshold)), + invert=args.invert, + ) + + +if __name__ == '__main__': + main()