App-ifying 'recovering data from images'

See the other notebook for the grisly details and dead-ends.

Requirements:

  • numpy
  • scipy
  • scikit-learn
  • pillow

I recommend installing them with conda install.


In [1]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

Make an image

Make some fake data and apply a colourmap.


In [2]:
from scipy import signal

In [3]:
nx, ny = 100, 100
z = np.random.rand(nx, ny)

sizex, sizey = 30, 30
x, y = np.mgrid[-sizex:sizex+1, -sizey:sizey+1]
g = np.exp(-0.333*(x**2/float(sizex)+y**2/float(sizey)))
f = g/g.sum()

z = signal.convolve(z, f, mode='valid')
z = (z - z.min())/(z.max() - z.min())

In [ ]:
cmaps = [('Perceptually Uniform Sequential',
                            ['viridis', 'inferno', 'plasma', 'magma']),
         ('Sequential',     ['Blues', 'BuGn', 'BuPu',
                             'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd',
                             'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu',
                             'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']),
         ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool',
                             'copper', 'gist_heat', 'gray', 'hot',
                             'pink', 'spring', 'summer', 'winter']),
         ('Diverging',      ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr',
                             'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral',
                             'seismic']),
         ('Qualitative',    ['Accent', 'Dark2', 'Paired', 'Pastel1',
                             'Pastel2', 'Set1', 'Set2', 'Set3']),
         ('Miscellaneous',  ['gist_earth', 'terrain', 'ocean', 'gist_stern',
                             'brg', 'CMRmap', 'cubehelix',
                             'gnuplot', 'gnuplot2', 'gist_ncar',
                             'nipy_spectral', 'jet', 'rainbow',
                             'gist_rainbow', 'hsv', 'flag', 'prism'])]

In [42]:
# Note: interpolation introduces new colours.
plt.imshow(z, cmap="CMRmap")
#plt.imshow(z, cmap="spectral", interpolation='none')

plt.axis('off')
plt.savefig('data/cbar/test.png', bbox_inches='tight')
plt.show()


Read an image


In [35]:
from PIL import Image

#im = Image.open('data/cbar/drainage.jpg')
img = Image.open('data/cbar/test.png')
#img = Image.open('data/cbar/deptuck_salt.jpg')

img


Out[35]:

In [36]:
img.size


Out[36]:
(255, 252)

Instead of taking a random sample, let's take all pixels from a smaller version of the image.


In [37]:
def resize_if_necessary(img, max_size=256):
    h, w = img.size
    if h * w > max_size**2:
        img = img.resize((max_size, max_size))
    return img

In [38]:
img_sm = resize_if_necessary(img, 100)

Cast as an array, and ignore the last channel (alpha), if there is one.

Now we can plot all the pixels in the image into (R, G, B) space. As an added bonus, we can also colour the points with that (R, G, B) colour.


In [39]:
i = np.asarray(img_sm)[:,:,:3]
# Re-organize the pixels to use rgb triples for colour
h, w, d = i.shape
c = i.reshape((h*w, d)) / 255

In [40]:
from mpl_toolkits.mplot3d import Axes3D

# Set up the figure
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')

# Plot the pixels!
ax.scatter(*c.T, c=c, lw=0)
ax.set_title('Raw image')

plt.show()


Quantize with scikit


In [19]:
from sklearn.cluster import KMeans
from sklearn.utils import shuffle

In [20]:
im = np.asarray(img) / 255
h, w, d = im.shape
im_ = im.reshape((w * h, d))[:, :3]
sample = shuffle(im_, random_state=0)[:1000]  # Defines training set size

In [21]:
im.dtype, im.shape, im_.shape


Out[21]:
(dtype('float64'), (252, 255, 4), (64260, 3))

In [22]:
kmeans = KMeans(n_clusters=256, random_state=0).fit(sample)

Now I can make and regularize an RGB palette p:


In [23]:
p = kmeans.cluster_centers_[:, :3]

# Make certain we're in [0, 1]
p[p<1e-9] = 0
p[p>1] = 1

In [24]:
labels = kmeans.predict(im_)

In [25]:
def recreate_image(palette, labels, h, w):
    image = np.zeros((h, w, palette.shape[1]))
    label_idx = 0
    for i in range(h):
        for j in range(w):
            image[i][j] = palette[labels[label_idx]]
            label_idx += 1
    return image

In [26]:
q = recreate_image(p, labels, h, w)

plt.imshow(q)
plt.show()


Reconstructing the colourmap locus

Remember that these points are essentially in random order, and that there are many, many duplicates. Most of those dots are actually hundreds of dots.

We would like to measure ditances between dots. This is just a norm, but there's a convenient function in scipy.spatial for finding distances in n-space.


In [27]:
from scipy.spatial.distance import pdist, squareform, cdist
dists = squareform(pdist(p))

In [28]:
dists.shape, dists.size


Out[28]:
((256, 256), 65536)

In [29]:
dists


Out[29]:
array([[ 0.        ,  1.27125376,  0.98833644, ...,  1.34325032,
         0.79355342,  1.00151366],
       [ 1.27125376,  0.        ,  0.90583142, ...,  0.07231015,
         0.89746535,  0.91640607],
       [ 0.98833644,  0.90583142,  0.        , ...,  0.94842795,
         1.26301909,  0.06871143],
       ..., 
       [ 1.34325032,  0.07231015,  0.94842795, ...,  0.        ,
         0.95870924,  0.95843651],
       [ 0.79355342,  0.89746535,  1.26301909, ...,  0.95870924,
         0.        ,  1.27820279],
       [ 1.00151366,  0.91640607,  0.06871143, ...,  0.95843651,
         1.27820279,  0.        ]])

Each row is the set of distances for a point. Below is an excerpt of only the first 100 points.


In [30]:
# The image is 10k x 10k pixels, so let's just look at a bit of it...
dist_img = Image.fromarray(dists[:100, :100]*255, 'P').resize((500, 500))
dist_img


Out[30]:

I need an algorithm. I think this might work:

Preprocessing

  • Find the median minimum non-zero distance between points, m (nonzero because many points are exactly the same — colourbars are often only 6 or 8 bit).
  • We are interested in points that are close to each other, but outside some minimum distance. Below the minimum distance, it's basically the same point. So ignore everything closer than some small distance n. Let's set n to 0.2 × m (we may need to change this).
  • We want to eliminate points that are further than some maximum separation distance from the other points. Let's set x to 5 × m.

