Last active
December 20, 2015 22:33
-
-
Save sjlongland/6fd8974ef95202d35f5e to your computer and use it in GitHub Desktop.
Decoding itanimulli's images: discussion at https://hackaday.io/project/8762-itanimulli-code-crackers/log/29279-border-graphical-analysis/discussion-43675
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gd | |
import argparse | |
from sys import stdout | |
parser = argparse.ArgumentParser( | |
description='Extract bits from the average colour of each tile', | |
formatter_class=argparse.RawTextHelpFormatter) | |
parser.add_argument('--image', help='Image file to parse') | |
parser.add_argument('--invert', help='Invert the bits', | |
action='store_const', default=False, const=True) | |
parser.add_argument('--binary', help='Write binary', | |
action='store_const', default=False, const=True) | |
parser.add_argument('--bits', help='Number of bits per grouping', | |
type=int, default=8) | |
parser.add_argument('--output', help='Output file') | |
parser.add_argument('--tile-w', type=int, default=12, | |
help='Width of FEZ tile') | |
parser.add_argument('--tile-h', type=int, default=12, | |
help='Height of FEZ tile') | |
parser.add_argument('--offset-x', type=int, default=1, | |
help='X co-ordinate of first tile') | |
parser.add_argument('--offset-y', type=int, default=1, | |
help='Y co-ordinate of first tile') | |
parser.add_argument('--direction', default='rd', | |
help='''Order of traversal, one of: | |
- rd ("right then down": Start top-left, scan to bottom-right, | |
horizontally) | |
- ld ("left then down": Start top-right, scan to bottom-left, | |
horizontally) | |
- ru ("right then up": Start bottom-left, scan to top-right, | |
horizontally) | |
- lu ("left then up": Start bottom-right, scan to top-left, | |
horizontally) | |
- dr ("down then right": Start top-left, scan to bottom-right, | |
vertically) | |
- dl ("down then left": Start top-right, scan to bottom-left, | |
vertically) | |
- ur ("up then right": Start bottom-left, scan to top-right, | |
vertically) | |
- ul ("up then left": Start bottom-right, scan to top-left, | |
vertically)''') | |
args = parser.parse_args() | |
img = gd.image(args.image) | |
if args.output is not None: | |
out = file(args.output,'w') | |
else: | |
out = stdout | |
# Compute number of tiles | |
(p_width, p_height) = img.size() | |
width = (p_width - args.offset_x) // args.tile_w | |
height = (p_height - args.offset_y) // args.tile_h | |
tile_pos = lambda x, y : (((x*args.tile_w) + args.offset_x), \ | |
((y*args.tile_h) + args.offset_y)) | |
# Compute the number of pixels in a tile | |
tile_num = args.tile_w * args.tile_h | |
# Bit threshold | |
threshold = tile_num // 2 | |
# Enumerate a list of co-ordinates to read. | |
if args.direction[0] in ('d','u'): | |
v_dir = args.direction[0] | |
h_dir = args.direction[1] | |
hv_dir = 'v' | |
elif args.direction[0] in ('l','r'): | |
h_dir = args.direction[0] | |
v_dir = args.direction[1] | |
hv_dir = 'h' | |
else: | |
raise ValueError('Invalid direction %r' % args.direction) | |
x_posn = range(0, width) | |
y_posn = range(0, height) | |
if h_dir == 'l': | |
x_posn.reverse() | |
if v_dir == 'u': | |
y_posn.reverse() | |
if hv_dir == 'v': | |
coordinates = [tile_pos(x,y) for x in x_posn for y in y_posn] | |
else: | |
coordinates = [tile_pos(x,y) for y in y_posn for x in x_posn] | |
# Inversion logic | |
if args.invert: | |
read_bit = lambda p : not bool(p) | |
else: | |
read_bit = lambda p : bool(p) | |
# Average bit of tile | |
def avg_bit(c): | |
(tx, ty) = c | |
colour = 0 | |
for py in range(ty, ty + args.tile_h): | |
for px in range(tx, tx + args.tile_w): | |
if read_bit(img.getPixel((px,py))): | |
colour += 1 | |
return colour > threshold | |
# Traverse the image, collect bits. | |
bits = args.bits | |
byte = 0 | |
for c in coordinates: | |
bit = avg_bit(c) | |
if args.binary: | |
byte <<= 1 | |
if bit: | |
byte |= 1 | |
else: | |
if bit: | |
out.write('1') | |
else: | |
out.write('0') | |
bits -= 1 | |
if bits <= 0: | |
bits = args.bits | |
if args.binary: | |
out.write(chr(byte)) | |
byte = 0 | |
else: | |
out.write('\n') | |
if bits != args.bits: | |
if args.binary: | |
out.write(chr(byte)) | |
else: | |
out.write('\n') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment