In [67]:
%%javascript
IPython.OutputArea.prototype._should_scroll = function(lines) {
    return false;
}
In [2]:
from numpy import *
import matplotlib.pyplot as plt
%matplotlib inline

Drawing on top of an image

Use extent option in imshow:

In [8]:
a = 255*ones((400,500,3),dtype=uint8)
a[100:200,:,:] = [150,0,150]
plt.imshow(a,extent=[-2,2,-1.5,1.5])
plt.plot(0,0.5,'wo')
plt.plot([0,1],[0,1],'#FF8000');

Mask for tartan picture

Step by step:

In [13]:
nr = 10
nc = 16
L = 3
m = empty((nr,nc,3),dtype=int)
In [14]:
arange(nc)
Out[14]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
In [15]:
arange(nc)//L
Out[15]:
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5])
In [16]:
mod(arange(nc)//L,2)
Out[16]:
array([0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1])
In [19]:
mod((1+arange(nc))//L,2)  # to offset
Out[19]:
array([0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1])
In [20]:
mod((1+arange(nc))//L,2)==1  # create bool array that we can use for indexing into m
Out[20]:
array([False, False,  True,  True,  True, False, False, False,  True,
        True,  True, False, False, False,  True,  True], dtype=bool)

All together now:

In [9]:
nr = 10
nc = 16
L = 3
m = zeros((nr,nc,3),dtype=uint8)
for i in range(nr):
    m[i,mod((-i+arange(nc))//L,2)==1,:] = 1
m[:,:,0]
Out[9]:
array([[0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1],
       [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0],
       [1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0],
       [0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
       [0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1],
       [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1],
       [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0],
       [1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0]], dtype=uint8)

A bit bigger:

In [10]:
nr = 500
nc = 500
L = 2
m = zeros((nr,nc,3),dtype=uint8)
for i in range(nr):
    m[i,mod((-i+arange(nc))//L,2)==1,:] = 1
#m[:,:,0]

Now let's use the mask to draw a simple woven fabric:

In [12]:
v = zeros((nr,nc,3),dtype=uint8)  # vertical threads
v[:,100:120,:] = [255,128,0]  # add a vertical orange stripe
plt.imshow(v);
In [13]:
v = zeros((nr,nc,3),dtype=uint8)  # vertical threads
v[:,100:120,:] = [255,128,0]
v[:,150:170,:] = [255,128,0]
v[:,130:140,:] = [0  ,  0,200]
plt.imshow(v);

Now apply the mask (effectively weaving with black horizontal threads):

In [14]:
v = zeros((nr,nc,3),dtype=uint8)  # vertical threads
v[:,100:120,:] = [255,128,0]
v[:,150:170,:] = [255,128,0]
v[:,130:140,:] = [0  ,  0,200]
from scipy.misc import imsave
imsave('temp.png',m*v)
plt.figure(figsize=(10,10))
plt.imshow(m*v);

Now use colored horizontal threads too:

In [16]:
v = zeros((nr,nc,3),dtype=uint8)  # vertical threads
v[:,100:120,:] = [255,128,0]
v[:,150:170,:] = [255,128,0]
v[:,130:140,:] = [0  ,  0,200]
from scipy.misc import imsave
h = v.transpose(1,0,2)
a = m*v + (1-m)*h
imsave('temp.png',a)
plt.figure(figsize=(10,10))
plt.imshow(a)
plt.axis('off');

Parse the color palette string

Dictionaries

In [36]:
d = {'apple':'round crispy fruit', 'banana':'long yellow fruit'}
In [37]:
d
Out[37]:
{'apple': 'round crispy fruit', 'banana': 'long yellow fruit'}
In [38]:
d['banana']
Out[38]:
'long yellow fruit'
In [39]:
d['orange'] = 'round orange fruit'
In [40]:
d
Out[40]:
{'apple': 'round crispy fruit',
 'banana': 'long yellow fruit',
 'orange': 'round orange fruit'}
In [41]:
d['orange']
Out[41]:
'round orange fruit'
In [101]:
#p = 'B=2C4084BLUE;K=101010BLACK;G=005020MOD GREEN;W=E0E0E0WHITE;'
p = 'B=2C99FFLIGHT BLUE;K=101010BLACK;G=005020MOD GREEN;W=E0E0E0WHITE;'
In [102]:
colors = p.split(';')[:-1]
colors
Out[102]:
['B=2C99FFLIGHT BLUE', 'K=101010BLACK', 'G=005020MOD GREEN', 'W=E0E0E0WHITE']
In [103]:
[color[0] for color in colors]
Out[103]:
['B', 'K', 'G', 'W']
In [104]:
[zebra[0] for zebra in colors]  # item name can be anything you like!
Out[104]:
['B', 'K', 'G', 'W']

Above assumed every color shorthand was just one character. Evidently some have more than one.

We can accommodate this by splitting on the "=" sign, and taking the part before it:

In [105]:
keys = [color.split('=')[0] for color in colors]
keys
Out[105]:
['B', 'K', 'G', 'W']
In [106]:
values = [color.split('=')[1] for color in colors]
values
Out[106]:
['2C99FFLIGHT BLUE', '101010BLACK', '005020MOD GREEN', 'E0E0E0WHITE']

Let's throw away the descriptor after the 6 hex digits:

In [107]:
values = [color.split('=')[1][:6] for color in colors]
values
Out[107]:
['2C99FF', '101010', '005020', 'E0E0E0']
In [108]:
d = {k:v for k,v in zip(keys,values)}
d
Out[108]:
{'B': '2C99FF', 'G': '005020', 'K': '101010', 'W': 'E0E0E0'}
In [109]:
d['G']
Out[109]:
'005020'
In [110]:
dict(zip(keys,values))  # another way of creating a dict
Out[110]:
{'B': '2C99FF', 'G': '005020', 'K': '101010', 'W': 'E0E0E0'}

Converting hex numbers to ints:

In [111]:
int('FF',16)
Out[111]:
255
In [112]:
# need to convert hex triples to triples of ints
def hex2int(h):
    return [int(h[i:i+2],16) for i in [0,2,4]]
In [113]:
hex2int('005020')
Out[113]:
[0, 80, 32]
In [114]:
s = '005020'
s[2:4]
Out[114]:
'50'

Now we can create a usable color lookup table:

In [115]:
d = {k:hex2int(v) for k,v in zip(keys,values)}
d
Out[115]:
{'B': [44, 153, 255],
 'G': [0, 80, 32],
 'K': [16, 16, 16],
 'W': [224, 224, 224]}
In [116]:
d['B']
Out[116]:
[44, 153, 255]
In [117]:
d['foo']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-117-96e03e4217d2> in <module>()
----> 1 d['foo']

KeyError: 'foo'

Parse the threadcount string

In [118]:
tc = 'B4K4B36K40G36W8G36K40B34K6'
In [119]:
import re  # "regular expressions"
In [120]:
re.findall('[A-Z]+',tc)
Out[120]:
['B', 'K', 'B', 'K', 'G', 'W', 'G', 'K', 'B', 'K']
In [121]:
re.findall('[0-9]+',tc)
Out[121]:
['4', '4', '36', '40', '36', '8', '36', '40', '34', '6']
In [122]:
stripes = re.findall('[A-Z]+',tc)
counts = [int(count) for count in re.findall('[0-9]+',tc)]
In [123]:
stripes
Out[123]:
['B', 'K', 'B', 'K', 'G', 'W', 'G', 'K', 'B', 'K']
In [124]:
counts
Out[124]:
[4, 4, 36, 40, 36, 8, 36, 40, 34, 6]
In [125]:
# to duplicate the colors in reverse order
In [126]:
stripes = re.findall('[A-Z]+',tc)
counts = [int(count) for count in re.findall('[0-9]+',tc)]

# append reversed copies
stripes += stripes[::-1]
counts += counts[::-1]
counts
Out[126]:
[4, 4, 36, 40, 36, 8, 36, 40, 34, 6, 6, 34, 40, 36, 8, 36, 40, 36, 4, 4]
In [127]:
stripes
Out[127]:
['B',
 'K',
 'B',
 'K',
 'G',
 'W',
 'G',
 'K',
 'B',
 'K',
 'K',
 'B',
 'K',
 'G',
 'W',
 'G',
 'K',
 'B',
 'K',
 'B']
In [128]:
nr = sum(counts)
nc = nr
nr
Out[128]:
488

Useful to have the thread number of the start of each stripe. This can be obtained from the cumulative sum (cumsum) of the stripe widths:

In [129]:
cumsum(counts)
Out[129]:
array([  4,   8,  44,  84, 120, 128, 164, 204, 238, 244, 250, 284, 324,
       360, 368, 404, 444, 480, 484, 488])
In [130]:
starts = [0]+list(cumsum(counts))
In [131]:
starts[:16]
Out[131]:
[0, 4, 8, 44, 84, 120, 128, 164, 204, 238, 244, 250, 284, 324, 360, 368]

Now draw the tartan