Main loop

  • Find the point nearest the origin. I think the blackest point is generally going to be the closest to 'zero' on the colorbar.
  • Find the nearest point that is at least n away.
  • If the point is more than x away, eliminate everything done so far and continue.
  • If the point is less than x away, keep the current points and continue.

After this, we'll have a minimal set of points, in order from blackest to the other end of the locus, wherever that is (not necessarily far from where we started.


In [31]:
# Start by eliminating duplicate rows — only needed for PIL quantization
def unique_rows(a):
    a = np.ascontiguousarray(a)
    unique_a = np.unique(a.view([('', a.dtype)]*a.shape[1]))
    return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))

u = unique_rows(p)

In [24]:
len(u)


Out[24]:
256

But we can't use this right now because we need to be able to look up the (r, g, b) triples for the pixels we identify. So we need their original coordinates.

Compute the median non-zero distance...


In [25]:
#dists = squareform(pdist(p))

# This is the only way I could think of to eliminate the zeros and keep the shape.
dists[dists==0] = np.nan
mins = np.nanmin(dists, axis=0)

# Now get the median of these minimum nonzero distances.
m = np.median(mins)

# Now compute those numbers I was talking about.
n = m / 2
x = m * 5

In [26]:
m, n, x


Out[26]:
(0.019685123103342222, 0.0098425615516711112, 0.098425615516711112)

Let's compute the distance of every point from the black point, [0, 0, 0].


In [27]:
target = cdist([[0,0,0]], u)
dists = squareform(pdist(u))

In [28]:
hits = [np.argmin(target)]  # Start with the first point.
i = 0

# Discretionary multipler. Higher numbers results in fewer points.
# Essentially, this is like selecting fewer colours to start with.
# 
z = 3

while i < len(u):
    i += 1
    
    # Eliminate everything close to target.
    dists[:, np.squeeze(np.where(target < z*n))] = 10
    target[target < z*n] = 10  # Eliminate everything close to target.

    # Get the next point in the locus.
    nearest = np.argmin(target)
    if nearest not in hits: hits.append(nearest)

    target = dists[nearest, :]

In [29]:
len(hits)


Out[29]:
139

In [30]:
# Set up the figure
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')

# Plot the pixels!
ax.scatter(*u[hits].T, c=u[hits], lw=0, s=40, alpha=1)
ax.plot(*u[hits].T, 'k', alpha=0.5)
ax.text(*u[hits][0], '0')
ax.text(*u[hits][20], '20')
ax.text(*u[hits][80], '80')
ax.text(*u[hits][100], '100')
ax.text(*u[hits][-1], 'end')
plt.show()


The alogithm is missing something: it's possible to find the closest points, making a locus, but leaving out a nearby point...


In [31]:
u[hits]


Out[31]:
array([[  1.88235294e-01,   0.00000000e+00,   2.15686275e-01],
       [  4.54901961e-01,   0.00000000e+00,   5.13725490e-01],
       [  4.86274510e-01,   0.00000000e+00,   5.52941176e-01],
       [  5.05882353e-01,   0.00000000e+00,   5.76470588e-01],
       [  4.69934641e-01,   0.00000000e+00,   5.92810458e-01],
       [  4.24836601e-01,   0.00000000e+00,   6.05228758e-01],
       [  3.80392157e-01,   0.00000000e+00,   6.13725490e-01],
       [  3.27450980e-01,   0.00000000e+00,   6.19607843e-01],
       [  2.82352941e-01,   0.00000000e+00,   6.31372549e-01],
       [  2.48039216e-01,   0.00000000e+00,   6.32352941e-01],
       [  1.38562092e-01,   0.00000000e+00,   6.50980392e-01],
       [  9.67320261e-02,   0.00000000e+00,   6.58823529e-01],
       [  1.17647059e-01,   0.00000000e+00,   6.90196078e-01],
       [  8.62745098e-02,   0.00000000e+00,   7.21568627e-01],
       [  3.92156863e-02,   0.00000000e+00,   7.60784314e-01],
       [  3.92156863e-03,   0.00000000e+00,   7.62745098e-01],
       [  1.56862745e-02,   0.00000000e+00,   7.29411765e-01],
       [  0.00000000e+00,   0.00000000e+00,   6.94117647e-01],
       [  1.56862745e-02,   0.00000000e+00,   6.58823529e-01],
       [  1.56862745e-02,   3.52941176e-02,   7.88235294e-01],
       [  5.22875817e-03,   1.69934641e-02,   8.10457516e-01],
       [  0.00000000e+00,   5.29411765e-02,   8.43137255e-01],
       [  0.00000000e+00,   8.62745098e-02,   8.43137255e-01],
       [  0.00000000e+00,   1.33333333e-01,   8.62745098e-01],
       [  0.00000000e+00,   1.82352941e-01,   8.60784314e-01],
       [  0.00000000e+00,   2.27450980e-01,   8.58823529e-01],
       [  0.00000000e+00,   2.66666667e-01,   8.43137255e-01],
       [  0.00000000e+00,   3.21568627e-01,   8.62745098e-01],
       [  0.00000000e+00,   3.64705882e-01,   8.64705882e-01],
       [  0.00000000e+00,   4.10457516e-01,   8.61437908e-01],
       [  0.00000000e+00,   4.45098039e-01,   8.60784314e-01],
       [  0.00000000e+00,   4.78431373e-01,   8.50980392e-01],
       [  0.00000000e+00,   5.05882353e-01,   8.66666667e-01],
       [  0.00000000e+00,   5.38235294e-01,   8.60784314e-01],
       [  0.00000000e+00,   5.63398693e-01,   8.35294118e-01],
       [  0.00000000e+00,   6.00000000e-01,   8.40522876e-01],
       [  0.00000000e+00,   5.94117647e-01,   8.10784314e-01],
       [  0.00000000e+00,   6.00000000e-01,   7.78431373e-01],
       [  0.00000000e+00,   6.24313725e-01,   7.60784314e-01],
       [  0.00000000e+00,   6.40196078e-01,   7.26470588e-01],
       [  0.00000000e+00,   6.47058824e-01,   6.79738562e-01],
       [  0.00000000e+00,   6.54901961e-01,   6.42156863e-01],
       [  0.00000000e+00,   6.61064426e-01,   6.01120448e-01],
       [  0.00000000e+00,   6.62745098e-01,   5.56862745e-01],
       [  0.00000000e+00,   6.56862745e-01,   5.15686275e-01],
       [  0.00000000e+00,   6.52941176e-01,   4.41176471e-01],
       [  0.00000000e+00,   6.47058824e-01,   3.90849673e-01],
       [  0.00000000e+00,   6.39215686e-01,   3.56862745e-01],
       [  0.00000000e+00,   6.37908497e-01,   3.20261438e-01],
       [  0.00000000e+00,   6.25490196e-01,   2.17647059e-01],
       [  0.00000000e+00,   6.20915033e-01,   1.59477124e-01],
       [  0.00000000e+00,   6.15686275e-01,   1.09803922e-01],
       [  0.00000000e+00,   6.11764706e-01,   5.49019608e-02],
       [  0.00000000e+00,   6.15686275e-01,   2.35294118e-02],
       [  0.00000000e+00,   6.43137255e-01,   5.09803922e-02],
       [  0.00000000e+00,   6.70588235e-01,   2.74509804e-02],
       [  0.00000000e+00,   6.47058824e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.99346405e-01,   3.26797386e-03],
       [  0.00000000e+00,   7.35947712e-01,   0.00000000e+00],
       [  0.00000000e+00,   7.67973856e-01,   1.30718954e-03],
       [  0.00000000e+00,   8.06372549e-01,   4.90196078e-04],
       [  6.53594771e-04,   8.45751634e-01,   0.00000000e+00],
       [  0.00000000e+00,   8.91176471e-01,   0.00000000e+00],
       [  1.12044818e-03,   9.23809524e-01,   0.00000000e+00],
       [  2.74509804e-02,   9.37254902e-01,   0.00000000e+00],
       [  1.68067227e-03,   9.56302521e-01,   0.00000000e+00],
       [  3.37254902e-02,   9.71764706e-01,   0.00000000e+00],
       [  7.00280112e-02,   9.85434174e-01,   0.00000000e+00],
       [  1.04901961e-01,   9.92156863e-01,   0.00000000e+00],
       [  1.13725490e-01,   9.60784314e-01,   0.00000000e+00],
       [  1.52941176e-01,   9.64705882e-01,   0.00000000e+00],
       [  1.41176471e-01,   9.96078431e-01,   0.00000000e+00],
       [  1.86274510e-01,   9.92156863e-01,   0.00000000e+00],
       [  2.23529412e-01,   9.96078431e-01,   0.00000000e+00],
       [  2.58823529e-01,   9.98692810e-01,   0.00000000e+00],
       [  2.98039216e-01,   1.00000000e+00,   0.00000000e+00],
       [  3.33333333e-01,   9.94117647e-01,   0.00000000e+00],
       [  3.72549020e-01,   1.00000000e+00,   0.00000000e+00],
       [  4.03921569e-01,   9.96078431e-01,   0.00000000e+00],
       [  4.82352941e-01,   9.96078431e-01,   0.00000000e+00],
       [  5.14705882e-01,   9.97058824e-01,   0.00000000e+00],
       [  5.47058824e-01,   9.94771242e-01,   0.00000000e+00],
       [  5.88235294e-01,   9.90196078e-01,   0.00000000e+00],
       [  6.47712418e-01,   9.90196078e-01,   0.00000000e+00],
       [  6.92810458e-01,   9.90849673e-01,   0.00000000e+00],
       [  7.25490196e-01,   9.84967320e-01,   0.00000000e+00],
       [  7.62745098e-01,   9.76470588e-01,   0.00000000e+00],
       [  8.03137255e-01,   9.68627451e-01,   0.00000000e+00],
       [  8.41830065e-01,   9.58169935e-01,   0.00000000e+00],
       [  8.79302832e-01,   9.42047930e-01,   0.00000000e+00],
       [  9.15032680e-01,   9.22875817e-01,   0.00000000e+00],
       [  9.46078431e-01,   8.94607843e-01,   0.00000000e+00],
       [  9.57843137e-01,   8.65686275e-01,   0.00000000e+00],
       [  9.80392157e-01,   8.23529412e-01,   0.00000000e+00],
       [  9.90196078e-01,   7.90196078e-01,   0.00000000e+00],
       [  9.80392157e-01,   7.52941176e-01,   0.00000000e+00],
       [  9.94117647e-01,   7.24509804e-01,   0.00000000e+00],
       [  1.00000000e+00,   6.88888889e-01,   0.00000000e+00],
       [  9.99019608e-01,   6.54901961e-01,   0.00000000e+00],
       [  1.00000000e+00,   6.10644258e-01,   0.00000000e+00],
       [  9.98879552e-01,   5.77591036e-01,   0.00000000e+00],
       [  9.98692810e-01,   5.17647059e-01,   0.00000000e+00],
       [  1.00000000e+00,   4.66666667e-01,   0.00000000e+00],
       [  9.97385621e-01,   4.22222222e-01,   0.00000000e+00],
       [  9.98692810e-01,   3.84313725e-01,   0.00000000e+00],
       [  9.96078431e-01,   3.49019608e-01,   0.00000000e+00],
       [  1.00000000e+00,   3.09803922e-01,   0.00000000e+00],
       [  1.00000000e+00,   2.33333333e-01,   0.00000000e+00],
       [  1.00000000e+00,   1.92156863e-01,   0.00000000e+00],
       [  9.98039216e-01,   1.52941176e-01,   0.00000000e+00],
       [  9.60784314e-01,   9.80392157e-02,   0.00000000e+00],
       [  9.78431373e-01,   7.05882353e-02,   0.00000000e+00],
       [  9.52941176e-01,   5.49019608e-02,   0.00000000e+00],
       [  9.21568627e-01,   2.35294118e-02,   0.00000000e+00],
       [  9.52941176e-01,   1.96078431e-03,   0.00000000e+00],
       [  9.96078431e-01,   1.56862745e-02,   0.00000000e+00],
       [  8.73389356e-01,   0.00000000e+00,   0.00000000e+00],
       [  8.37254902e-01,   1.37254902e-02,   1.37254902e-02],
       [  8.12549020e-01,   0.00000000e+00,   0.00000000e+00],
       [  8.03921569e-01,   3.39869281e-02,   3.39869281e-02],
       [  8.03921569e-01,   6.66666667e-02,   6.66666667e-02],
       [  8.00000000e-01,   9.80392157e-02,   9.80392157e-02],
       [  8.00000000e-01,   2.35294118e-01,   2.35294118e-01],
       [  8.00000000e-01,   2.90196078e-01,   2.90196078e-01],
       [  8.00000000e-01,   4.11764706e-01,   4.11764706e-01],
       [  8.00000000e-01,   4.39215686e-01,   4.39215686e-01],
       [  8.00000000e-01,   5.01960784e-01,   5.01960784e-01],
       [  8.00000000e-01,   5.49019608e-01,   5.49019608e-01],
       [  8.00000000e-01,   6.62745098e-01,   6.62745098e-01],
       [  8.00000000e-01,   7.09803922e-01,   7.09803922e-01],
       [  1.00000000e+00,   1.00000000e+00,   1.00000000e+00],
       [  2.60784314e-01,   9.58823529e-01,   0.00000000e+00],
       [  2.11764706e-01,   9.56862745e-01,   0.00000000e+00],
       [  1.17647059e-01,   9.17647059e-01,   0.00000000e+00],
       [  4.70588235e-02,   9.01960784e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.82352941e-01,   8.23529412e-02],
       [  0.00000000e+00,   6.66666667e-01,   1.31372549e-01],
       [  0.00000000e+00,   1.29411765e-01,   8.03921569e-01],
       [  0.00000000e+00,   1.01960784e-01,   7.88235294e-01]])

Awesome!


Get all pixels using nearest neighbour

cKDTree, hopefully fast...


In [32]:
from scipy.spatial import cKDTree

In [33]:
kdtree = cKDTree(u[hits])

We get distances and indices simultaneously:


In [34]:
#im = im.reshape(h*w, d)
dx, ix = kdtree.query(im_)

In [35]:
dx.shape, ix.shape


Out[35]:
((64262,), (64262,))

In [36]:
plt.imshow(ix.reshape((h, w)), cmap='viridis')
plt.colorbar()
plt.show()



In [37]:
plt.imshow(dx.reshape((h, w)))
plt.colorbar()
plt.show()


We can apply a cutoff where the distance was unacceptably far.


In [38]:
ix = ix.astype(float)
ix[np.where(dx>0.2)] = np.nan

In [39]:
plt.imshow(ix.reshape((h, w)), cmap='viridis')
plt.colorbar()
plt.show()



In [40]:
fig = plt.figure(figsize=(18, 5))

ax0 = fig.add_subplot(131)
plt.imshow(im, interpolation='none')

ax1 = fig.add_subplot(132, projection='3d')
ax1.scatter(*u[hits].T, c=u[hits], lw=0, s=40, alpha=1)
ax1.plot(*u[hits].T, 'k', alpha=0.5)
ax1.text(*u[hits][0], 'start')
ax1.text(*u[hits][-1], 'end')

ax2 = fig.add_subplot(133)
plt.imshow(ix.reshape((h, w)), cmap="spectral", interpolation='none')
plt.colorbar(shrink=0.75)

plt.show()


TSP

Two main algos:

I think I've installed Concorde, LKH and OR Tools.

LKH and Concorde can be used via this Python package: https://github.com/perrygeo/pytsp (but note that it used to be called pyconcorde so you need to change the names of some functions — look at the source. I can't get the concorde implementation to work... see error below.

Compile the libs and add to PATH as mentioned in the docs for pytsp.


In [41]:
from pytsp import atsp_tsp, run, dumps_matrix

Add black and white to p:


In [142]:
p = np.vstack([[[0,0,0]], p])

In [164]:
p


