Skip to content

Instantly share code, notes, and snippets.

@kylemcdonald
Created December 12, 2016 02:04
Show Gist options
  • Save kylemcdonald/3273da5a510629d5df54cd52ff8cc941 to your computer and use it in GitHub Desktop.
Save kylemcdonald/3273da5a510629d5df54cd52ff8cc941 to your computer and use it in GitHub Desktop.
Some tools for drawing pixel-accurate images in Jupyter notebooks with Python.
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