Updated algorithm options
This commit is contained in:
parent
5736885a92
commit
65a9b1cf37
150
lid
150
lid
|
@ -8,8 +8,8 @@ from PIL import Image
|
|||
import sys
|
||||
import os.path
|
||||
|
||||
# Main program to output the new image
|
||||
def dither(r):
|
||||
# Default dithering: 4 level ordered dither
|
||||
def ordered_dither_4(r):
|
||||
im = Image.open(r["path"]).convert("L")
|
||||
width, height = im.size
|
||||
new = create_image(width, height)
|
||||
|
@ -20,26 +20,128 @@ def dither(r):
|
|||
[192, 0]
|
||||
]
|
||||
|
||||
count = 0
|
||||
print(" dithering...\033[?25l", end="")
|
||||
total_px_count = width * height
|
||||
counter = 1
|
||||
last_percent_val = 0
|
||||
last_hash_val = 0
|
||||
print("Ordered dither: 4 level")
|
||||
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])
|
||||
counter += 1
|
||||
new_percent_val = round(counter / total_px_count * 100)
|
||||
new_hash_val = round(counter / total_px_count * 10)
|
||||
if new_percent_val != last_percent_val or new_hash_val != last_hash_val:
|
||||
print("\r{:>3}% |{:<10}|".format(new_percent_val, "#" * new_hash_val), end="")
|
||||
last_percent_val = new_percent_val
|
||||
last_hash_val = new_hash_val
|
||||
|
||||
new.save("{}.{}".format(r["out"], r["-f"]), r["-f"].upper(), optimize=True, quality=25)
|
||||
new.save("{}.{}".format(r["out"], r["-f"]), r["-f"].upper(), optimize=True, quality=r["-q"])
|
||||
print("\033[20C\nfinished\033[?25h")
|
||||
|
||||
# 9 level ordered dither
|
||||
def ordered_dither_9(r):
|
||||
im = Image.open(r["path"]).convert("L")
|
||||
width, height = im.size
|
||||
new = create_image(width, height)
|
||||
newpixels = new.load()
|
||||
|
||||
dots = [
|
||||
[0, 196, 84],
|
||||
[168, 140, 56],
|
||||
[112, 28, 224]
|
||||
]
|
||||
|
||||
total_px_count = width * height
|
||||
counter = 1
|
||||
last_percent_val = 0
|
||||
last_hash_val = 0
|
||||
print("Ordered dither: 9 level")
|
||||
for row in range(0, height):
|
||||
for col in range(0, width):
|
||||
if not row % 3:
|
||||
dotrow = 2
|
||||
elif not row % 2:
|
||||
dotrow = 1
|
||||
else:
|
||||
dotrow = 0
|
||||
|
||||
if not col % 3:
|
||||
dotcol = 2
|
||||
elif not col % 2:
|
||||
dotcol = 1
|
||||
else:
|
||||
dotcol = 0
|
||||
impx = get_pixel(im, col, row)
|
||||
newpixels[col, row] = int(impx > dots[dotrow][dotcol])
|
||||
counter += 1
|
||||
new_percent_val = round(counter / total_px_count * 100)
|
||||
new_hash_val = round(counter / total_px_count * 10)
|
||||
if new_percent_val != last_percent_val or new_hash_val != last_hash_val:
|
||||
print("\r{:>3}% |{:<10}|".format(new_percent_val, "#" * new_hash_val), end="")
|
||||
last_percent_val = new_percent_val
|
||||
last_hash_val = new_hash_val
|
||||
|
||||
new.save("{}.{}".format(r["out"], r["-f"]), r["-f"].upper(), optimize=True, quality=r["-q"])
|
||||
print("\033[20C\nfinished\033[?25h")
|
||||
|
||||
# Alternate dither mode
|
||||
def error_dither(r):
|
||||
im = Image.open(r["path"]).convert("L")
|
||||
width, height = im.size
|
||||
new = create_image(width, height)
|
||||
newpixels = new.load()
|
||||
i = im.load()
|
||||
|
||||
total_px_count = width * height
|
||||
counter = 1
|
||||
last_percent_val = 0
|
||||
last_hash_val = 0
|
||||
print("Error diffusion dither")
|
||||
for row in range(0, height):
|
||||
for col in range(0, width):
|
||||
newpixels[col, row] = update_error(im, i, row, col)
|
||||
counter += 1
|
||||
new_percent_val = round(counter / total_px_count * 100)
|
||||
new_hash_val = round(counter / total_px_count * 10)
|
||||
if new_percent_val != last_percent_val or new_hash_val != last_hash_val:
|
||||
print("\r{:>3}% |{:<10}|".format(new_percent_val, "#" * new_hash_val), end="")
|
||||
last_percent_val = new_percent_val
|
||||
last_hash_val = new_hash_val
|
||||
|
||||
new.save("{}.{}".format(r["out"], r["-f"]), r["-f"].upper(), optimize=True, quality=r["-q"])
|
||||
print("\033[20C\nfinished\033[?25h")
|
||||
|
||||
|
||||
def update_error(im, i, r, c):
|
||||
w, h = im.size
|
||||
current = get_pixel(im, c, r)
|
||||
if current > 128:
|
||||
res = 1
|
||||
diff = -(255 - current)
|
||||
else:
|
||||
res = 0
|
||||
diff = abs(0 - current)
|
||||
mov = [[0, 1, 0.4375],[1, 1, 0.0625],[1, 0, 0.3125],[1, -1, 0.1875]]
|
||||
for x in mov:
|
||||
if r + x[0] >= h or c + x[1] >= w or c + x[1] <= 0:
|
||||
continue
|
||||
p = get_pixel(im, c + x[1], r + x[0])
|
||||
p = round(diff * x[2] + p)
|
||||
if p < 0:
|
||||
p = 0
|
||||
elif p > 255:
|
||||
p = 255
|
||||
i[c + x[1], r + x[0]] = p
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def print_spinner(n):
|
||||
parts = ["- "," - "," - "," -"]
|
||||
print("\r{}".format(parts[n]), end="")
|
||||
print("\r|{:<10}|".format("#" * n), end="")
|
||||
|
||||
# Create a new image with the given size
|
||||
def create_image(i, j):
|
||||
|
@ -66,15 +168,16 @@ def display_help():
|
|||
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
|
||||
-f output format. defaults to: png
|
||||
-q image quality. defaults to: 90. only has effect on png/jpg
|
||||
-m dither mode. defaults to: o4. other options: e, o9
|
||||
|
||||
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
|
||||
lid -f jpeg -q 75 -m e ~/my_picture.png my_dithered_picture
|
||||
"""
|
||||
print(helptext)
|
||||
|
||||
|
@ -85,7 +188,8 @@ def parse_input():
|
|||
args = sys.argv[1:]
|
||||
argmap = {
|
||||
"-f": "png", # output format
|
||||
"-q": 90, # optimization quality
|
||||
"-q": 25, # optimization quality
|
||||
"-m": "o4", # mode, defaults to ordered
|
||||
"path": None, # path to file
|
||||
"out": "lid_file" # output filename
|
||||
}
|
||||
|
@ -120,6 +224,10 @@ def parse_input():
|
|||
elif argmap["-f"] == "jpg":
|
||||
argmap["-f"] = "jpeg"
|
||||
|
||||
if not argmap["-m"] in ["o4", "e", "o9"]:
|
||||
print("lid error: invalid dither mode provided for -m. Options: o4, o9, e. Default: o4")
|
||||
return None
|
||||
|
||||
try:
|
||||
argmap["-q"] = int(argmap["-q"])
|
||||
if argmap["-q"] < 1 or argmap["-q"] > 100:
|
||||
|
@ -138,6 +246,14 @@ def parse_input():
|
|||
if __name__ == "__main__":
|
||||
request = parse_input()
|
||||
if request:
|
||||
dither(request)
|
||||
if request["-m"] == "o4":
|
||||
ordered_dither_4(request)
|
||||
elif request["-m"] == "o9":
|
||||
ordered_dither_9(request)
|
||||
elif request["-m"] == "e":
|
||||
error_dither(request)
|
||||
else:
|
||||
print("Unknown mode flag")
|
||||
sys.exit(2)
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
|
|
Loading…
Reference in New Issue
Block a user