Out[164]:
array([[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   6.20915033e-01,   1.59477124e-01],
       [  9.96862745e-01,   7.12156863e-01,   0.00000000e+00],
       [  1.00000000e+00,   1.00000000e+00,   1.00000000e+00],
       [  0.00000000e+00,   2.27450980e-01,   8.58823529e-01],
       [  9.27058824e-01,   3.92156863e-03,   0.00000000e+00],
       [  7.00280112e-02,   9.85434174e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.09803922e-01,   8.17647059e-01],
       [  2.48039216e-01,   0.00000000e+00,   6.32352941e-01],
       [  7.43529412e-01,   9.77254902e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.64313725e-01,   6.21176471e-01],
       [  9.29411765e-01,   9.16078431e-01,   0.00000000e+00],
       [  9.98692810e-01,   3.84313725e-01,   0.00000000e+00],
       [  0.00000000e+00,   7.35947712e-01,   0.00000000e+00],
       [  5.05882353e-01,   0.00000000e+00,   5.76470588e-01],
       [  8.00000000e-01,   5.49019608e-01,   5.49019608e-01],
       [  5.47058824e-01,   9.94771242e-01,   0.00000000e+00],
       [  1.76470588e-02,   0.00000000e+00,   7.07843137e-01],
       [  1.00000000e+00,   1.92156863e-01,   0.00000000e+00],
       [  1.04575163e-03,   9.09281046e-01,   0.00000000e+00],
       [  2.37908497e-01,   9.88235294e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.47058824e-01,   3.90849673e-01],
       [  8.73389356e-01,   0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   5.05882353e-01,   8.66666667e-01],
       [  8.00000000e-01,   2.35294118e-01,   2.35294118e-01],
       [  0.00000000e+00,   6.47058824e-02,   8.25490196e-01],
       [  0.00000000e+00,   6.48179272e-01,   6.98599440e-01],
       [  1.00000000e+00,   6.10644258e-01,   0.00000000e+00],
       [  1.88235294e-01,   0.00000000e+00,   2.15686275e-01],
       [  3.33333333e-01,   9.94117647e-01,   0.00000000e+00],
       [  8.60130719e-01,   9.45751634e-01,   0.00000000e+00],
       [  9.84313725e-01,   8.03137255e-01,   0.00000000e+00],
       [  1.00000000e+00,   4.93333333e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.70588235e-01,   2.74509804e-02],
       [  1.20261438e-01,   0.00000000e+00,   6.52287582e-01],
       [  6.47712418e-01,   9.90196078e-01,   0.00000000e+00],
       [  8.03921569e-01,   8.23529412e-02,   8.23529412e-02],
       [  0.00000000e+00,   4.10457516e-01,   8.61437908e-01],
       [  0.00000000e+00,   6.58823529e-01,   4.86274510e-01],
       [  1.00000000e+00,   3.09803922e-01,   0.00000000e+00],
       [  4.07843137e-01,   0.00000000e+00,   6.11764706e-01],
       [  1.56862745e-03,   9.70980392e-01,   0.00000000e+00],
       [  6.53594771e-04,   8.45751634e-01,   0.00000000e+00],
       [  8.00000000e-01,   7.09803922e-01,   7.09803922e-01],
       [  1.41176471e-01,   9.96078431e-01,   0.00000000e+00],
       [  4.82352941e-01,   9.96078431e-01,   0.00000000e+00],
       [  8.00000000e-01,   4.11764706e-01,   4.11764706e-01],
       [  8.03921569e-01,   3.39869281e-02,   3.39869281e-02],
       [  8.03137255e-01,   9.68627451e-01,   0.00000000e+00],
       [  3.10784314e-01,   0.00000000e+00,   6.27450980e-01],
       [  9.78431373e-01,   7.05882353e-02,   0.00000000e+00],
       [  0.00000000e+00,   6.37908497e-01,   3.20261438e-01],
       [  0.00000000e+00,   5.53921569e-01,   8.63725490e-01],
       [  0.00000000e+00,   6.24313725e-01,   7.60784314e-01],
       [  3.96078431e-01,   9.72549020e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.15686275e-01,   2.35294118e-02],
       [  4.69934641e-01,   0.00000000e+00,   5.92810458e-01],
       [  0.00000000e+00,   9.80392157e-03,   7.88235294e-01],
       [  0.00000000e+00,   8.06372549e-01,   4.90196078e-04],
       [  9.51372549e-01,   8.80000000e-01,   0.00000000e+00],
       [  0.00000000e+00,   9.44771242e-01,   0.00000000e+00],
       [  8.32026144e-01,   0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   1.33333333e-01,   8.62745098e-01],
       [  9.97385621e-01,   4.22222222e-01,   0.00000000e+00],
       [  9.80392157e-01,   7.52941176e-01,   0.00000000e+00],
       [  0.00000000e+00,   7.67973856e-01,   1.30718954e-03],
       [  8.81045752e-01,   9.24183007e-01,   0.00000000e+00],
       [  0.00000000e+00,   3.37254902e-01,   8.66666667e-01],
       [  9.99019608e-01,   5.50000000e-01,   0.00000000e+00],
       [  1.13725490e-01,   9.60784314e-01,   0.00000000e+00],
       [  1.56862745e-02,   0.00000000e+00,   6.58823529e-01],
       [  0.00000000e+00,   6.56862745e-01,   5.15686275e-01],
       [  0.00000000e+00,   2.66666667e-01,   8.43137255e-01],
       [  0.00000000e+00,   6.62745098e-01,   6.86274510e-02],
       [  0.00000000e+00,   6.99346405e-01,   3.26797386e-03],
       [  2.98039216e-01,   1.00000000e+00,   0.00000000e+00],
       [  9.92156863e-01,   1.25490196e-01,   0.00000000e+00],
       [  8.00000000e-01,   2.90196078e-01,   2.90196078e-01],
       [  4.54901961e-01,   0.00000000e+00,   5.13725490e-01],
       [  3.37254902e-02,   9.71764706e-01,   0.00000000e+00],
       [  0.00000000e+00,   1.29411765e-01,   8.03921569e-01],
       [  9.67320261e-02,   0.00000000e+00,   6.58823529e-01],
       [  0.00000000e+00,   4.45098039e-01,   8.60784314e-01],
       [  9.68627451e-01,   3.13725490e-02,   0.00000000e+00],
       [  9.99019608e-01,   6.54901961e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.15686275e-01,   1.09803922e-01],
       [  2.00000000e-01,   9.72549020e-01,   0.00000000e+00],
       [  8.00000000e-01,   5.01960784e-01,   5.01960784e-01],
       [  9.80392157e-04,   1.96078431e-03,   7.47058824e-01],
       [  0.00000000e+00,   6.25490196e-01,   2.17647059e-01],
       [  4.98039216e-01,   9.93464052e-01,   0.00000000e+00],
       [  6.15686275e-01,   9.95098039e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.54901961e-01,   6.42156863e-01],
       [  0.00000000e+00,   6.66666667e-01,   1.31372549e-01],
       [  9.96078431e-01,   3.49019608e-01,   0.00000000e+00],
       [  9.98692810e-01,   5.17647059e-01,   0.00000000e+00],
       [  9.98692810e-01,   4.53594771e-01,   0.00000000e+00],
       [  0.00000000e+00,   1.80392157e-01,   8.43137255e-01],
       [  8.62745098e-02,   0.00000000e+00,   7.21568627e-01],
       [  9.98879552e-01,   5.77591036e-01,   0.00000000e+00],
       [  7.25490196e-01,   9.84967320e-01,   0.00000000e+00],
       [  8.00000000e-01,   6.62745098e-01,   6.62745098e-01],
       [  4.26143791e-01,   9.93464052e-01,   0.00000000e+00],
       [  1.04901961e-01,   9.92156863e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.52941176e-01,   4.41176471e-01],
       [  2.60784314e-01,   9.58823529e-01,   0.00000000e+00],
       [  0.00000000e+00,   5.29411765e-02,   8.43137255e-01],
       [  9.70588235e-01,   8.41176471e-01,   0.00000000e+00],
       [  3.60784314e-01,   0.00000000e+00,   6.19607843e-01],
       [  0.00000000e+00,   6.47058824e-01,   0.00000000e+00],
       [  9.31372549e-01,   9.02941176e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.62745098e-01,   5.56862745e-01],
       [  9.74509804e-01,   1.60784314e-01,   0.00000000e+00],
       [  5.22875817e-03,   1.69934641e-02,   8.10457516e-01],
       [  9.98039216e-01,   2.05882353e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.00000000e-01,   8.40522876e-01],
       [  4.48366013e-01,   0.00000000e+00,   6.07843137e-01],
       [  6.92810458e-01,   9.90849673e-01,   0.00000000e+00],
       [  0.00000000e+00,   5.63398693e-01,   8.35294118e-01],
       [  0.00000000e+00,   6.41176471e-01,   2.94117647e-01],
       [  9.97058824e-01,   7.35294118e-01,   0.00000000e+00],
       [  0.00000000e+00,   8.91176471e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.39215686e-01,   3.56862745e-01],
       [  3.72549020e-01,   1.00000000e+00,   0.00000000e+00],
       [  4.05228758e-02,   0.00000000e+00,   6.70588235e-01],
       [  5.88235294e-01,   9.90196078e-01,   0.00000000e+00],
       [  9.02521008e-01,   1.68067227e-03,   0.00000000e+00],
       [  0.00000000e+00,   4.66666667e-01,   8.64052288e-01],
       [  0.00000000e+00,   1.03267974e-01,   8.64052288e-01],
       [  0.00000000e+00,   6.22549020e-01,   7.83333333e-01],
       [  1.00000000e+00,   6.31372549e-01,   0.00000000e+00],
       [  8.12549020e-01,   0.00000000e+00,   0.00000000e+00],
       [  9.66666667e-01,   5.88235294e-03,   0.00000000e+00],
       [  0.00000000e+00,   6.64052288e-01,   5.88235294e-01],
       [  0.00000000e+00,   6.40196078e-01,   7.26470588e-01],
       [  0.00000000e+00,   6.75163399e-01,   6.53594771e-04],
       [  9.15032680e-01,   9.22875817e-01,   0.00000000e+00],
       [  0.00000000e+00,   9.33333333e-01,   0.00000000e+00],
       [  4.11764706e-02,   9.90196078e-01,   0.00000000e+00],
       [  8.00000000e-01,   4.39215686e-01,   4.39215686e-01],
       [  0.00000000e+00,   6.14379085e-01,   2.61437908e-03],
       [  9.79084967e-01,   4.96732026e-02,   0.00000000e+00],
       [  0.00000000e+00,   6.47058824e-01,   6.79738562e-01],
       [  4.86274510e-01,   0.00000000e+00,   5.52941176e-01],
       [  4.70588235e-02,   9.01960784e-01,   0.00000000e+00],
       [  1.17647059e-01,   9.17647059e-01,   0.00000000e+00],
       [  2.58823529e-01,   9.98692810e-01,   0.00000000e+00],
       [  0.00000000e+00,   8.71241830e-01,   0.00000000e+00],
       [  9.80392157e-01,   8.23529412e-01,   0.00000000e+00],
       [  8.96732026e-01,   9.32897603e-01,   0.00000000e+00],
       [  7.82352941e-01,   9.76470588e-01,   0.00000000e+00],
       [  3.92156863e-02,   0.00000000e+00,   7.60784314e-01],
       [  9.96078431e-01,   7.51633987e-01,   0.00000000e+00],
       [  1.17647059e-01,   0.00000000e+00,   6.90196078e-01],
       [  0.00000000e+00,   3.64705882e-01,   8.64705882e-01],
       [  2.07843137e-01,   9.96078431e-01,   0.00000000e+00],
       [  7.84313725e-04,   7.86666667e-01,   0.00000000e+00],
       [  2.82352941e-01,   0.00000000e+00,   6.31372549e-01],
       [  8.41830065e-01,   9.58169935e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.11764706e-01,   5.49019608e-02],
       [  0.00000000e+00,   1.01960784e-01,   7.88235294e-01],
       [  5.68627451e-02,   9.66666667e-01,   0.00000000e+00],
       [  1.00000000e+00,   6.88888889e-01,   0.00000000e+00],
       [  9.98039216e-01,   1.52941176e-01,   0.00000000e+00],
       [  0.00000000e+00,   5.03921569e-01,   8.49019608e-01],
       [  1.56862745e-02,   3.52941176e-02,   7.88235294e-01],
       [  0.00000000e+00,   5.94117647e-01,   8.10784314e-01],
       [  1.52941176e-01,   9.64705882e-01,   0.00000000e+00],
       [  5.14705882e-01,   9.97058824e-01,   0.00000000e+00],
       [  8.79302832e-01,   9.42047930e-01,   0.00000000e+00],
       [  9.46078431e-01,   8.94607843e-01,   0.00000000e+00],
       [  9.92156863e-01,   2.94117647e-01,   0.00000000e+00],
       [  8.50980392e-01,   0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   8.62745098e-02,   8.43137255e-01],
       [  0.00000000e+00,   6.00000000e-01,   7.78431373e-01],
       [  2.74509804e-02,   9.37254902e-01,   0.00000000e+00],
       [  9.52941176e-01,   5.49019608e-02,   0.00000000e+00],
       [  0.00000000e+00,   5.38235294e-01,   8.60784314e-01],
       [  4.03921569e-01,   9.96078431e-01,   0.00000000e+00],
       [  0.00000000e+00,   7.14509804e-01,   3.92156863e-03],
       [  9.96078431e-01,   1.56862745e-02,   0.00000000e+00],
       [  1.00000000e+00,   2.33333333e-01,   0.00000000e+00],
       [  8.23529412e-02,   0.00000000e+00,   6.84313725e-01],
       [  0.00000000e+00,   6.48366013e-01,   4.16993464e-01],
       [  0.00000000e+00,   6.82352941e-01,   8.23529412e-02],
       [  1.68067227e-03,   9.56302521e-01,   0.00000000e+00],
       [  9.96078431e-01,   1.76470588e-01,   0.00000000e+00],
       [  0.00000000e+00,   3.13725490e-02,   8.29411765e-01],
       [  2.61437908e-02,   0.00000000e+00,   6.84967320e-01],
       [  0.00000000e+00,   2.54901961e-01,   8.66666667e-01],
       [  8.37254902e-01,   1.37254902e-02,   1.37254902e-02],
       [  0.00000000e+00,   2.74509804e-02,   7.52941176e-01],
       [  1.29411765e-01,   9.80392157e-01,   0.00000000e+00],
       [  3.80392157e-01,   0.00000000e+00,   6.13725490e-01],
       [  0.00000000e+00,   6.61064426e-01,   6.01120448e-01],
       [  0.00000000e+00,   5.67320261e-01,   8.56209150e-01],
       [  0.00000000e+00,   0.00000000e+00,   6.94117647e-01],
       [  4.24836601e-01,   0.00000000e+00,   6.05228758e-01],
       [  0.00000000e+00,   6.55686275e-01,   6.69803922e-01],
       [  8.00000000e-01,   5.68627451e-01,   5.68627451e-01],
       [  0.00000000e+00,   6.41176471e-01,   3.42156863e-01],
       [  0.00000000e+00,   6.43137255e-01,   5.09803922e-02],
       [  4.99346405e-01,   0.00000000e+00,   5.66013072e-01],
       [  7.05882353e-01,   9.72549020e-01,   0.00000000e+00],
       [  8.03921569e-01,   6.66666667e-02,   6.66666667e-02],
       [  4.70588235e-01,   0.00000000e+00,   5.33333333e-01],
       [  8.00000000e-01,   9.80392157e-02,   9.80392157e-02],
       [  9.21568627e-01,   2.35294118e-02,   0.00000000e+00],
       [  0.00000000e+00,   1.82352941e-01,   8.60784314e-01],
       [  9.29411765e-01,   8.74509804e-01,   0.00000000e+00],
       [  5.14509804e-01,   0.00000000e+00,   5.85098039e-01],
       [  9.01960784e-02,   9.72549020e-01,   0.00000000e+00],
       [  0.00000000e+00,   2.86274510e-01,   8.43137255e-01],
       [  1.86274510e-01,   9.92156863e-01,   0.00000000e+00],
       [  9.09803922e-01,   9.10784314e-01,   0.00000000e+00],
       [  0.00000000e+00,   5.88235294e-01,   8.33333333e-01],
       [  9.60784314e-01,   9.80392157e-02,   0.00000000e+00],
       [  0.00000000e+00,   5.49019608e-02,   8.66666667e-01],
       [  9.98692810e-01,   6.01307190e-01,   0.00000000e+00],
       [  1.12044818e-03,   9.23809524e-01,   0.00000000e+00],
       [  1.38562092e-01,   0.00000000e+00,   6.50980392e-01],
       [  5.21568627e-01,   9.76470588e-01,   0.00000000e+00],
       [  0.00000000e+00,   4.27450980e-01,   8.62745098e-01],
       [  5.74509804e-01,   9.90196078e-01,   0.00000000e+00],
       [  2.84967320e-01,   9.94771242e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.31372549e-01,   1.96078431e-03],
       [  0.00000000e+00,   2.23529412e-01,   8.39215686e-01],
       [  3.92156863e-03,   0.00000000e+00,   7.62745098e-01],
       [  8.05882353e-01,   4.50980392e-02,   4.50980392e-02],
       [  6.57843137e-01,   9.93137255e-01,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,   8.11764706e-01],
       [  9.57843137e-01,   8.65686275e-01,   0.00000000e+00],
       [  0.00000000e+00,   6.44117647e-01,   7.09803922e-01],
       [  3.27450980e-01,   0.00000000e+00,   6.19607843e-01],
       [  9.90196078e-01,   7.90196078e-01,   0.00000000e+00],
       [  0.00000000e+00,   5.17647059e-01,   8.43137255e-01],
       [  0.00000000e+00,   6.15686275e-01,   8.00000000e-01],
       [  1.00000000e+00,   4.66666667e-01,   0.00000000e+00],
       [  2.23529412e-01,   9.96078431e-01,   0.00000000e+00],
       [  1.09803922e-01,   9.41176471e-01,   0.00000000e+00],
       [  2.11764706e-01,   9.56862745e-01,   0.00000000e+00],
       [  1.00000000e+00,   4.43137255e-01,   0.00000000e+00],
       [  1.43790850e-02,   9.75163399e-01,   0.00000000e+00],
       [  0.00000000e+00,   8.31372549e-01,   0.00000000e+00],
       [  7.62745098e-01,   9.76470588e-01,   0.00000000e+00],
       [  0.00000000e+00,   7.50980392e-01,   0.00000000e+00],
       [  9.98039216e-01,   4.00000000e-01,   0.00000000e+00],
       [  9.94117647e-01,   7.24509804e-01,   0.00000000e+00],
       [  3.08496732e-01,   9.96078431e-01,   0.00000000e+00],
       [  9.98039216e-01,   2.86274510e-01,   0.00000000e+00],
       [  1.56862745e-02,   0.00000000e+00,   7.29411765e-01],
       [  0.00000000e+00,   3.21568627e-01,   8.62745098e-01],
       [  0.00000000e+00,   4.78431373e-01,   8.50980392e-01],
       [  9.96078431e-01,   3.25490196e-01,   0.00000000e+00],
       [  9.89542484e-01,   7.63398693e-01,   0.00000000e+00],
       [  0.00000000e+00,   4.88888889e-01,   8.66666667e-01],
       [  9.52941176e-01,   1.96078431e-03,   0.00000000e+00]])

