#!/usr/bin/env python3 """Command-line interface for pixel sorting.""" import argparse import sys from pathlib import Path from PIL import Image from pixelsort import pixelsort, analyze, INTERVAL_FUNCTIONS, SORTING_FUNCTIONS def print_analysis(stats): """Pretty print analysis results.""" brightness_stats = stats['brightness_stats'] pixel_int_stats = stats['pixel_int_stats'] suggestions = stats['suggestions'] print("=" * 60) print("BRIGHTNESS ANALYSIS (0-255 scale)") print("=" * 60) print(f" Min: {brightness_stats['min']:.1f}") print(f" Max: {brightness_stats['max']:.1f}") print(f" Mean: {brightness_stats['mean']:.1f}") print(f" Median: {brightness_stats['median']:.1f}") print() print(" Percentiles:") print(f" 10%: {brightness_stats['p10']:.1f}") print(f" 25%: {brightness_stats['p25']:.1f}") print(f" 50%: {brightness_stats['median']:.1f} (median)") print(f" 75%: {brightness_stats['p75']:.1f}") print(f" 90%: {brightness_stats['p90']:.1f}") print() print("=" * 60) print("PIXEL INTEGER ANALYSIS (signed 32-bit)") print("=" * 60) print(f" Min: {pixel_int_stats['min']:,}") print(f" Max: {pixel_int_stats['max']:,}") print(f" Median: {pixel_int_stats['median']:,}") print() print(" Percentiles:") print(f" 25%: {pixel_int_stats['p25']:,}") print(f" 50%: {pixel_int_stats['median']:,} (median)") print(f" 75%: {pixel_int_stats['p75']:,}") print() print("=" * 60) print("SUGGESTED THRESHOLDS") print("=" * 60) print() print("For --interval bright (sorts pixels above threshold):") print(f" Subtle (top 25%): --bright-value {suggestions['bright']['subtle']}") print(f" Moderate (top 50%): --bright-value {suggestions['bright']['moderate']}") print(f" Aggressive (top 75%): --bright-value {suggestions['bright']['aggressive']}") print() print("For --interval dark (sorts pixels below threshold):") print(f" Subtle (bottom 25%): --dark-value {suggestions['dark']['subtle']}") print(f" Moderate (bottom 50%): --dark-value {suggestions['dark']['moderate']}") print(f" Aggressive (bottom 75%): --dark-value {suggestions['dark']['aggressive']}") print() print("For --interval white (sorts pixels above threshold):") print(f" Subtle (top 25%): --white-value {suggestions['white']['subtle']}") print(f" Moderate (top 50%): --white-value {suggestions['white']['moderate']}") print(f" Aggressive (top 75%): --white-value {suggestions['white']['aggressive']}") print() print("For --interval black (sorts pixels below threshold):") print(f" Subtle (bottom 25%): --black-value {suggestions['black']['subtle']}") print(f" Moderate (bottom 50%): --black-value {suggestions['black']['moderate']}") print(f" Aggressive (bottom 75%): --black-value {suggestions['black']['aggressive']}") print() print("=" * 60) def main(): parser = argparse.ArgumentParser( description='Pixel sorting with ASDF and extended modes', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # ASDF-style modes (classic) python main.py input.jpg --interval bright python main.py input.jpg --interval dark --dark-value 50 # New interval modes python main.py input.jpg --interval random --char-length 100 python main.py input.jpg --interval edges python main.py input.jpg --interval waves --char-length 75 # Different sorting functions python main.py input.jpg --interval bright --sort hue python main.py input.jpg --interval threshold --sort saturation # Angle and randomness python main.py input.jpg --interval bright --angle 45 python main.py input.jpg --interval random --randomness 20 # Analyze image first python main.py input.jpg --analyze """ ) parser.add_argument('input', type=str, help='Input image path') parser.add_argument('--analyze', action='store_true', help='Analyze image and suggest thresholds (does not sort)') # Interval mode parser.add_argument('-i', '--interval', type=str, default='bright', choices=list(INTERVAL_FUNCTIONS.keys()), help='Interval function (default: bright)') # Sorting function parser.add_argument('-s', '--sort', type=str, default='value', choices=list(SORTING_FUNCTIONS.keys()), help='Sorting function (default: value)') # Output parser.add_argument('-o', '--output', type=str, help='Output image path') # Parameters parser.add_argument('-a', '--angle', type=float, default=0, help='Angle in degrees (default: 0)') parser.add_argument('-r', '--randomness', type=float, default=0, help='Randomness percentage 0-100 (default: 0)') parser.add_argument('-c', '--char-length', type=int, default=50, help='Characteristic length for random/waves (default: 50)') parser.add_argument('-l', '--loops', type=int, default=1, help='Number of sorting passes (default: 1)') # Threshold values parser.add_argument('--lower-threshold', type=float, default=0.25, help='Lower threshold 0-1 for threshold mode (default: 0.25)') parser.add_argument('--upper-threshold', type=float, default=0.8, help='Upper threshold 0-1 for threshold mode (default: 0.8)') parser.add_argument('--white-value', type=int, default=-12345678, help='White threshold for white mode (default: -12345678)') parser.add_argument('--black-value', type=int, default=-3456789, help='Black threshold for black mode (default: -3456789)') parser.add_argument('--bright-value', type=int, default=127, help='Brightness threshold for bright mode (default: 127)') parser.add_argument('--dark-value', type=int, default=223, help='Darkness threshold for dark mode (default: 223)') args = parser.parse_args() # Load image input_path = Path(args.input) if not input_path.exists(): print(f"Error: Input file '{args.input}' not found") return 1 print(f"Loading image: {args.input}") img = Image.open(input_path) # If analyze mode, run analysis and exit if args.analyze: stats = analyze(img) print_analysis(stats) return 0 # Determine output path if args.output: output_path = Path(args.output) else: output_path = input_path.with_name(f"{input_path.stem}_{args.interval}_{args.sort}.png") # Sort the image print(f"Sorting with interval={args.interval}, sorting={args.sort}") if args.angle != 0: print(f" Angle: {args.angle}°") if args.randomness > 0: print(f" Randomness: {args.randomness}%") if args.loops > 1: print(f" Loops: {args.loops}") result = pixelsort( img, interval=args.interval, sorting=args.sort, angle=args.angle, randomness=args.randomness, char_length=args.char_length, loops=args.loops, lower_threshold=args.lower_threshold, upper_threshold=args.upper_threshold, white_value=args.white_value, black_value=args.black_value, bright_value=args.bright_value, dark_value=args.dark_value, ) # Save output print(f"Saving to: {output_path}") result.save(output_path) print("Done!") return 0 if __name__ == "__main__": sys.exit(main())