Added support for nonrandom dither, auto dither, refactored test suite again
This commit is contained in:
parent
b4d8fedf74
commit
eca976891f
34
2bit.py
34
2bit.py
|
@ -2,21 +2,38 @@ bits = 2
|
||||||
outfile = "out.png"
|
outfile = "out.png"
|
||||||
per_color = False
|
per_color = False
|
||||||
dither = False
|
dither = False
|
||||||
|
use_non_random_dither = False
|
||||||
|
|
||||||
# End of the configuration section
|
# End of the configuration section
|
||||||
|
|
||||||
import PIL.Image, sys, random
|
import PIL.Image, sys, random
|
||||||
import pip._vendor.progress.bar as libbar
|
import pip._vendor.progress.bar as libbar
|
||||||
|
def format_dither(d):
|
||||||
|
d = float(int(float(dither) * 1000)) / 10
|
||||||
|
return str(d) + "%"
|
||||||
|
def auto_dither(bits):
|
||||||
|
return 1.0 / (bits + 1)
|
||||||
if len(sys.argv) >= 3:
|
if len(sys.argv) >= 3:
|
||||||
outfile = sys.argv[2]
|
outfile = sys.argv[2]
|
||||||
if len(sys.argv) >= 4:
|
if len(sys.argv) >= 4:
|
||||||
bits = int(sys.argv[3])
|
bits = int(sys.argv[3])
|
||||||
args = [f.lower() for f in sys.argv]
|
args = [f.lower() for f in sys.argv]
|
||||||
if "--dither" in args:
|
if "--dither" in args:
|
||||||
dither = float(args[args.index("--dither") + 1].replace("%",""))
|
dither = args[args.index("--dither") + 1].replace("%","")
|
||||||
|
if dither == "auto":
|
||||||
|
dither = auto_dither(bits)
|
||||||
|
print("Using dither of " + format_dither(dither))
|
||||||
|
else:
|
||||||
|
dither = float(dither)
|
||||||
if dither > 1:
|
if dither > 1:
|
||||||
dither /= 100
|
dither /= 100
|
||||||
per_color = "--per-color" in args
|
per_color = "--per-color" in args
|
||||||
|
use_non_random_dither = "--non-random-dither" in args
|
||||||
|
if use_non_random_dither:
|
||||||
|
counter_max = int(args[args.index("--non-random-dither") + 1])
|
||||||
|
if not dither:
|
||||||
|
dither = auto_dither(bits)
|
||||||
|
print("Non-random dither has no effect if dither is disabled. Guessing you want "+format_dither(dither)+"% dither")
|
||||||
def binprecision(start, bits, length):
|
def binprecision(start, bits, length):
|
||||||
end = bin(int(start))[2:]
|
end = bin(int(start))[2:]
|
||||||
while len(end) < bits:
|
while len(end) < bits:
|
||||||
|
@ -33,13 +50,20 @@ colors = [int(i*255.0/(2**bits-1)) for i in range(2**bits)]
|
||||||
bar = libbar.IncrementalBar(max = image.height * image.width)
|
bar = libbar.IncrementalBar(max = image.height * image.width)
|
||||||
bar.start()
|
bar.start()
|
||||||
i = 0
|
i = 0
|
||||||
|
if use_non_random_dither:
|
||||||
|
counter = 1
|
||||||
for x in range(image.width):
|
for x in range(image.width):
|
||||||
for y in range(image.height):
|
for y in range(image.height):
|
||||||
i += 1
|
i += 1
|
||||||
|
if use_non_random_dither:
|
||||||
|
counter += 1
|
||||||
|
counter %= counter_max + 1
|
||||||
bar.index = i
|
bar.index = i
|
||||||
if i % 193 == 0: bar.update() # I used to use 200 but then the last two digits of the current status were always "00" which made it look like the progress bar was fake
|
if i % 193 == 0: bar.update() # I used to use 200 but then the last two digits of the current status were always "00" which made it look like the progress bar was fake
|
||||||
pos = (x,y)
|
pos = (x,y)
|
||||||
color = image.getpixel(pos)
|
color = image.getpixel(pos)
|
||||||
|
if type(color) == int:
|
||||||
|
color = (color,)
|
||||||
if len(color) == 4:
|
if len(color) == 4:
|
||||||
color = color[:3] # Exclude alpha layer
|
color = color[:3] # Exclude alpha layer
|
||||||
color = list(color)
|
color = list(color)
|
||||||
|
@ -47,7 +71,13 @@ for x in range(image.width):
|
||||||
color = [float(sum(color))/len(color)]
|
color = [float(sum(color))/len(color)]
|
||||||
for z in range(len(color)):
|
for z in range(len(color)):
|
||||||
if dither:
|
if dither:
|
||||||
color[z] += 255 * random.uniform(-dither,dither)
|
val = 255 * random.uniform(-dither, dither)
|
||||||
|
if use_non_random_dither:
|
||||||
|
val = 255*float(counter)/counter_max # goes from 0 to 255
|
||||||
|
val *= dither # for dither = .1 and counter max = 4 it goes from 0 to 25.5
|
||||||
|
val -= 255 * dither / 2 # for those values it goes from -12.75 to 12.75
|
||||||
|
val = int(val)
|
||||||
|
color[z] += val
|
||||||
color[z] = min(color[z], 255)
|
color[z] = min(color[z], 255)
|
||||||
color[z] = max(color[z], 0)
|
color[z] = max(color[z], 0)
|
||||||
index = int(binprecision(color[z], 8, bits), 2)
|
index = int(binprecision(color[z], 8, bits), 2)
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
|
|
||||||
Inspired by [2bit](http://2bit.neocities.org/)
|
Inspired by [2bit](http://2bit.neocities.org/)
|
||||||
|
|
||||||
Usage: `python3 2bit.py infile.png [outfile.png] [bits] [--per-color] [--dither dither_value]`
|
Usage: `python3 2bit.py infile.png [outfile.png] [bits] [--per-color] [--dither dither_value] [--non-random-dither num_pixels]`
|
||||||
|
|
||||||
dither\_value can be like `50%`, `50` or `.5`
|
dither\_value can be like `50%`, `50` or `.5`
|
||||||
|
|
||||||
|
Note that using `--non-random-dither` without a dither argument will attempt to guess what dither percentage you want based on the number of bits
|
||||||
|
|
||||||
|
The best number for num\_pixels is usually 7, but it should ideally be coprime with the height of the image
|
||||||
|
|
||||||
Warning: Will probably turn transparency black
|
Warning: Will probably turn transparency black
|
||||||
|
|
||||||
You can change the number of bits for different results. 1 would be just black and white, while 8 would be the original image in greyscale.
|
You can change the number of bits for different results. 1 would be just black and white, while 8 would be the original image in greyscale.
|
||||||
|
|
68
test/run.py
68
test/run.py
|
@ -5,44 +5,44 @@ try:
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
files = glob.glob("*")
|
files = glob.glob("*")
|
||||||
|
del files[files.index("gentest.py")]
|
||||||
del files[files.index("run.py")]
|
del files[files.index("run.py")]
|
||||||
if not os.path.isdir("out"): os.mkdir("out")
|
if not os.path.isdir("out"): os.mkdir("out")
|
||||||
i = 0
|
i = 0
|
||||||
max_i = len(files) * (16*3)
|
max_i = len(files) * (112)
|
||||||
|
def run(bits, percolor, dither, nonrandom):
|
||||||
def getparams(i):
|
if not percolor:
|
||||||
percolor = dither = False
|
percolor = []
|
||||||
i %= 48
|
pc = ""
|
||||||
if i % 16 >= 8:
|
else:
|
||||||
percolor = True
|
percolor = ["--per-color"]
|
||||||
if i >= 16:
|
pc = "per-color-"
|
||||||
dither = '15'
|
if not dither:
|
||||||
if i >= 32:
|
dither = []
|
||||||
dither = '50'
|
dc = ""
|
||||||
return percolor, dither
|
else:
|
||||||
|
dc = "dither-" + dither + "%-"
|
||||||
|
dither = ["--dither", dither]
|
||||||
|
if not nonrandom:
|
||||||
|
nonrandom = []
|
||||||
|
nc = ""
|
||||||
|
else:
|
||||||
|
nc = "nonrandom-dither-" + nonrandom + "pixels-"
|
||||||
|
nonrandom = ["--non-random-dither", nonrandom]
|
||||||
|
bits_formatted = str(bits + 1)
|
||||||
|
outfilename = "out/" + ".".join(f.split(".")[:-1]) + "-output-" + pc + dc + nc + bits_formatted + "bits.png"
|
||||||
|
print("On " + outfilename)
|
||||||
|
subprocess.call(["python3", "../2bit.py", f, outfilename, bits_formatted, *percolor, *dither, *nonrandom])
|
||||||
|
print(str(max_i) + " images to generate")
|
||||||
for f in files:
|
for f in files:
|
||||||
if (os.path.isdir(f)): continue
|
if (os.path.isdir(f)): continue
|
||||||
if (f[-3:]) == '.py': continue
|
if (f[-3:]) == '.py': continue
|
||||||
for bits in range(16*3):
|
for bits in range(8):
|
||||||
bits += 1
|
for dither in [False, "15", "50", "auto"]:
|
||||||
percolor, dither = getparams(i)
|
for nonrandom in [False, "7", "10"]:
|
||||||
if not percolor:
|
for percolor in [False, True]:
|
||||||
percolor = []
|
i += 1
|
||||||
pc = ""
|
run(bits, percolor, dither, nonrandom)
|
||||||
else:
|
print(str(int(float(1000*i)/max_i)/10) + "% done")
|
||||||
percolor = ["--per-color"]
|
|
||||||
pc = "per-color-"
|
|
||||||
if not dither:
|
|
||||||
dither = []
|
|
||||||
dc = ""
|
|
||||||
else:
|
|
||||||
dc = "dither-" + dither + "%-"
|
|
||||||
dither = ["--dither", dither]
|
|
||||||
print(str(int(float(1000*i)/max_i)/10) + "% done")
|
|
||||||
bits_formatted = str((bits-1)%8+1)
|
|
||||||
outfilename = "out/" + ".".join(f.split(".")[:-1]) + "-output-" + pc + dc + bits_formatted + "bits.png"
|
|
||||||
print("On " + outfilename)
|
|
||||||
subprocess.call(["python3", "../2bit.py", f, outfilename, bits_formatted, *percolor, *dither])
|
|
||||||
i += 1
|
|
||||||
print("Done")
|
print("Done")
|
||||||
|
|
Loading…
Reference in New Issue