In [154]:
# Make dists again and cast as integers
dists = squareform(pdist(p))
# dists = dists * 10000
# d = dists.astype(int)

# If distances are asymmetric
# matrix_sym = atsp_tsp(d, strategy="avg")

# Mine aren't
matrix_sym = d

In [155]:
outf = "/tmp/myroute_concorde.tsp"
with open(outf, 'w') as dest:
    dest.write(dumps_matrix(matrix_sym, name="My Route"))

In [156]:
tour_concorde = run(outf, start=0, solver="Concorde")

In [157]:
outf = "/tmp/myroute_lkh.tsp"
with open(outf, 'w') as dest:
    dest.write(dumps_matrix(matrix_sym, name="My Route"))

In [158]:
tour_lkh = run(outf, start=0, solver="LKH")

In [159]:
result = np.array(tour_concorde['tour'])
#result = np.array(tour_lkh['tour'])

In [166]:
result


Out[166]:
array([  0,  28,  78, 205, 143, 202,  14, 210,  56, 116, 197,  40, 193,
       108, 233,  49, 157,   8, 220,  34,  81, 153,  98, 182, 124,  70,
       188, 196,  17, 250, 151, 227,  88, 191, 165,  57, 230, 113, 187,
       217, 106,  25, 160,  80, 173, 128,  62, 208,  97, 226,   4, 189,
        72, 212, 251,  67, 154,  37, 222,  82, 127, 252, 255,  23, 164,
       235, 177,  52, 195, 118, 215, 115, 166,   7, 236, 129, 174,  53,
       134, 232,  26, 142, 198,  92,  10, 194, 133, 111,  71,  38, 104,
       183,  21, 122, 200,  51, 119,  89,   1,  85,  93, 184,  73, 201,
       159,  55, 140, 225, 109,  33, 135,  74, 179,  13, 245,  65, 156,
        58, 243,  42, 147, 121,  19, 219, 137,  60, 185,  41, 242,  79,
       138,   6, 161, 175, 144, 145, 239,  69, 211, 103, 192,  44, 167,
       240,  86, 213, 155, 238,  20, 105, 146, 224,  75, 248,  29, 123,
        54, 178, 102,  45,  90, 168, 221,  16, 223, 125,  91,  35, 229,
       117, 203, 100,   9, 244, 150,  48, 158,  30, 169,  66, 149, 136,
       214,  11, 110, 170, 209,  59, 231, 107, 148,  31, 234, 254,  64,
       152, 120, 247,   2, 162,  84, 130,  27, 218,  99,  68,  95,  32,
       237,  96, 241,  63, 246,  12,  94, 253,  39, 171, 249, 181, 114,
        18, 186, 112, 163,  76, 216,  50, 141, 176,  83, 180, 132, 256,
         5, 207, 126,  22, 172, 190,  61, 131,  47, 228, 204,  36, 206,
        24,  77,  46, 139,  87,  15, 199, 101,  43,   3])

Now result is the indices of points for the shortest path, shape (256,). And p is our quantized colormap, shape (256, 3). So we can select the points easily for an ordered colourmap.

The offset is to account for the fact that we added a black point at the start.


In [169]:
c = p[result[1:]]

Ideally I'd like all the distances too, but it wouldn't be too hard to compute these.

Now let's look at it all.


In [168]:
# Set up the figure
fig = plt.figure(figsize=(18, 8))

# Result of TSP solver
ax = fig.add_subplot(121, projection='3d')
ax.scatter(*c.T, c=c, lw=0, s=40, alpha=1)
ax.plot(*c.T, 'k', alpha=0.5)
ax.set_title('TSP solver')

ax1 = fig.add_subplot(122, projection='3d')
ax1.scatter(*u[hits].T, c=u[hits], lw=0, s=40, alpha=1)
ax1.plot(*u[hits].T, 'k', alpha=0.5)
ax1.set_title('Naive solution')

plt.show()



In [170]:
kdtree = cKDTree(c)

In [171]:
dx, ix = kdtree.query(im_)

In [172]:
plt.imshow(ix.reshape((h, w)), cmap='viridis')
plt.colorbar()
plt.show()



In [ ]:

Naive TSP


In [44]:
import doctest
from itertools import permutations


def distance(point1, point2):
    """
    Returns the Euclidean distance of two points in the Cartesian Plane.

    >>> distance([[3,4]],[[0,0]])
    5.0
    >>> distance([[3,6]],[[10,6]])
    7.0
    """
    return cdist(point1, point2)


