#*******************************************************************#
# written by: Margriet Palm (16-12-11) #
#-------------------------------------------------------------------#
# #
#*******************************************************************#
from PIL import Image,ImageDraw,ImageFont
from mahotas import labeled
import numpy as np
import copy
from .AnalysisUtils import getCellOrientation
## @package SimUtils.ImageUtils
# Package with utility functions aimed at making and changing images
# based on the Python Imaging Library (PIL).
def addBox(im,bw,bh,pos,color=None):
""" Draw box on image """
if color is None:
color = (0,0,0)
draw = ImageDraw.Draw(im)
draw.rectangle([pos,(pos[0]+bw,pos[1]+bh)],outline=color)
## Add a time stamp to an image (or any other label)
# @param im: PIL image
# @param stamp: text to put on image
# @param fs: font size
def addTimeStamp(im,stamp,fs=6,fc=None,fontpath='/usr/share/fonts/msttcore/'):
""" Draw a timestamp at the right bottom of the image. """
if fc is None:
fc = (0,0,0)
draw = ImageDraw.Draw(im)
fontsize = int(fs)
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
(w,h) = draw.textsize(str(stamp),font=font)
(nx,ny) = im.size
y = ny-h
x = nx-w
#~ print 'add time ',x,y
draw.text((x,y),str(stamp),fill=fc,font=font)
## Add a label to the top center of the image
# @param im: PIL image
# @param label: text to put on image
# @param fs: font size
def addLabel(im,label,fs=8,fc=None,fontpath='/usr/share/fonts/msttcore/'):
""" Draw label at the top center of the image. """
if fc is None:
fc = (0,0,0)
draw = ImageDraw.Draw(im)
fontsize = int(fs)
font = ImageFont.truetype(fontpath+"/arial.ttf", fontsize)
(w,h) = draw.textsize(str(label),font=font)
(nx,ny) = im.size
y = h
x = int((0.5*nx)-w)
#~ print 'add label ',x,y
draw.text((x,y),str(label),fill=fc,font=font)
def addGrayBar(im,cmin,cmax,values=None,w=.1,mx=10,my=25,fontpath='/usr/share/fonts/msttcore/'):
if values is None:
values = []
# create new image and paste original image on the left side
ny = im.size[1]
nx = int((1+w)*im.size[0]+10+mx)
newim = Image.new('RGB',(nx,ny),(255,255,255))
newim.paste(im,(0,0,im.size[0],im.size[1]))
# create colorbar
bh = ny-2*my
bw = w*im.size[0]
h = bh/(cmax-cmin)
w = 10
y0 = im.size[0]-my-25
x0 = im.size[0]+mx
draw = ImageDraw.Draw(newim)
ymin = y0
for i in range(cmin,cmax):
draw.rectangle([(x0,y0-i*h),(x0+w,y0-(i+1)*h)],fill=(i,i,i),outline=(i,i,i))
ymin = y0-(i+1)*h
#~ draw.rectangle([(x0,y0),(x0+w,ymin)],fill=None,outline=(0,0,0))
fontsize = 24
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
rbh = y0-ymin
for i,v in enumerate(values):
pos = i/float(len(values)-1)
ypos = y0-pos*rbh
xpos = x0+w+10
draw.text((xpos,ypos),str(v),fill=(0,0,0),font=font)
return newim
def getGrayBar(cmin,cmax,values=None,bw=50,h=500,mx=10,my=25,fontsize=12,border=False,fontpath="/usr/share/fonts/msttcore/"):
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
tw = 0
th = 0
for val in values:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(val),font=font)
if lsize[0] > tw:
tw = lsize[0]
th = lsize[1]
im = Image.new('RGB',(bw+2*mx+int(1.1*tw),h),(255,255,255))
draw = ImageDraw.Draw(im)
bh = h-2*my
dh = bh/(cmax-cmin)
y0 = h-my-th/2
#~ print y0
x0 = mx
ymin = 0
for i in range(cmin,cmax):
draw.rectangle([(x0,y0-i*dh),(x0+bw,y0-(i+1)*dh)],fill=(i,i,i),outline=(i,i,i))
ymin = y0-(i+1)*dh
if border:
draw.rectangle([(x0,y0),(x0+bw,ymin)],fill=None,outline=(0,0,0))
#~ fontsize = 12
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
rbh = y0-ymin
for i,v in enumerate(values):
pos = i/float(len(values)-1)
ypos = y0-pos*rbh-th/2
xpos = x0+bw+10
#~ print v,ypos
draw.text((xpos,ypos),str(v),fill=(0,0,0),font=font)
return im
def addColorBar(im,cmap,values,w=.1,mx=10,my=25,fontpath='/usr/share/fonts/msttcore/'):
# create new image and paste original image on the left side
ny = im.size[1]
nx = int((1+w)*im.size[0]+10+mx)
newim = Image.new('RGB',(nx,ny),(255,255,255))
newim.paste(im,(0,0,im.size[0],im.size[1]))
# create colorbar
bh = ny-2*my
bw = w*im.size[0]
h = bh/256
w = 10
y0 = im.size[0]-my-25
x0 = im.size[0]+mx
draw = ImageDraw.Draw(newim)
ymin = y0
for i in range(0,256):
color = tuple(int(255*cmap[i,idx]) for idx in range(0,3))
draw.rectangle([(x0,y0-i*h),(x0+w,y0-(i+1)*h)],fill=color,outline=color)
ymin = y0-(i+1)*h
#~ draw.rectangle([(x0,y0),(x0+w,ymin)],fill=None,outline=(0,0,0))
fontsize = 24
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
rbh = y0-ymin
for i,v in enumerate(values):
pos = i/float(len(values)-1)
ypos = y0-pos*rbh
xpos = x0+w+10
draw.text((xpos,ypos),str(v),fill=(0,0,0),font=font)
return newim
def drawColorBar(fn,cmap,nx,ny):
# create new image and paste original image on the left side
h = int(np.round(ny/256.0))
ny = 256*h
newim = Image.new('RGB',(nx,ny),(255,255,255))
# create colorbar
bh = ny
bw = nx
#~ h = bh/256
w = bw
y0 = ny
x0 = 0
draw = ImageDraw.Draw(newim)
ymin = y0
for i in range(0,256):
color = tuple(int(255*cmap[i,idx]) for idx in range(0,3))
draw.rectangle([(x0,y0-i*h),(x0+w,y0-(i+1)*h)],fill=color,outline=color)
ymin = y0-(i+1)*h
newim.save(fn+'.png')
[docs]def stackImages(images,geometry,filename,label=False,title=None,fontsize=20,border=False,scale=1,fontpath="/usr/share/fonts/msttcore/"):
""" Stack a set of images together in one image.
:param images: dictionary with labels as keys and image filenames as values
:param geometry: number of rows and columns (x,y)
:param filename: target of the stacked image
:param label: add labels to the subimages
:param title: overall title for image
:param fontsize: fontsize for labels and title
:param border: add border to subimages
:type border: bool
:param scale: scaling factor of the created picture
"""
fontsize=int(fontsize*scale)
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
labels = images.keys()
nx = 0
ny = 0
for im in images.values():
sz = Image.open(im).size
if sz[0] > nx:
nx = sz[0]
ny = sz[1]
imsize = (scale*nx,scale*ny)
if label:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(labels[0]),font=font)
imsize = (int(scale*imsize[0]),int(scale*imsize[1])+lsize[1])
#~ imsize[1] += lsize[1]
#---- Create new image ----#
offsetX = 5
offsetY = 0
imw = int(np.ceil(imsize[0]*geometry[0]+2*offsetX))
imh = int(np.ceil(imsize[1]*geometry[1]+offsetY))
if title is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
tsize = draw.textsize(str(title),font=font)
im = Image.new('RGBA',(imw,imh+tsize[1]),(255,255,255))
offsetY = tsize[1]
else:
im = Image.new('RGBA',(imw,imh),(255,255,255))
offsetY = 0
draw = ImageDraw.Draw(im)
#----- Put plots on canvas ----#
labels.sort()
for i,l in enumerate(labels):
x0 = int(i%geometry[0]*imsize[0]+offsetX)
y0 = int((i/geometry[0])*imsize[1]+offsetY)
newim = Image.open(images[l])
im.paste(newim.resize((int(nx*scale),int(ny*scale))),(x0,y0))
if border:
draw.rectangle([(x0,y0),(x0+int(nx*scale),y0+int(ny*scale))],outline=(0,0,0))
if label:
for i,l in enumerate(labels):
tsize = draw.textsize(str(l),font=font)
x0 = (i%geometry[0])*imsize[0]+0.5*imsize[0]-0.5*tsize[0]+offsetX
y0 = (i/geometry[0])*imsize[1]+1*imsize[1]-tsize[1]+offsetY
draw.text((x0,y0),str(l),fill=(0,0,0),font=font)
if not (title is None):
tsize = draw.textsize(str(title),font=font)
draw.text((im.size[0]/2.0-0.5*tsize[0]+offsetX,0),str(title),fill=(0,0,0),font=font)
#----- Save image -----#
#~ if scale is not 1:
#~ im = im.resize((int(scale*im.size[0]),int(scale*im.size[1])))
im.save(filename)
[docs]def morphImages(images,filename,xlabel=None,ylabel=None,xtics=None,ytics=None,fontsize=20,scale=1,border=False,title=None,bcolor=(255,255,255),fcolor=(0,0,0),fontpath='/usr/share/fonts/msttcore/',delta=0):
""" Stack a set of images together in one morphospace.
:param images: 2D array with image filenames
:param filename: target of the stacked image
:param xlabel: label to be plotted on x-axis
:param ylabel: label to be plotted on y-axis
:param xtics: items on x-axis
:param ytics: items on y-axis
:param fontsize: fontsize for labels and title
:param scale: scaling factor of the created picture
:param border: add border to subimages
:param title: overall title for image
"""
font = ImageFont.truetype(fontpath+"/times.ttf", fontsize)
tfont = ImageFont.truetype(fontpath+"/times.ttf", int(1.1*fontsize))
#~ subimsize = Image.open(images[0][0]).size
orgsize = Image.open(images[0][0]).size
subimsize = (int(scale*orgsize[0]+delta),int(scale*orgsize[1]+delta))
imsize = [subimsize[0]*len(images[0])-delta,subimsize[1]*len(images)-delta]
oX = 0
oY = 0
toX = 0
toY = 0
if xlabel is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(xlabel),font=font)
imsize[1] = imsize[1]+lsize[1]
oY += lsize[1]
toY += lsize[1]
if ylabel is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(ylabel),font=font)
imsize[0] += lsize[1]
oX += lsize[1]
toX += lsize[1]
if xtics is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(xtics[0]),font=font)
imsize[1] += lsize[1]
oY += lsize[1]
if ytics is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(ytics[0]),font=font)
imsize[0] += lsize[1]
oX += lsize[1]
loY = 0
if title is not None:
im = Image.new('RGB',(10,10))
draw = ImageDraw.Draw(im)
lsize = draw.textsize(str(title),tfont)
offset = lsize[1]+20*scale
imsize[1] += offset
oY += offset
if xlabel is not None:
loY += offset
toY += offset
#---- Create new image ----#
im = Image.new('RGBA',imsize,bcolor)
draw = ImageDraw.Draw(im)
#----- Put plots on canvas ----#
for i in range(len(images)):
for j in range(len(images[0])):
#~ print i,j,len(images),len(images[0])
newim = Image.open(images[i][j])
x0 = j*subimsize[0]+oX
y0 = i*subimsize[0]+oY
im.paste(newim.resize((int(scale*orgsize[0]),int(scale*orgsize[1]))),(x0,y0))
#~ im.paste(newim.resize(subimsize),(x0,y0))
if border:
draw.rectangle([(x0,y0),(x0+newim.size[0],y0+newim.size[1])],outline=(0,0,0))
#----- Draw axis and tics -----#
ticsfont = ImageFont.truetype(fontpath+"/times.ttf", int(.75*fontsize))
if xtics is not None:
for i in range(len(xtics)):
tsize = draw.textsize(str(xtics[i]),font=ticsfont)
x0 = (i+0.5)*subimsize[0]+oX-int(tsize[0]/2.0)
y0 = toY
draw.text((x0,y0),str(xtics[i]),fill=fcolor,font=ticsfont)
if ytics is not None:
for i in range(len(ytics)):
x0 = toX
y0 = int((i+.5)*subimsize[1]+oY)
#~ draw.text((x0,y0),str(ytics[i]),fill=(0,0,0),font=ticsfont)
tsize = draw.textsize(str(ytics[i]),font=ticsfont)
tim = Image.new('RGBA',(tsize[0],tsize[1]),bcolor)
tdraw = ImageDraw.Draw(tim)
tdraw.text((0,0),str(ytics[i]),fill=fcolor,font=ticsfont)
tim = tim.rotate(90)
y0 -= int(tsize[0]/2.0)
im.paste(tim,(x0,y0))
if xlabel is not None:
tsize = draw.textsize(str(xlabel),font=font)
draw.text((im.size[0]/2.0-0.5*tsize[0],loY),str(xlabel),fill=fcolor,font=font)
if ylabel is not None:
tsize = draw.textsize(str(ylabel),font=font)
tim = Image.new('RGBA',(tsize[0],tsize[1]),bcolor)
tdraw = ImageDraw.Draw(tim)
tdraw.text((0,0),str(ylabel),fill=fcolor,font=font)
tim = tim.rotate(90)
im.paste(tim,(0,int(im.size[1]/2.0-tsize[0])))
if title is not None:
tsize = draw.textsize(str(title),font=tfont)
draw.text((im.size[0]/2.0-0.5*tsize[0],0),str(title),fill=fcolor,font=tfont)
#----- Save image -----#
im.save(filename)
def drawField(field,scale=1,gv=None,raw=False,nx=None,ny=None):
""" Draw gray-scale image of a field
:param field: numpy array with field
:param scale: scaling factor
:param gv: minimum and maximum color value
:param raw: use raw values of field, otherwise values are mapped on the range of gv
:return: image object
"""
if (nx is None) or (ny is None):
(nx,ny) = field.shape
if gv is None:
gv = [100,255]
if (not raw) and ((np.max(field)-np.min(field)) > 0):
field = (((field-np.max(field))/(np.min(field)-np.max(field)))*(gv[1]-gv[0]))+gv[0]
fim = Image.fromarray(field)
fim = fim.resize((int(scale*nx),int(scale*ny)))
fim.convert("RGB")
im = Image.fromarray(np.uint8(np.dstack((np.asarray(fim),np.asarray(fim),np.asarray(fim)))))
im = im.transpose(Image.FLIP_TOP_BOTTOM)
return im
def drawFieldWithMap(field,sigma,cmap,scale=1):
""" Draw gray-scale image of a field
:param field: numpy array with field
:param scale: scaling factor
:param cmap: numpy array with rows [r g b alpha]
:return: image object
"""
(nx,ny) = field.shape
field = np.transpose((field.astype(np.float)))
sigma = np.transpose((sigma.astype(np.float)))
im = Image.fromarray(sigma)
im = im.resize((int(scale*nx),int(scale*ny)))
sigma = np.asarray(im)
#~ field = (((field-np.max(field))/(np.min(field)-np.max(field)))*(255)).astype(np.int)
field = ((255.0/np.max(field))*field).astype(np.int)
fim = Image.fromarray(field.astype(np.float))
fim = fim.resize((int(scale*nx),int(scale*ny)))
field = np.asarray(fim)
a = np.dstack((np.asarray(np.zeros_like(field)),np.asarray(np.zeros_like(field)),np.asarray(np.zeros_like(field))))
for val in np.unique(field):
#~ if val == 0:
#~ continue
color = [int(255*cmap[val,i]) for i in range(0,3)]
#~ print val,color
a[np.where(field==val)] = color
a[np.where(sigma==0)] = [255,255,255]
return Image.fromarray(np.uint8(a))
[docs]def drawRelDirField(field,sigma,scale=1):
""" Draw gray-scale image of a field of the relative director
:param field: numpy array with field
:param scale: scaling factor
:param gv: minimum and maximum color value
:param raw: use raw values of field, otherwise values are mapped on the range of gv
:return: image object
"""
(nx,ny) = field.shape
gv = [0,220]
field = (((field-np.max(field))/(np.min(field)-np.max(field)))*(gv[1]-gv[0]))+gv[0]
field[np.where(sigma==0)] = 255
fim = Image.fromarray(field)
fim = fim.resize((int(scale*nx),int(scale*ny)))
fim.convert("RGB")
im = Image.fromarray(np.uint8(np.dstack((np.asarray(fim),np.asarray(fim),np.asarray(fim)))))
im = im.transpose(Image.FLIP_TOP_BOTTOM)
#~ im = im.rotate(90)
return im
def _getImAsArray(sigma,types,field,colormap,scale=1,nx=None,ny=None,transparant=False,gv=None,bc=None):
if bc is None:
bc = (0,0,0)
(nx,ny) = sigma.shape
sigma = np.transpose((sigma.astype(np.float)))
types = np.transpose((types.astype(np.float)))
field = np.transpose((field.astype(np.float)))
if np.sum(sigma) == 0:
im = Image.fromarray(255*np.uint8(np.ones_like(sigma)))
im = im.resize((int(scale*nx),int(scale*ny)))
return np.asarray(im),sigma,np.zeros_like(sigma)
if gv is None:
gv = [100,255]
if ((np.max(field)-np.min(field)) > 0):
field = (((field-np.max(field))/(np.min(field)-np.max(field)))*(gv[1]-gv[0]))+gv[0]
fim = Image.fromarray(field)
fim = fim.resize((int(scale*nx),int(scale*ny)))
fim.convert("RGB")
im = Image.fromarray(sigma)
im = im.resize((int(scale*nx),int(scale*ny)))
sigma = np.asarray(im)
tim = Image.fromarray(np.uint8(types))
tim = tim.resize((int(scale*nx),int(scale*ny)))
types = np.asarray(tim)
sigMin = 0
bim = None
while sigMin < np.max(sigma):
imtemp = copy.deepcopy(np.asarray(im))
imtemp[np.where(imtemp < sigMin)] = 0
imtemp[np.where(imtemp > sigMin+254)] = 0
if bim is None:
bim = labeled.borders(imtemp)
else:
bim += labeled.borders(imtemp)
sigMin += 254
if np.sum(field) == 0:
imnew = colormap[0]*np.ones((scale*ny,scale*nx,3))
else:
imnew = np.dstack((np.asarray(fim),np.asarray(fim),np.asarray(fim)))
if not transparant:
for tp in np.unique(types):
if tp == 0:
continue
imnew[types==tp] = colormap[tp]
#~ imnew[types==0] = colormap[0]
# set border black
imnew[bim] = bc
return imnew,sigma,np.asarray(tim)
def drawCellsHL(sigma,types,field,colormap,scale=1,nx=None,ny=None,transparant=False,gv=None,HLid=None,HLcolor=None):
""" Draw cells, colored by cell type and highlight one single cell in another color.
:param sigma: numpy array with cell id's
:param types: numpy array with types
:param field: numpy array with field
:param colormap: dictionary with cell type as keys and corresponding color as values
:param scale: scaling factor
:param transparant: if True, only field and cell borders are drawn
:param gv: minimum and maximum color value
:param HLid: cell id of highlighted cell.
:param HLcolor: color of highlighted cell (r,g,b)
:return: image object
"""
if HLcolor is None:
HLcolor = (255,0,0)
mtype = np.max(np.unique(types))
colormap[mtype+1] = HLcolor
typesHL = copy.deepcopy(types)
if HLid is not None:
for hlid in HLid:
typesHL[sigma==hlid] = mtype+1
#~ imnew[sigma==hlid] = HLcolor
(imnew,sigma,types) = _getImAsArray(sigma,typesHL,field,colormap,scale,nx,ny,transparant,gv)
im = Image.fromarray(np.uint8(imnew))
im = im.rotate(90)
return im
def drawCells(sigma,types,field,colormap,scale=1,nx=None,ny=None,transparant=False,gv=None,bc=None):
""" Draw cells, colored by cell type
:param sigma: numpy array with cell id's
:param types: numpy array with types
:param field: numpy array with field
:param colormap: dictionary with cell type as keys and corresponding color as values
:param scale: scaling factor
:param transparant: if True, only field and cell borders are drawn
:param gv: minimum and maximum color value
:param bc: color of cell borders
:return: image object
"""
(imnew,sigma,types) = _getImAsArray(sigma,types,field,colormap,scale=scale,nx=nx,ny=ny,transparant=transparant,gv=gv,bc=bc)
im = Image.fromarray(np.uint8(imnew))
im = im.rotate(90)
return im
def drawGS(sigma,types,field,colormap,scale=1,nx=None,ny=None):
if (nx is None) or (ny is None):
(nx,ny) = sigma.shape
gv = [100,255]
field = (((field-np.max(field))/(np.min(field)-np.max(field)))*(gv[1]-gv[0]))+gv[0]
fim = Image.fromarray(field)
fim = fim.resize((int(scale*nx),int(scale*ny)))
fim.convert("RGB")
im = Image.fromarray(sigma)
im = im.resize((int(scale*nx),int(scale*ny)))
tim = Image.fromarray(np.uint8(types))
tim = tim.resize((int(scale*nx),int(scale*ny)))
types = np.asarray(tim)
sigMin = 1
bim = None
while sigMin < np.max(sigma):
imtemp = copy.deepcopy(np.asarray(im))
imtemp[np.where(imtemp < sigMin)] = 0
imtemp[np.where(imtemp > sigMin+254)] = 0
if bim is None:
bim = labeled.borders(imtemp)
else:
bim += labeled.borders(imtemp)
sigMin += 254
print np.sum(field)
if np.sum(field) == 0:
imnew = 255*np.ones((scale*nx,scale*ny))
else:
#~ imnew = np.dstack((np.asarray(fim),np.asarray(fim),np.asarray(fim)))
imnew = copy.deepcopy(np.asarray(fim))
for tp in np.unique(types):
if tp == 0:
continue
imnew[types==tp] = colormap[tp]
# set border black
imnew[bim] = 0
im = Image.fromarray(np.uint8(imnew))
return im
def drawCellsWithOrientation(sigma,scale=1,bc=None):
""" Draw cells, colored by cell angle.
:param sigma: numpy array with cell id's
:param scale: scaling factor
:param bc: color of cell borders
:return: image object
"""
if bc is None:
bc = (0,0,0)
(nx,ny) = sigma.shape
sigma = np.transpose(sigma)
if np.sum(sigma) == 0:
#~ print 'empty field'
im = Image.fromarray(255*np.uint8(np.ones_like(sigma)))
im = im.resize((int(scale*nx),int(scale*ny)))
return im
(nx,ny) = sigma.shape
im = Image.fromarray(sigma)
im = im.resize((int(scale*nx),int(scale*ny)))
sigMin = 0
bim = None
while sigMin < np.max(sigma):
imtemp = copy.deepcopy(np.asarray(im))
imtemp[np.where(imtemp < sigMin)] = 0
imtemp[np.where(imtemp > sigMin+254)] = 0
if bim is None:
bim = labeled.borders(imtemp)
else:
bim += labeled.borders(imtemp)
sigMin += 254
imnew = 255*np.ones((scale*ny,scale*nx,3))
cells = np.unique(sigma)
sim = Image.fromarray(np.uint8(sigma))
sim = sim.resize((int(scale*nx),int(scale*ny)))
ssigma = np.asarray(sim)
for cell in cells:
if cell == 0:
continue
pix = np.where(sigma==cell)
a = getCellAngle(pix)
color = 55+int((255-55)*a/np.pi)
imnew[ssigma==cell] = [color,color,color]
imnew[sigma==0] = [255,255,255]
imnew[bim] = bc
im = Image.fromarray(np.uint8(imnew))
im = im.rotate(90)
return im
def drawClusters(clusters,sigma,outbase,scale=2):
""" Draw image for each cluster highlighting the cells in that cluster.
:param cluster: list of :class:Cluster instances
:param sigma: CPM grid
:param outbase: basename of the output files (including path), a suffix _cluster_clusterID.png will be added
:param scale: scaling factor
"""
colormap = {0:(255,255,255),1:(200,200,200),2:(255,0,0)}
for cid,cluster in clusters.iteritems():
types = np.zeros_like(sigma)
types[sigma>0] = 1
for cell in cluster.cells:
types[sigma==cell] = 2
im = drawCells(sigma,types,np.zeros_like(types),colormap,scale=scale)
out = outbase+'_cluster_'+string.zfill(cid,3)+'.png'
im = im.transpose(Image.FLIP_LEFT_RIGHT)
im = im.rotate(180)
im.save(out)
def drawTrajectories(traj,out,cm,scale=2,size=[200,200],crop=False,offset=0,fontpath='/usr/share/fonts/liberation/LiberationSans-Regular.ttf',legend=None,bg=(255,255,255),fc=(0,0,0)):
xmin = 0
ymin = 0
if crop:
xmax = 0
ymax = 0
for tj in traj.values():
x = np.array(tj)[:,0]
y = np.array(tj)[:,1]
if (xmin == 0) or (xmin > x.min()):
xmin = x.min()
if (ymin == 0) or (ymin > y.min()):
ymin = y.min()
if x.max() > xmax:
xmax = x.max()
if y.max() > ymax:
ymax = y.max()
print xmin,xmax,ymin,ymax
size = [int(xmax-xmin+1),int(ymax-ymin+1)]
im = Image.new('RGBA',(size[0]*scale+2*offset,size[1]*scale+2*offset),bg)
draw = ImageDraw.Draw(im)
p0 = np.array([0,0])
p1 = np.array([0,0])
for id,tj in traj.iteritems():
# scale polygon!!!!
polygon = [(scale*(p[0]-xmin)+offset,scale*(p[1]-ymin)+offset) for p in tj]
draw.line(polygon,fill=cm[id])
p0 += polygon[0]
p1 += polygon[-1]
fs = int(3*scale)
font = ImageFont.truetype(fontpath, fs)
draw.text(tuple(p0/float(len(traj))),'start',fill=fc,font=font)
draw.text(tuple(p1/float(len(traj))),'end',fill=fc,font=font)
if legend is not None:
x0 = offset
y0 = im.size[1]
print im.size
for text,color in legend.iteritems():
print text,x0,y0
(w,h) = draw.textsize(str(text),font=font)
y0 -= 1.1*h
draw.text((x0,y0),text,fill=color,font=font)
im.save(out)
def drawCellGraph(g,image=None,filename=None,scale=1,size=[200,200]):
""" Draw graph of cell connections
:param graph: cell graph
:param image: image to draw the graph on, if omitted a clear canvas is used
:param filename: filename to save the image to, if omitted image is returned instead
:param scale: scaling factor
:param size: image size [nx,ny]
:return: image whene filename is omitted
"""
if image is None:
xmax = size[0]*scale
ymax = size[1]*scale
im = Image.new('RGBA',(xmax,ymax),(255,255,255))
else:
im = image
im = im.rotate(270)
draw = ImageDraw.Draw(im)
for e in g.edges():
x0 = g.node_attributes(e[0])[0]['pos'][0]*scale
y0 = g.node_attributes(e[0])[0]['pos'][1]*scale
x1 = g.node_attributes(e[1])[0]['pos'][0]*scale
y1 = g.node_attributes(e[1])[0]['pos'][1]*scale
draw.line(((x0,y0),(x1,y1)),fill=(0,0,0))
for n in g.nodes():
attr = g.node_attributes(n)[0]
x = attr['pos'][0]*scale
y = attr['pos'][1]*scale
w = attr['size']*scale
c = attr['color']
draw.ellipse((x-w,y-w,x+w,y+w),fill=c)
if (image is None):
if filename is None:
filename = 'graph.png'
im.save(filename+'.png',"PNG")
else:
return im
def drawNodesColored(nodelist,image,filename,colormap,scale=1,r=2):
""" Draw nodes as circles on a morphology.
:param nodelist: dictionary with {nodeid : n(x,y)}.
:param image: PIL image with the morphology.
:param filename: file to store output image to.
:param colormap: map with node colors {nodeid : (r,g,b)}.
:param scale: scaling, should fit with the morpholog.
:param r: radius of the circles at the node positions (without scaling).
"""
(nx,ny) = image.size
im = image
im.convert("RGBA")
pc = (255,0,0)
r = r*scale
r = r*scale
draw = ImageDraw.Draw(im)
for nid,node in nodelist.iteritems():
if nid in colormap:
c = colormap[nid]
else:
c = (255,0,0)
x = scale*node[0]
y = scale*node[1]
draw.ellipse((x-r,y-r,x+r,y+r),fill=c)
im.save(filename+'_nodes.png')
def drawSkelOnMorph(skel,morph,filename,scale=1,save=True,skelcol=[0,0,0]):
""" Draw nodes as circles on a morphology.
:param skel: array representation of the skeleton (as returned by getNodes).
:param morph: PIL image with the morphology.
:param filename: file to store output image to.
:param scale: scaling, should fit with the morpholog.
"""
morph = morph.rotate(180)
morph = morph.transpose(Image.FLIP_LEFT_RIGHT)
(nx,ny) = morph.size
skelim = Image.fromarray(np.uint8(skel))
skelim = skelim.resize((int(scale*skelim.size[0]),int(scale*skelim.size[1])))
skelsc = np.asarray(skelim)
imar = np.asarray(morph)
imar.flags.writeable = True
#~ imar[np.where(skelsc==1)] = [255,255,255]
imar[np.where(skelsc==1)] = skelcol
im = Image.fromarray(imar)
#~ im = im.transpose(Image.FLIP_LEFT_RIGHT)
#~ im = im.rotate(90)
if save:
im.save(filename+'_skel.png')
else:
return im
def drawNodesOnMorph(nodes,morph,filename,scale=1,r=2,save=True,nodecol=(0,0,0)):
""" Draw nodes as circles on a morphology.
:param skel: array representation of the skeleton (as returned by getNodes).
:param morph: PIL image with the morphology.
:param filename: file to store output image to.
:param scale: scaling, should fit with the morpholog.
"""
morph = morph.rotate(-90)
morph = morph.transpose(Image.FLIP_LEFT_RIGHT)
r = r*scale
r = r*scale
draw = ImageDraw.Draw(morph)
for node in nodes.values():
x = scale*node[0]
y = scale*node[1]
draw.ellipse((x-r,y-r,x+r,y+r),fill=nodecol)
if save:
morph.save(filename+'_nodes.png')
else:
return morph
def drawNodesAndEdgesOnMorph(nodes,edges,morph,filename,scale=1,r=2,save=True,nodecol=(0,0,0)):
""" Draw nodes as circles on a morphology.
:param skel: array representation of the skeleton (as returned by getNodes).
:param morph: PIL image with the morphology.
:param filename: file to store output image to.
:param scale: scaling, should fit with the morpholog.
"""
morph = morph.rotate(-90)
#~ morph = morph.transpose(Image.FLIP_LEFT_RIGHT)
r = r*scale
r = r*scale
draw = ImageDraw.Draw(morph)
for node in nodes.values():
x = scale*node[0]
y = scale*node[1]
draw.ellipse((x-r,y-r,x+r,y+r),fill=nodecol)
for e1,e2 in edges:
x1 = scale*nodes[e1][0]
y1 = scale*nodes[e1][1]
x2 = scale*nodes[e2][0]
y2 = scale*nodes[e2][1]
draw.line(((x1,y1),(x2,y2)),fill=(0,0,0))
if save:
morph.save(filename+'_nodes.png')
else:
return morph