2016-07-08 20:13:22 +02:00
bits = 2
outfile = " out.png "
2016-07-09 01:21:09 +02:00
per_color = False
2016-07-11 17:15:59 +02:00
dither = False
2016-07-21 18:43:11 +02:00
use_non_random_dither = False
2016-07-08 20:13:22 +02:00
# End of the configuration section
2016-07-11 17:15:59 +02:00
import PIL . Image , sys , random
2016-07-11 17:37:06 +02:00
import pip . _vendor . progress . bar as libbar
2016-07-21 18:43:11 +02:00
def format_dither ( d ) :
d = float ( int ( float ( dither ) * 1000 ) ) / 10
return str ( d ) + " % "
def auto_dither ( bits ) :
return 1.0 / ( bits + 1 )
2016-07-08 20:57:10 +02:00
if len ( sys . argv ) > = 3 :
outfile = sys . argv [ 2 ]
if len ( sys . argv ) > = 4 :
bits = int ( sys . argv [ 3 ] )
2016-07-11 17:15:59 +02:00
args = [ f . lower ( ) for f in sys . argv ]
if " --dither " in args :
2016-07-21 18:43:11 +02:00
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 )
2016-07-11 17:15:59 +02:00
if dither > 1 :
dither / = 100
per_color = " --per-color " in args
2016-07-21 18:43:11 +02:00
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 )
2016-07-21 18:45:54 +02:00
print ( " Non-random dither has no effect if dither is disabled. Guessing you want " + format_dither ( dither ) + " dither " )
2016-07-08 20:13:22 +02:00
def binprecision ( start , bits , length ) :
end = bin ( int ( start ) ) [ 2 : ]
while len ( end ) < bits :
end = ' 0 ' + end
return end [ : length ]
image = PIL . Image . open ( sys . argv [ 1 ] )
2016-07-09 01:21:09 +02:00
mode = " L " # 1x8 bit unsigned integer
if per_color :
mode = " RGB "
elif bits == 1 :
mode = " 1 " # 1x1 bit unsigned integer
2016-07-08 20:13:22 +02:00
out = PIL . Image . new ( mode , image . size )
colors = [ int ( i * 255.0 / ( 2 * * bits - 1 ) ) for i in range ( 2 * * bits ) ]
2016-07-11 17:32:38 +02:00
bar = libbar . IncrementalBar ( max = image . height * image . width )
bar . start ( )
i = 0
2016-07-21 18:43:11 +02:00
if use_non_random_dither :
counter = 1
2016-07-08 20:13:22 +02:00
for x in range ( image . width ) :
for y in range ( image . height ) :
2016-07-11 17:32:38 +02:00
i + = 1
2016-07-21 18:43:11 +02:00
if use_non_random_dither :
counter + = 1
counter % = counter_max + 1
2016-07-11 17:32:38 +02:00
bar . index = i
2016-07-11 23:46:56 +02:00
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
2016-07-08 20:13:22 +02:00
pos = ( x , y )
color = image . getpixel ( pos )
2016-07-21 18:43:11 +02:00
if type ( color ) == int :
color = ( color , )
2016-07-08 20:13:22 +02:00
if len ( color ) == 4 :
color = color [ : 3 ] # Exclude alpha layer
2016-07-09 01:21:09 +02:00
color = list ( color )
if not per_color :
color = [ float ( sum ( color ) ) / len ( color ) ]
for z in range ( len ( color ) ) :
2016-07-11 17:15:59 +02:00
if dither :
2016-07-21 18:43:11 +02:00
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
2016-07-21 20:13:04 +02:00
val * = 2 # 0 to +51
val - = 255 * dither # -25.5 to +25.5
2016-07-21 18:43:11 +02:00
val = int ( val )
color [ z ] + = val
2016-07-11 17:15:59 +02:00
color [ z ] = min ( color [ z ] , 255 )
color [ z ] = max ( color [ z ] , 0 )
2016-07-09 01:21:09 +02:00
index = int ( binprecision ( color [ z ] , 8 , bits ) , 2 )
color [ z ] = colors [ index ]
out . putpixel ( pos , tuple ( color ) )
2016-07-08 20:13:22 +02:00
out . save ( outfile )
2016-07-11 23:46:56 +02:00
bar . update ( ) # Otherwise it will display the last multiple of 193 out of the total value
2016-07-11 17:32:38 +02:00
bar . finish ( )