#!/usr/bin/env python3 """Quick ASCII art generator from images. Usage: python ascii_tool.py test # Run self-test python ascii_tool.py [width] # Convert local image Produces ASCII art output saved to output_ascii.txt """ import sys from PIL import Image ASCII_CHARS = "@%#*+=-:. " def resize_image(img: Image.Image, new_width: int = 80) -> Image.Image: """Resize image maintaining aspect ratio.""" w, h = img.size aspect_ratio = h / w new_height = int(aspect_ratio * new_width * 0.55) return img.resize((new_width, max(1, new_height))) def image_to_ascii(img: Image.Image, width: int = 80) -> str: """Convert PIL Image to ASCII art string.""" img = resize_image(img, width) img = img.convert("L") # Use get_flattened_data for Pillow 11+, fallback to getdata() try: pixels = list(img.get_flattened_data()) except AttributeError: pixels = list(img.getdata()) chars_per_pixel = len(ASCII_CHARS) lines = [] row = [] for i, pixel in enumerate(pixels): if i > 0 and (i % width) == 0: lines.append("".join(row)) row = [] idx = min(pixel * (chars_per_pixel - 1) // 255, chars_per_pixel - 1) row.append(ASCII_CHARS[idx]) if row: lines.append("".join(row)) return "\n".join(lines) def run_self_test(): """Create test image and verify ASCII conversion works.""" # Create a simple 20x20 test image with a gradient img = Image.new("RGB", (20, 20)) pixels = img.load() for x in range(20): for y in range(20): # Create a diagonal gradient val = int((x + y) * 255 / 40) pixels[x, y] = (val, val, val) ascii_result = image_to_ascii(img, 40) lines = ascii_result.split("\n") print(f"Self-test: Created 20x20 gradient image") print(f"ASCII result: {len(lines)} lines") print(f"First line: '{lines[0][:20]}...'") print(f"Line lengths vary: {set(len(l) for l in lines)} chars") # Verify output is saved output_path = "/root/.hermes/wizard-outputs/spark/output_ascii.txt" with open(output_path, "w") as f: f.write(ascii_result) print(f"Saved to {output_path}") # Verify file exists with open(output_path) as f: verify = f.read() if verify == ascii_result: print("VERIFICATION: File written correctly") return True else: print("ERROR: File content mismatch") return False def main(): if len(sys.argv) < 2: print(__doc__) sys.exit(1) if sys.argv[1] == "test": success = run_self_test() sys.exit(0 if success else 1) # Convert local image file img_path = sys.argv[1] width = int(sys.argv[2]) if len(sys.argv) > 2 else 80 print(f"Loading {img_path}...") img = Image.open(img_path) print(f"Original size: {img.size}, converting to ASCII (width={width})...") ascii_art = image_to_ascii(img, width) print(ascii_art) output_path = "/root/.hermes/wizard-outputs/spark/output_ascii.txt" with open(output_path, "w") as f: f.write(ascii_art) print(f"\nSaved to {output_path}") if __name__ == "__main__": main()