PaletteOptimiser

Changeset

117:4a75d044c644
2015-10-11 Paul Boddie raw files shortlog changelog graph Added support for choosing the colour combination with the least error. Added interpretation of "-" as the input filename to suppress processing and output generation, thus providing a more concise way of verifying an image.
optimiser.py (file) optimiserlib.py (file)
     1.1 --- a/optimiser.py	Sun Oct 11 20:10:50 2015 +0200
     1.2 +++ b/optimiser.py	Sun Oct 11 23:23:26 2015 +0200
     1.3 @@ -118,11 +118,18 @@
     1.4  -D - Darken the input image (optional float, 1.0 if unspecified)
     1.5  -B - Brighten the input image (optional float, 1.0 if unspecified)
     1.6  
     1.7 --r - Rotate the input image clockwise
     1.8 +-l - Use colours producing the least error
     1.9 +     (slower but useful for fewer than 4 colours)
    1.10 +
    1.11 +-r - Rotate the input image clockwise explicitly
    1.12 +     (EXIF information is used otherwise)
    1.13  -p - Generate a separate preview image
    1.14  -h - Make the preview image with half horizontal resolution (MODE 2)
    1.15  -v - Verify the output image (loaded if -n is given)
    1.16  -n - Generate no output image
    1.17 +
    1.18 +An input filename of - implies the -n option, and is useful for verifying
    1.19 +previously generated images.
    1.20  """ % split(sys.argv[0])[1]
    1.21          sys.exit(1)
    1.22  
    1.23 @@ -149,11 +156,12 @@
    1.24  
    1.25      # General output options.
    1.26  
    1.27 +    least_error = "-l" in options
    1.28      rotate = "-r" in options
    1.29      preview = "-p" in options
    1.30      half_resolution_preview = "-h" in options
    1.31      verify = "-v" in options
    1.32 -    no_normal_output = "-n" in options
    1.33 +    no_normal_output = "-n" in options or input_filename == "-"
    1.34      make_image = not no_normal_output
    1.35  
    1.36      # Load the input image if requested.
    1.37 @@ -184,7 +192,7 @@
    1.38      # Generate an output image if requested.
    1.39  
    1.40      if make_image:
    1.41 -        convert_image(im, number_of_colours)
    1.42 +        convert_image(im, number_of_colours, least_error)
    1.43          im.save(output_filename)
    1.44  
    1.45      # Verify the output image (which may be loaded) if requested.
     2.1 --- a/optimiserlib.py	Sun Oct 11 20:10:50 2015 +0200
     2.2 +++ b/optimiserlib.py	Sun Oct 11 23:23:26 2015 +0200
     2.3 @@ -22,6 +22,7 @@
     2.4  
     2.5  from random import random, randrange
     2.6  import itertools
     2.7 +import math
     2.8  
     2.9  corners = [
    2.10      (0, 0, 0), (255, 0, 0), (0, 255, 0), (255, 255, 0),
    2.11 @@ -36,6 +37,9 @@
    2.12  def clip(v):
    2.13      return int(within(v, 0, 255))
    2.14  
    2.15 +def distance(rgb1, rgb2):
    2.16 +    return math.sqrt(pow(abs(rgb1[0] - rgb2[0]), 2) + pow(abs(rgb1[1] - rgb2[1]), 2) + pow(abs(rgb1[2] - rgb2[2]), 2))
    2.17 +
    2.18  def restore(srgb):
    2.19      r, g, b = srgb
    2.20      return int(r * 255.0), int(g * 255.0), int(b * 255.0)
    2.21 @@ -257,7 +261,7 @@
    2.22                  x += 1
    2.23              y += 1
    2.24  
    2.25 -def convert_image(im, colours):
    2.26 +def convert_image(im, colours, least_error=False):
    2.27  
    2.28      "Convert image 'im' to an appropriate output representation."
    2.29  
    2.30 @@ -271,19 +275,22 @@
    2.31  
    2.32          for l in get_combinations(c, colours):
    2.33              most = [value for f, value in l]
    2.34 -            missing = 0
    2.35 +            error = 0
    2.36  
    2.37              x = 0
    2.38              while x < width:
    2.39                  rgb = im.getpixel((x, y))
    2.40 -                value = get_value(rgb, most, True)
    2.41 -                if value is None:
    2.42 -                    missing += 1
    2.43 +                value = get_value(rgb, most)
    2.44 +                if least_error:
    2.45 +                    error += distance(value, rgb)
    2.46 +                elif value is None:
    2.47 +                    error += 1
    2.48                  x += 1
    2.49  
    2.50 -            if not missing:
    2.51 +            if not least_error and not error:
    2.52                  break # use this combination
    2.53 -            suggestions.append((missing, l))
    2.54 +
    2.55 +            suggestions.append((error, l))
    2.56  
    2.57          # Find the most accurate suggestion.
    2.58