# HG changeset patch # User Paul Boddie # Date 1444427301 -7200 # Node ID ba632fd74cbb14d16f59105e1cce956998300f1d # Parent 5b3e85002c1048fd41536b731b48c3a9b0bc63b4 Introduced a separate main program for potential Shedskin analysis. diff -r 5b3e85002c10 -r ba632fd74cbb main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.py Fri Oct 09 23:48:21 2015 +0200 @@ -0,0 +1,174 @@ +#!/usr/bin/env python + +""" +Convert and optimise images for display in an Acorn Electron MODE 1 variant +with four colours per line but eight colours available for selection on each +line. + +Copyright (C) 2015 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +""" + +from optimiser import * +from os.path import split, splitext +import EXIF +import PIL.Image +import sys + +def rotate_and_scale(exif, im, width, height, rotate): + + """ + Using the given 'exif' information, rotate and scale image 'im' given the + indicated 'width' and 'height' constraints and any explicit 'rotate' + indication. The returned image will be within the given 'width' and + 'height', filling either or both, and preserve its original aspect ratio. + """ + + if rotate or exif and exif["Image Orientation"].values == [6L]: + im = im.rotate(270) + + w, h = im.size + if w > h: + height = (width * h) / w + else: + width = (height * w) / h + + return im.resize((width, height)) + +def test(): + + "Generate slices of the colour cube." + + size = 64 + for r in (0, 63, 127, 191, 255): + pim = PIL.Image.new("RGB", (size, size)) + im = SimpleImage(list(pim.getdata()), pim.size) + test_slice(im, size, r) + pim.putdata(im.getdata()) + pim.save("rgb%d.png" % r) + +def test_flat(rgb): + + "Generate a flat image for the colour 'rgb'." + + size = 64 + pim = PIL.Image.new("RGB", (size, size)) + im = SimpleImage(list(pim.getdata()), pim.size) + test_flat_slice(im, size, rgb) + pim.putdata(im.getdata()) + pim.save("rgb%02d%02d%02d.png" % rgb) + +def get_float(options, flag): + try: + i = options.index(flag) + if i+1 < len(options) and options[i+1].isdigit(): + return float(options[i+1]) + else: + return 1.0 + except ValueError: + return 0.0 + +# Main program. + +if __name__ == "__main__": + + # Test options. + + if "--test" in sys.argv: + test() + sys.exit(0) + elif "--test-flat" in sys.argv: + test_flat((120, 40, 60)) + sys.exit(0) + elif "--help" in sys.argv: + print >>sys.stderr, """\ +Usage: %s [ ] + +Options are... + +-s - Saturate the input image (can be followed by a float, default 1.0) +-d - Desaturate the input image (can be followed by a float, default 1.0) +-D - Darken the input image (can be followed by a float, default 1.0) +-B - Brighten the input image (can be followed by a float, default 1.0) + +-r - Rotate the input image clockwise +-p - Generate a separate preview image +-h - Make the preview image with half horizontal resolution (MODE 2) +-v - Verify the output image (loaded if -n is given) +-n - Generate no output image +""" % split(sys.argv[0])[1] + sys.exit(1) + + width = 320 + height = 256 + + input_filename, output_filename = sys.argv[1:3] + basename, ext = splitext(output_filename) + preview_filename = "".join([basename + "_preview", ext]) + + options = sys.argv[3:] + + # Preprocessing options that can be repeated for extra effect. + + saturate = get_float(options, "-s") + desaturate = get_float(options, "-d") + darken = get_float(options, "-D") + brighten = get_float(options, "-B") + + # General output options. + + rotate = "-r" in options + preview = "-p" in options + half_resolution_preview = "-h" in options + verify = "-v" in options + no_normal_output = "-n" in options + make_image = not no_normal_output + + # Load the input image if requested. + + if make_image or preview: + exif = EXIF.process_file(open(input_filename)) + pim = PIL.Image.open(input_filename).convert("RGB") + pim = rotate_and_scale(exif, pim, width, height, rotate) + im = SimpleImage(list(pim.getdata()), pim.size) + process_image(im, saturate, desaturate, darken, brighten) + + # Generate a preview if requested. + + if preview: + imp = preview_image(im, half_resolution_preview) + pimp = pim.copy() + pimp.putdata(imp.getdata()) + pimp.save(preview_filename) + + # Generate an output image if requested. + + if make_image: + convert_image(im) + pim.putdata(im.getdata()) + pim.save(output_filename) + + # Verify the output image (which may be loaded) if requested. + + if verify: + if no_normal_output: + pim = PIL.Image.open(output_filename).convert("RGB") + im = SimpleImage(list(pim.getdata()), pim.size) + + result = count_colours(im, 4) + if result is not None: + y, colours = result + print "Image %s: row %d has the following colours: %s" % (output_filename, y, "; ".join([repr(c) for c in colours])) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 5b3e85002c10 -r ba632fd74cbb optimiser.py --- a/optimiser.py Fri Oct 09 22:16:49 2015 +0200 +++ b/optimiser.py Fri Oct 09 23:48:21 2015 +0200 @@ -21,9 +21,6 @@ """ from random import random, randrange -from os.path import split, splitext -import EXIF -import PIL.Image import itertools import math import sys @@ -208,49 +205,16 @@ all.sort(reverse=True) return [l for total, l in all] -def test(): - - "Generate slices of the colour cube." +def test_slice(im, size, r): + for g in range(0, size): + for b in range(0, size): + value = get_value((r, (g * 256) / size, (b * 256 / size))) + im.putpixel((g, b), value) - size = 512 - for r in (0, 63, 127, 191, 255): - im = PIL.Image.new("RGB", (size, size)) - for g in range(0, size): - for b in range(0, size): - value = get_value((r, (g * 256) / size, (b * 256 / size))) - im.putpixel((g, b), value) - im.save("rgb%d.png" % r) - -def test_flat(rgb): - - "Generate a flat image for the colour 'rgb'." - - size = 64 - im = PIL.Image.new("RGB", (size, size)) +def test_flat_slice(im, size, rgb): for y in range(0, size): for x in range(0, size): im.putpixel((x, y), get_value(rgb)) - im.save("rgb%02d%02d%02d.png" % rgb) - -def rotate_and_scale(exif, im, width, height, rotate): - - """ - Using the given 'exif' information, rotate and scale image 'im' given the - indicated 'width' and 'height' constraints and any explicit 'rotate' - indication. The returned image will be within the given 'width' and - 'height', filling either or both, and preserve its original aspect ratio. - """ - - if rotate or exif and exif["Image Orientation"].values == [6L]: - im = im.rotate(270) - - w, h = im.size - if w > h: - height = (width * h) / w - else: - width = (height * w) / h - - return im.resize((width, height)) def count_colours(im, colours): @@ -263,9 +227,7 @@ width, height = im.size for y in range(0, height): - l = set() - for x in range(0, width): - l.add(im.getpixel((x, y))) + l = set(im.getdata()[y * width:(y+1) * width]) if len(l) > colours: return (y, l) return None @@ -344,101 +306,44 @@ rgbn = tuple(map(lambda i: clip(i[0] + (i[1] - i[2]) / 2.0), zip(rgbn, rgb, value))) im.putpixel((x, y+1), rgbn) -def get_float(options, flag): - try: - i = options.index(flag) - if i+1 < len(options) and options[i+1].isdigit(): - return float(options[i+1]) - else: - return 1.0 - except ValueError: - return 0.0 +class SimpleImage: + + "An image behaving like PIL.Image." + + def __init__(self, data, size): + self.data = data + self.width, self.height = self.size = size + + def copy(self): + return SimpleImage(self.data[:], self.size) -# Main program. + def getpixel(self, xy): + x, y = xy + return self.data[y * self.width + x] + + def putpixel(self, xy, value): + x, y = xy + self.data[y * self.width + x] = value + + def getdata(self): + return self.data + +# Test program. if __name__ == "__main__": - - # Test options. - - if "--test" in sys.argv: - test() - sys.exit(0) - elif "--test-flat" in sys.argv: - test_flat((120, 40, 60)) - sys.exit(0) - elif "--help" in sys.argv: - print >>sys.stderr, """\ -Usage: %s [ ] - -Options are... + data = [(0, 0, 0)] * 1024 + size = (32, 32) --s - Saturate the input image (can be followed by a float, default 1.0) --d - Desaturate the input image (can be followed by a float, default 1.0) --D - Darken the input image (can be followed by a float, default 1.0) --B - Brighten the input image (can be followed by a float, default 1.0) - --r - Rotate the input image clockwise --p - Generate a separate preview image --h - Make the preview image with half horizontal resolution (MODE 2) --v - Verify the output image (loaded if -n is given) --n - Generate no output image -""" % split(sys.argv[0])[1] - sys.exit(1) - - width = 320 - height = 256 - - input_filename, output_filename = sys.argv[1:3] - basename, ext = splitext(output_filename) - preview_filename = "".join([basename + "_preview", ext]) - - options = sys.argv[3:] - - # Preprocessing options that can be repeated for extra effect. + im = SimpleImage(data, size) - saturate = get_float(options, "-s") - desaturate = get_float(options, "-d") - darken = get_float(options, "-D") - brighten = get_float(options, "-B") - - # General output options. - - rotate = "-r" in options - preview = "-p" in options - half_resolution_preview = "-h" in options - verify = "-v" in options - no_normal_output = "-n" in options - make_image = not no_normal_output - - # Load the input image if requested. - - if make_image or preview: - exif = EXIF.process_file(open(input_filename)) - im = PIL.Image.open(input_filename).convert("RGB") - im = rotate_and_scale(exif, im, width, height, rotate) + process_image(im, 1.0, 0.0, 1.0, 0.0) + imp = preview_image(im, False) + convert_image(im) - process_image(im, saturate, desaturate, darken, brighten) - - # Generate a preview if requested. - - if preview: - preview_image(im, half_resolution_preview).save(preview_filename) - - # Generate an output image if requested. + test_im = SimpleImage(data, size) + test_slice(test_im, 32, 0) - if make_image: - convert_image(im) - im.save(output_filename) - - # Verify the output image (which may be loaded) if requested. - - if verify: - if no_normal_output: - im = PIL.Image.open(output_filename).convert("RGB") - - result = count_colours(im, 4) - if result is not None: - y, colours = result - print "Image %s: row %d has the following colours: %s" % (output_filename, y, "; ".join([repr(c) for c in colours])) + test_flat_im = SimpleImage(data, size) + test_flat_slice(test_flat_im, 32, (200, 100, 50)) # vim: tabstop=4 expandtab shiftwidth=4