I have the following task: generate multiple image crops, and then display them grouped in rows next to each other. So far I managed to generates crops based on coordinates. I have no clue how to display the images in rows, grouped by 'User' and ordered by 'Time'. I should use Bokeh for plotting the images, because there will be more plots to integrate. Please help!!!!
The dataframe:
#Import libraries
import numpy as np
from numpy import asarray
import pandas as pd
from PIL import Image
import matplotlib as plt
#Create the dataframe
data = {'Time': ['2586', '2836', '2986', '3269', '3702'],
'Map': ['Buc.jpg', 'Buc.jpg', 'Buc.jpg', 'Buc.jpg', 'Buc.jpg'],
'Index': ['9', '10', '11', '12', '13'],
'PSize': ['250', '150', '283', '433', '183'],
'X': ['751', '673', '542', '762', '624'],
'Y': ['458', '316', '287', '303', '297'],
'User': ['u1', 'u1', 'u2', 'u2', 'u2'],
}
columns = ['Time','Map','Index','PSize','X','Y','User']
df = pd.DataFrame (data, columns = columns)
df = df.astype(dtype= {"Time":"int64", "Map":"object","Index":"int64", 'PSize':'int64', 'X': 'int64', 'Y':"int64", 'User':'object'})
df.head()
Generate the crops based on coordinates:
#Create coordinate and crop the image
imagefile = 'Buc.jpg'
coordinates = list(df[['X', 'Y']].itertuples(index=False, name=None))
psize = 100
img = Image.open(imagefile)
for x, y in coordinates:
box = (x-psize/2, y-psize/2, x+psize/2, y+psize/2)
img.crop(box).show('%s.x%03d.y%03d.jpg'% (imagefile.replace('.jpg',''), x, y))
Output example:
Something like this. Note that Buc.jpg
must be available within the current working directory when you run the script.
import numpy as np
import pandas as pd
from PIL import Image
from bokeh.io import show
from bokeh.models import FixedTicker, FuncTickFormatter, ColumnDataSource
from bokeh.plotting import figure
from bokeh.transform import dodge
df = pd.DataFrame({'Time': [2586, 2836, 2986, 3269, 3702],
'X': [751, 673, 542, 762, 624],
'Y': [458, 316, 287, 303, 297],
'User': ['u1', 'u1', 'u2', 'u2', 'u2']})
imagefile = 'Buc.jpg'
coordinates = list(df[['X', 'Y']].itertuples(index=False, name=None))
psize = 100
img = Image.open(imagefile).convert('RGBA')
cropped_images = []
for x, y in coordinates:
box = (x - psize / 2, y - psize / 2, x + psize / 2, y + psize / 2)
cropped_images.append(np.array(img.crop(box)).view(np.uint32)[::-1])
df['Image'] = cropped_images
# There's probably a better method to populate `TimeCoord` which I don't know.
df = df.sort_values('Time')
df['TimeCoord'] = 0
for u in df['User'].unique():
udf = (df['User'] == u)
df.loc[udf, 'TimeCoord'] = np.arange(udf.sum())
user_coords = dict(zip(df['User'].unique(), range(df.shape[0])))
df['UserCoord'] = df['User'].replace(user_coords)
p = figure(match_aspect=True)
for r in [p.xaxis, p.xgrid, p.ygrid]:
r.visible = False
# Manually creating a categorical-like axis to make sure that we can use `dodge` below.
p.yaxis.ticker = FixedTicker(ticks=list(user_coords.values()))
p.yaxis.formatter = FuncTickFormatter(args=dict(rev_user_coords={v: k for k, v in user_coords.items()}),
code="return rev_user_coords[tick];")
ds = ColumnDataSource(df)
img_size = 0.8
p.image_rgba(image='Image',
x=dodge('TimeCoord', -img_size / 2), y=dodge('UserCoord', -img_size / 2),
dw=img_size, dh=img_size, source=ds)
p.rect(x='TimeCoord', y='UserCoord', width=img_size, height=img_size, source=ds,
line_dash='dashed', fill_alpha=0)
show(p)