def total_distance(points):
    """
    Returns the length of the path passing throught
    all the points in the given order.

    >>> total_distance([[[1,2]],[[4,6]]])
    5.0
    >>> total_distance([[[3,6]],[[7,6]],[[12,6]]])
    9.0
    """
    return sum([distance(point, points[index + 1]) for index, point in enumerate(points[:-1])])


def travelling_salesman(points, start=None):
    """
    Finds the shortest route to visit all the cities by bruteforce.
    Time complexity is O(N!), so never use on long lists.

    >>> travelling_salesman([[0,0],[10,0],[6,0]])
    ([0, 0], [6, 0], [10, 0])
    >>> travelling_salesman([[0,0],[6,0],[2,3],[3,7],[0.5,9],[3,5],[9,1]])
    ([0, 0], [6, 0], [9, 1], [2, 3], [3, 5], [3, 7], [0.5, 9])
    """
    if start is None:
        start = points[0]
    return min([perm for perm in permutations(points) if perm[0] == start], key=total_distance)


def optimized_travelling_salesman(points, start=None):
    """
    As solving the problem in the brute force way is too slow,
    this function implements a simple heuristic: always
    go to the nearest city.

    Even if this algoritmh is extremely simple, it works pretty well
    giving a solution only about 25% longer than the optimal one (cit. Wikipedia),
    and runs very fast in O(N^2) time complexity.

    >>> optimized_travelling_salesman([[i,j] for i in range(5) for j in range(5)])
    [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 4], [1, 3], [1, 2], [1, 1], [1, 0], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [3, 4], [3, 3], [3, 2], [3, 1], [3, 0], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4]]
    >>> optimized_travelling_salesman([[0,0],[10,0],[6,0]])
    [[0, 0], [6, 0], [10, 0]]
    """
    if start is None:
        start = points[0]
    must_visit = points
    path = [start]
    must_visit.remove(start)
    while must_visit:
        nearest = min(must_visit, key=lambda x: distance(path[-1], x))
        path.append(nearest)
        must_visit.remove(nearest)
    return path


def main():
    #doctest.testmod()
    points = [[[0, 0, 0]], [[1, 5.7, 0]], [[2, 3, 1]], [[3, 7, 1 ]],
              [[0.5, 9, 2]], [[3, 5, 2]], [[9, 1, 3]], [[10, 5, 3]]]
    print("""The minimum distance to visit all the following points: {}
starting at {} is {}.

The optimized algoritmh yields a path long {}.""".format(
        tuple(points),
        points[0],
        total_distance(travelling_salesman(points)),
        total_distance(optimized_travelling_salesman(points))))

main()


The minimum distance to visit all the following points: ([[0, 0, 0]], [[1, 5.7, 0]], [[2, 3, 1]], [[3, 7, 1]], [[0.5, 9, 2]], [[3, 5, 2]], [[9, 1, 3]], [[10, 5, 3]])
starting at [[0, 0, 0]] is [[ 27.46496619]].

The optimized algoritmh yields a path long [[ 29.38399704]].

In [ ]: