Created
December 12, 2016 02:04
-
-
Save kylemcdonald/3273da5a510629d5df54cd52ff8cc941 to your computer and use it in GitHub Desktop.
Some tools for drawing pixel-accurate images in Jupyter notebooks with Python.
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
from cStringIO import StringIO | |
import numpy as np | |
import math | |
import PIL.Image | |
import IPython.display | |
import shutil | |
def show_array(a, fmt='png', filename=None): | |
a = np.uint8(np.clip(a, 0, 255)) | |
image_data = StringIO() | |
PIL.Image.fromarray(a).save(image_data, fmt) | |
if filename is None: | |
IPython.display.display(IPython.display.Image(data=image_data.getvalue())) | |
else: | |
with open(filename, 'w') as f: | |
image_data.seek(0) | |
shutil.copyfileobj(image_data, f) | |
def find_rectangle(n, max_ratio=2): | |
sides = [] | |
square = int(math.sqrt(n)) | |
for w in range(square, max_ratio * square): | |
h = n / w | |
used = w * h | |
leftover = n - used | |
sides.append((leftover, (w, h))) | |
return sorted(sides)[0][1] | |
# should work for 1d and 2d images, assumes images are square but can be overriden | |
def make_mosaic(images, n=None, nx=None, ny=None, w=None, h=None): | |
if n is None and nx is None and ny is None: | |
nx, ny = find_rectangle(len(images)) | |
else: | |
nx = n if nx is None else nx | |
ny = n if ny is None else ny | |
images = np.array(images) | |
if images.ndim == 2: | |
side = int(np.sqrt(len(images[0]))) | |
h = side if h is None else h | |
w = side if w is None else w | |
images = images.reshape(-1, h, w) | |
else: | |
h = images.shape[1] | |
w = images.shape[2] | |
image_gen = iter(images) | |
mosaic = np.empty((h*ny, w*nx)) | |
for i in range(ny): | |
ia = (i)*h | |
ib = (i+1)*h | |
for j in range(nx): | |
ja = j*w | |
jb = (j+1)*w | |
mosaic[ia:ib, ja:jb] = next(image_gen) | |
return mosaic | |
def map(x, in_min, in_max, out_min, out_max): | |
return out_min + (out_max - out_min) * (x - in_min) / (in_max - in_min) | |
def plot_images(images, xy, w=None, h=None, blend=np.maximum, canvas_shape=(512, 512), fill=0): | |
if images.ndim == 2: | |
side = int(np.sqrt(len(images[0]))) | |
h = side if h is None else h | |
w = side if w is None else w | |
images = images.reshape(-1, h, w) | |
else: | |
h = images.shape[1] | |
w = images.shape[2] | |
min_xy = np.amin(xy, 0) | |
max_xy = np.amax(xy, 0) | |
min_canvas = np.array((0, 0)) | |
max_canvas = np.array((canvas_shape[0] - h, canvas_shape[1] - w)) | |
canvas = np.full(canvas_shape, fill) | |
for image, pos in zip(images, xy): | |
x_off, y_off = map(pos, min_xy, max_xy, min_canvas, max_canvas).astype(int) | |
for y in range(0, h): | |
cy = y_off + y | |
for x in range(0, w): | |
cx = x_off + x | |
canvas[cy, cx] = blend(canvas[cy, cx], image[y, x]) | |
return canvas |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment