## Transforming images into networks

Trying to come up with a cool visualization for a small side-project, i was contemplating how to draw, or approximate, an object using networks. During my creative process i remembered my colleague and friend Piotr Sapiezynski once told me how he once did something similar (see here and here). Thinking his visualizations look absolutely stunning i tried to do my own version.

The procedure is actually quite simple:

1. Find an image, png or jpeg
2. Sample pixels from the image according to some probability (use these as nodes)
3. Connect nodes according to a heuristic (e.g. k-nearest neighbors)

Using these 3 simple steps you can create stunning visuals like the t-rex and other examples below. (Also check out the visuals i created for the Complex Science for the Most Vulnerable session at CCS2018).

As usual you can find the code further down.

### Code

Lets first import some packages and define some functions. Note, I have been trying to switch to python 3.6, but below code is in python 2.7.

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import cKDTree
import random

# function to transform color image to grayscale
def rgb2gray(rgb):
return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])

def rgb2hex(color):
'''
Matplotlib scatter is not happy with rgb tuples so we need to transform them to hex
'''
c = tuple([np.int(255 if c == 1.0 else c * 256.0) for c in color])
return "#%02x%02x%02x" % c


In order to sample pixels from an image we first need to set some parameters. Its possible to write an algorithm that estimates the best parameters for each figure, but as i was too lazy, so you will need to experiment a bit instead.

# parameters
p = 0.03 # propability of selecting a pixel/node
k = 5 # number of connections pre per pixel/node
pix_threshold = 0.9  # remove values above this value 0 (white) - 255 (black) OR 0 (black) - 1 (white)


Now, find an image and load it using python. It can be an image of anything, even a png/jpeg of some text. In this example we will focus on the outline of a robot (see below).

# load image
y,x = np.where(rgb2gray(data[:,:,:3])<pix_threshold)
y_norm, x_norm = map(float,data[:,:,0].shape)
colors = data[:,:,:3]

# if its a large image it might be a good idea to downsample
# y,x = np.where(rgb2gray(data[::3,::3,:3])<pix_threshold)
# y_norm, x_norm = map(float,data[::3,::3,0].shape)
# colors = data[::3,::3,:3]


Given the image, sample pixels according to some probability. Here we sample pixels at random, you can also apply a different heuristic. After selecting the nodes, lets connect them. Again you can do this using different methodologies, here i connect each node to its k-nearest neighbors, using a KDTree.

# select nodes
X = np.array(random.sample(zip(x,y),int(len(y)*p)))*1.0

# find k nearest neighbors using scipy.spatial.cKDTree
tree = cKDTree(X)


Given the nodes and edges, only thing we are missing is to draw the network. For this i prefer using matplotlib. In below code i only plot edges, nodes are omitted, because i feel it looks prettier. If you, however, want to include nodes just comment-in the lines below.

# construct figure
plt.figure(figsize=(x_norm/120.,y_norm/120.))
ax = plt.subplot(111)

# create lists for position of links
x_ = []
y_ = []

# go through each node and construct links
for pt in X:
# find k nearest neighbors
dist, ind = tree.query(pt, k=k+1) # k' = k+1 because method returns points itself
for kneigh in ind[1:]:
x_.append([pt[0],X[kneigh][0]])
y_.append([pt[1],X[kneigh][1]])

plt.plot(np.array(x_).T,np.array(y_).T,color='#282828',lw=0.8,alpha=0.4,zorder=2)

# unpack nodes
# y,x = zip(*X)

# plot using a single color
# plt.scatter(y,x,marker='o',c='#282828',s=0.5,alpha=1)

# or if you want to draw the network with the original colors of your image
# c = [rgb2hex(colors[int(xx),int(yy),:]) for yy,xx in X] # colors
# plt.scatter(y,x,marker='o',c=c,s=3,alpha=1,zorder=3)

plt.axis('off')
plt.ylim(y_norm,0)
plt.xlim(0,x_norm)

plt.tight_layout()