首页 > 解决方案 > Python 用 Pillow 对多个作物进行图像处理,并用 Bokeh 分组并显示在一行中

问题描述

我有以下任务:生成多个图像裁剪,然后将它们分组显示在彼此相邻的行中。到目前为止,我设法根据坐标生成作物。我不知道如何按行显示图像,按“用户”分组并按“时间”排序。我应该使用 Bokeh 来绘制图像,因为会有更多的情节需要整合。请帮忙!!!!

数据框:

#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()

图像文件: 在此处输入图像描述

根据坐标生成作物:

#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))

输出示例:

在此处输入图像描述

标签: pythonimagepython-imaging-librarybokeh

解决方案


像这样的东西。请注意,Buc.jpg当您运行脚本时,它必须在当前工作目录中可用。

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)

推荐阅读