commit 28d07dd7aaa0dab0a390e2cc2761b222f443e679 Author: sloumdrone Date: Sun Jun 2 21:47:38 2019 -0700 First commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lid b/lid new file mode 100755 index 0000000..17e01a9 --- /dev/null +++ b/lid @@ -0,0 +1,143 @@ +#!/usr/bin/python3 +# lid: lo-fi image dithering +# written by sloum +# licence: public domain - use it, improve it, share it! +######### + +from PIL import Image +import sys +import os.path + +# Main program to output the new image +def dither(r): + im = Image.open(r["path"]).convert("L") + width, height = im.size + new = create_image(width, height) + newpixels = new.load() + + dots = [ + [64, 128], + [192, 0] + ] + + count = 0 + print(" dithering...\033[?25l", end="") + for row in range(0, height): + if not row % 7: + print_spinner(count) + count += 1 + if count > 3: + count = 0 + for col in range(0, width): + dotrow = 1 if row % 2 else 0 + dotcol = 1 if col % 2 else 0 + impx = get_pixel(im, col, row) + newpixels[col, row] = int(impx > dots[dotrow][dotcol]) + + new.save("{}.{}".format(r["out"], r["-f"]), r["-f"].upper(), optimize=True, quality=25) + print("\033[20C\nfinished\033[?25h") + +def print_spinner(n): + parts = ["- "," - "," - "," -"] + print("\r{}".format(parts[n]), end="") + +# Create a new image with the given size +def create_image(i, j): + image = Image.new("1", (i, j)) + return image + + +# Get the pixel from the given image +def get_pixel(image, i, j): + width, height = image.size + if i > width or j > height: + return None + + # Get Pixel + pixel = image.getpixel((i, j)) + return pixel + + +def display_help(): + helptext = """ + lid - lo-fi image dithering + + syntax: + lid [option]... [infile] [outfile] + + options: + -f output format. defaults to the recommended setting: png + -q image quality. defaults to 90. only has effect on png/jpg + + arguments: + infile path to a valid image file on your system + outfile file name to output, do not include file extension + + example usage: + lid -f jpeg -q 75 ~/my_picture.png my_dithered_picture + """ + print(helptext) + + +# Get and validate the arguments +# returns dict or None +def parse_input(): + args = sys.argv[1:] + argmap = { + "-f": "png", # output format + "-q": 90, # optimization quality + "path": None, # path to file + "out": "lid_file" # output filename + } + if len(args) < 1: + print("lid error: expected image filepath, received None") + return None + + if "help" in args or "-h" in args or "--help" in args: + display_help() + sys.exit(1) + + item = None + for x in args: + if x in argmap: + item = x + continue + elif item: + argmap[item] = x + item = None + elif len(x) > 0 and x[0] == "-": + print("lid error: unknown flag {}".format(x)) + return None + else: + if argmap["path"]: + argmap["out"] = x + else: + argmap["path"] = x + + if not argmap["-f"] in ["jpeg", "jpg", "png", "gif", None]: + print("lid error: invalid format requested with -f flag. Options: jpeg, png, gif. Defaults to png") + return None + elif argmap["-f"] == "jpg": + argmap["-f"] = "jpeg" + + try: + argmap["-q"] = int(argmap["-q"]) + if argmap["-q"] < 1 or argmap["-q"] > 100: + print("lid error: -q value must be between 1 and 100") + return None + except ValueError: + print("lid error: -q value must be an integer") + return None + + if not argmap["path"] or not os.path.isfile(argmap["path"]): + print("lid error: invalid filepath") + return None + + return argmap + +if __name__ == "__main__": + request = parse_input() + if request: + dither(request) + sys.exit(0) + sys.exit(1)