Created
August 1, 2016 20:26
-
-
Save inytar/71a674523cd16e091a982cd3d85d44aa to your computer and use it in GitHub Desktop.
Script that mangles images by switching color comparable spots in the image.
This file contains 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
#! 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