1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/main.py Fri Oct 09 23:48:21 2015 +0200
1.3 @@ -0,0 +1,174 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Convert and optimise images for display in an Acorn Electron MODE 1 variant
1.8 +with four colours per line but eight colours available for selection on each
1.9 +line.
1.10 +
1.11 +Copyright (C) 2015 Paul Boddie <paul@boddie.org.uk>
1.12 +
1.13 +This program is free software; you can redistribute it and/or modify it under
1.14 +the terms of the GNU General Public License as published by the Free Software
1.15 +Foundation; either version 3 of the License, or (at your option) any later
1.16 +version.
1.17 +
1.18 +This program is distributed in the hope that it will be useful, but WITHOUT ANY
1.19 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1.20 +PARTICULAR PURPOSE. See the GNU General Public License for more details.
1.21 +
1.22 +You should have received a copy of the GNU General Public License along
1.23 +with this program. If not, see <http://www.gnu.org/licenses/>.
1.24 +"""
1.25 +
1.26 +from optimiser import *
1.27 +from os.path import split, splitext
1.28 +import EXIF
1.29 +import PIL.Image
1.30 +import sys
1.31 +
1.32 +def rotate_and_scale(exif, im, width, height, rotate):
1.33 +
1.34 + """
1.35 + Using the given 'exif' information, rotate and scale image 'im' given the
1.36 + indicated 'width' and 'height' constraints and any explicit 'rotate'
1.37 + indication. The returned image will be within the given 'width' and
1.38 + 'height', filling either or both, and preserve its original aspect ratio.
1.39 + """
1.40 +
1.41 + if rotate or exif and exif["Image Orientation"].values == [6L]:
1.42 + im = im.rotate(270)
1.43 +
1.44 + w, h = im.size
1.45 + if w > h:
1.46 + height = (width * h) / w
1.47 + else:
1.48 + width = (height * w) / h
1.49 +
1.50 + return im.resize((width, height))
1.51 +
1.52 +def test():
1.53 +
1.54 + "Generate slices of the colour cube."
1.55 +
1.56 + size = 64
1.57 + for r in (0, 63, 127, 191, 255):
1.58 + pim = PIL.Image.new("RGB", (size, size))
1.59 + im = SimpleImage(list(pim.getdata()), pim.size)
1.60 + test_slice(im, size, r)
1.61 + pim.putdata(im.getdata())
1.62 + pim.save("rgb%d.png" % r)
1.63 +
1.64 +def test_flat(rgb):
1.65 +
1.66 + "Generate a flat image for the colour 'rgb'."
1.67 +
1.68 + size = 64
1.69 + pim = PIL.Image.new("RGB", (size, size))
1.70 + im = SimpleImage(list(pim.getdata()), pim.size)
1.71 + test_flat_slice(im, size, rgb)
1.72 + pim.putdata(im.getdata())
1.73 + pim.save("rgb%02d%02d%02d.png" % rgb)
1.74 +
1.75 +def get_float(options, flag):
1.76 + try:
1.77 + i = options.index(flag)
1.78 + if i+1 < len(options) and options[i+1].isdigit():
1.79 + return float(options[i+1])
1.80 + else:
1.81 + return 1.0
1.82 + except ValueError:
1.83 + return 0.0
1.84 +
1.85 +# Main program.
1.86 +
1.87 +if __name__ == "__main__":
1.88 +
1.89 + # Test options.
1.90 +
1.91 + if "--test" in sys.argv:
1.92 + test()
1.93 + sys.exit(0)
1.94 + elif "--test-flat" in sys.argv:
1.95 + test_flat((120, 40, 60))
1.96 + sys.exit(0)
1.97 + elif "--help" in sys.argv:
1.98 + print >>sys.stderr, """\
1.99 +Usage: %s <input filename> <output filename> [ <options> ]
1.100 +
1.101 +Options are...
1.102 +
1.103 +-s - Saturate the input image (can be followed by a float, default 1.0)
1.104 +-d - Desaturate the input image (can be followed by a float, default 1.0)
1.105 +-D - Darken the input image (can be followed by a float, default 1.0)
1.106 +-B - Brighten the input image (can be followed by a float, default 1.0)
1.107 +
1.108 +-r - Rotate the input image clockwise
1.109 +-p - Generate a separate preview image
1.110 +-h - Make the preview image with half horizontal resolution (MODE 2)
1.111 +-v - Verify the output image (loaded if -n is given)
1.112 +-n - Generate no output image
1.113 +""" % split(sys.argv[0])[1]
1.114 + sys.exit(1)
1.115 +
1.116 + width = 320
1.117 + height = 256
1.118 +
1.119 + input_filename, output_filename = sys.argv[1:3]
1.120 + basename, ext = splitext(output_filename)
1.121 + preview_filename = "".join([basename + "_preview", ext])
1.122 +
1.123 + options = sys.argv[3:]
1.124 +
1.125 + # Preprocessing options that can be repeated for extra effect.
1.126 +
1.127 + saturate = get_float(options, "-s")
1.128 + desaturate = get_float(options, "-d")
1.129 + darken = get_float(options, "-D")
1.130 + brighten = get_float(options, "-B")
1.131 +
1.132 + # General output options.
1.133 +
1.134 + rotate = "-r" in options
1.135 + preview = "-p" in options
1.136 + half_resolution_preview = "-h" in options
1.137 + verify = "-v" in options
1.138 + no_normal_output = "-n" in options
1.139 + make_image = not no_normal_output
1.140 +
1.141 + # Load the input image if requested.
1.142 +
1.143 + if make_image or preview:
1.144 + exif = EXIF.process_file(open(input_filename))
1.145 + pim = PIL.Image.open(input_filename).convert("RGB")
1.146 + pim = rotate_and_scale(exif, pim, width, height, rotate)
1.147 + im = SimpleImage(list(pim.getdata()), pim.size)
1.148 + process_image(im, saturate, desaturate, darken, brighten)
1.149 +
1.150 + # Generate a preview if requested.
1.151 +
1.152 + if preview:
1.153 + imp = preview_image(im, half_resolution_preview)
1.154 + pimp = pim.copy()
1.155 + pimp.putdata(imp.getdata())
1.156 + pimp.save(preview_filename)
1.157 +
1.158 + # Generate an output image if requested.
1.159 +
1.160 + if make_image:
1.161 + convert_image(im)
1.162 + pim.putdata(im.getdata())
1.163 + pim.save(output_filename)
1.164 +
1.165 + # Verify the output image (which may be loaded) if requested.
1.166 +
1.167 + if verify:
1.168 + if no_normal_output:
1.169 + pim = PIL.Image.open(output_filename).convert("RGB")
1.170 + im = SimpleImage(list(pim.getdata()), pim.size)
1.171 +
1.172 + result = count_colours(im, 4)
1.173 + if result is not None:
1.174 + y, colours = result
1.175 + print "Image %s: row %d has the following colours: %s" % (output_filename, y, "; ".join([repr(c) for c in colours]))
1.176 +
1.177 +# vim: tabstop=4 expandtab shiftwidth=4