PaletteOptimiser

Change of optimiser.py

83:3f96e51bfacb
optimiser.py simpleimage-shedskin
     1.1 --- a/optimiser.py	Sat Oct 10 15:25:12 2015 +0200
     1.2 +++ b/optimiser.py	Sat Oct 10 15:36:48 2015 +0200
     1.3 @@ -20,160 +20,13 @@
     1.4  with this program.  If not, see <http://www.gnu.org/licenses/>.
     1.5  """
     1.6  
     1.7 -from random import random, randrange
     1.8 +from optimiserlib import *
     1.9  from os.path import split, splitext
    1.10  import EXIF
    1.11  import PIL.Image
    1.12  import itertools
    1.13  import sys
    1.14  
    1.15 -corners = [
    1.16 -    (0, 0, 0), (255, 0, 0), (0, 255, 0), (255, 255, 0),
    1.17 -    (0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255)
    1.18 -    ]
    1.19 -
    1.20 -# Basic colour operations.
    1.21 -
    1.22 -def within(v, lower, upper):
    1.23 -    return min(max(v, lower), upper)
    1.24 -
    1.25 -def clip(v):
    1.26 -    return int(within(v, 0, 255))
    1.27 -
    1.28 -def restore(srgb):
    1.29 -    r, g, b = srgb
    1.30 -    return int(r * 255.0), int(g * 255.0), int(b * 255.0)
    1.31 -
    1.32 -def scale(rgb):
    1.33 -    r, g, b = rgb
    1.34 -    return r / 255.0, g / 255.0, b / 255.0
    1.35 -
    1.36 -def invert(srgb):
    1.37 -    r, g, b = srgb
    1.38 -    return 1.0 - r, 1.0 - g, 1.0 - b
    1.39 -
    1.40 -scaled_corners = map(scale, corners)
    1.41 -zipped_corners = zip(corners, scaled_corners)
    1.42 -
    1.43 -# Colour distribution functions.
    1.44 -
    1.45 -def combination(rgb):
    1.46 -
    1.47 -    "Return the colour distribution for 'rgb'."
    1.48 -
    1.49 -    # Get the colour with components scaled from 0 to 1, plus the inverted
    1.50 -    # component values.
    1.51 -
    1.52 -    srgb = scale(rgb)
    1.53 -    rgbi = invert(srgb)
    1.54 -    pairs = zip(rgbi, srgb)
    1.55 -
    1.56 -    # For each corner of the colour cube (primary and secondary colours plus
    1.57 -    # black and white), calculate the corner value's contribution to the
    1.58 -    # input colour.
    1.59 -
    1.60 -    d = []
    1.61 -    for corner, scaled in zipped_corners:
    1.62 -        rs, gs, bs = scaled
    1.63 -
    1.64 -        # Obtain inverted channel values where corner channels are low;
    1.65 -        # obtain original channel values where corner channels are high.
    1.66 -
    1.67 -        d.append((pairs[0][int(rs)] * pairs[1][int(gs)] * pairs[2][int(bs)], corner))
    1.68 -
    1.69 -    # Balance the corner contributions.
    1.70 -
    1.71 -    return balance(d)
    1.72 -
    1.73 -def complements(rgb):
    1.74 -
    1.75 -    "Return 'rgb' and its complement."
    1.76 -
    1.77 -    r, g, b = rgb
    1.78 -    return rgb, restore(invert(scale(rgb)))
    1.79 -
    1.80 -bases = [(0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255)]
    1.81 -base_complements = map(complements, bases)
    1.82 -
    1.83 -def balance(d):
    1.84 -
    1.85 -    """
    1.86 -    Balance distribution 'd', cancelling opposing values and their complements
    1.87 -    and replacing their common contributions with black and white contributions.
    1.88 -    """
    1.89 -
    1.90 -    d = dict([(value, f) for f, value in d])
    1.91 -    for primary, secondary in base_complements:
    1.92 -        common = min(d[primary], d[secondary])
    1.93 -        d[primary] -= common
    1.94 -        d[secondary] -= common
    1.95 -    return [(f, value) for value, f in d.items()]
    1.96 -
    1.97 -def combine(d):
    1.98 -
    1.99 -    "Combine distribution 'd' to get a colour value."
   1.100 -
   1.101 -    out = [0, 0, 0]
   1.102 -    for v, rgb in d:
   1.103 -        out[0] += v * rgb[0]
   1.104 -        out[1] += v * rgb[1]
   1.105 -        out[2] += v * rgb[2]
   1.106 -    return tuple(map(int, out))
   1.107 -
   1.108 -def pattern(rgb, chosen=None):
   1.109 -
   1.110 -    """
   1.111 -    Obtain a sorted colour distribution for 'rgb', optionally limited to any
   1.112 -    specified 'chosen' colours.
   1.113 -    """
   1.114 -
   1.115 -    l = [(f, value) for f, value in combination(rgb) if not chosen or value in chosen]
   1.116 -    l.sort(reverse=True)
   1.117 -    return l
   1.118 -
   1.119 -def get_value(rgb, chosen=None, fail=False):
   1.120 -
   1.121 -    """
   1.122 -    Get an output colour for 'rgb', optionally limited to any specified 'chosen'
   1.123 -    colours. If 'fail' is set to a true value, return None if the colour cannot
   1.124 -    be expressed using any of the chosen colours.
   1.125 -    """
   1.126 -
   1.127 -    l = pattern(rgb, chosen)
   1.128 -    limit = sum([f for f, c in l])
   1.129 -    if not limit:
   1.130 -        if fail:
   1.131 -            return None
   1.132 -        else:
   1.133 -            return l[randrange(0, len(l))][1]
   1.134 -
   1.135 -    choose = random() * limit
   1.136 -    threshold = 0
   1.137 -    for f, c in l:
   1.138 -        threshold += f
   1.139 -        if choose < threshold:
   1.140 -            return c
   1.141 -    return c
   1.142 -
   1.143 -# Colour processing operations.
   1.144 -
   1.145 -def sign(x):
   1.146 -    return x >= 0 and 1 or -1
   1.147 -
   1.148 -def saturate_rgb(rgb, exp):
   1.149 -    r, g, b = rgb
   1.150 -    return saturate_value(r, exp), saturate_value(g, exp), saturate_value(b, exp)
   1.151 -
   1.152 -def saturate_value(x, exp):
   1.153 -    return int(127.5 + sign(x - 127.5) * 127.5 * pow(abs(x - 127.5) / 127.5, exp))
   1.154 -
   1.155 -def amplify_rgb(rgb, exp):
   1.156 -    r, g, b = rgb
   1.157 -    return amplify_value(r, exp), amplify_value(g, exp), amplify_value(b, exp)
   1.158 -
   1.159 -def amplify_value(x, exp):
   1.160 -    return int(pow(x / 255.0, exp) * 255.0)
   1.161 -
   1.162  # Image operations.
   1.163  
   1.164  def get_colours(im, y):