Skip to content

Instantly share code, notes, and snippets.

@inytar
Created August 1, 2016 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inytar/71a674523cd16e091a982cd3d85d44aa to your computer and use it in GitHub Desktop.
Save inytar/71a674523cd16e091a982cd3d85d44aa to your computer and use it in GitHub Desktop.
Script that mangles images by switching color comparable spots in the image.
#! python3
# coding: utf-8
"""Script to mangle images.
This script mangles images by switching color comparable spots in the image.
This script uses the pillow python package.
"""
from __future__ import print_function
import argparse
import collections
import logging
import random
from PIL import Image
log = logging.getLogger('image_mangler')
class Box(collections.namedtuple('Box',
['left', 'upper', 'right', 'lower'])):
"""Describe a box used to get a region from an image."""
@classmethod
def from_steps(cls, left, upper, steps):
return cls(left, upper, left+steps, upper+steps)
@property
def size(self):
return (self.width, self.height)
@property
def width(self):
return self.right - self.left
@property
def height(self):
return self.lower - self.upper
def convert_image(image, colors=32, pixels=128):
img = Image.open(image)
# Create a temporary image with a color palette with just a few colors.
# We use this color palette to get regions with comparable color.
tmp_img = img.convert("P", palette=Image.ADAPTIVE, colors=colors)
log.debug('Image size %sx%s.' % img.size)
# Split the image in pixel pieces in the width.
steps = img.width // pixels
log.debug('Box size = %sx%s' % (steps, steps))
boxes_by_color = collections.defaultdict(list)
total_boxes = 0
for row in range(0, img.width, steps):
for col in range(0, img.height, steps):
# Box should never go over the size of the image.
box = Box(row, col, min(row + steps, img.width),
min(col + steps, img.height))
region = tmp_img.crop(box)
# Get the most dominent color in the region.
region_color = sorted(region.getcolors())[-1][1]
boxes_by_color[region_color].append(box)
total_boxes += 1
if not total_boxes % 4000:
log.debug('Read %s boxes' % total_boxes)
log.debug('Total boxes: %s.' % total_boxes)
final_img = img.copy()
box_n = 0
rotations = (None, Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
Image.ROTATE_90, Image.ROTATE_180, Image.ROTATE_270,
Image.TRANSPOSE)
for color, boxes in boxes_by_color.items():
# Get the real RGB color of the this color from the palette of tmp_img.
# Palette of tmp_img is a list of rgb values, not split into tuples!
color_location = color * 3
# Image.new wants a tuple.
rgb_color = tuple(
tmp_img.getpalette()[color_location:color_location+3]
)
# Shuffle the boxes.
random.shuffle(boxes)
# For every box in the boxes, make sure this is a copy of boxes as
# we are going to edit it inline.
for box in boxes[:]:
# Move the first (current) box to the back.
boxes = boxes[1:] + boxes[:1]
org_box = boxes[0]
# Get the first (now next) box from the orignal image.
# Create an square new region of size steps, in the correct color.
new_region = Image.new(img.mode, (steps, steps), rgb_color)
# Get original data.
org_data = img.crop(org_box).getdata()
# If orginal box width is smaller than steps, we can't just
# copy the data, but have to insert empty data to make sure
# the rows are set in the correct location.
if org_box.width != steps:
tmp_data = list(org_data)
org_data = []
missing_data = [rgb_color] * (steps - org_box.width)
for i in range(org_box.height):
row_start = i * org_box.width
row_end = (i + 1) * org_box.width
org_data += tmp_data[row_start:row_end] + missing_data
# Paste the original data into the new region.
new_region.putdata(org_data)
# Add a random rotation to the newly placed box.
rotation = random.choice(rotations)
# If rotation is None, just leave as is.
if rotation:
new_region = new_region.transpose(rotation)
# Crop new_region to the size of the original box.
new_region = new_region.crop(Box(0, 0, box.width, box.height))
final_img.paste(new_region, box)
if not box_n % 4000:
log.debug('Pasted box %s/%s' % (box_n, total_boxes))
box_n += 1
return final_img
def main():
parser = argparse.ArgumentParser(
'convert_image',
description=__doc__)
parser.add_argument('image', help='The image to mangle.')
parser.add_argument('out', help='The output file.')
parser.add_argument(
'--colors',
help='The amount of colors used to compare image spots.', type=int,
default=32)
parser.add_argument('--pixels',
help='The amount of mangled "pixels" the width of '
'the image will have.',
type=int, default=128)
args = parser.parse_args()
out_img = convert_image(args.image, colors=args.colors, pixels=args.pixels)
out_img.save(args.out)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment