功能

合并前景与背景两张图片,可设置图片前景的透明度,缩放因子,位置,可自适应调整背景大小并且输出图片所在边界框位置,用于标注数据

实现

  1. 利用PIL包,将两张图片进行 paste
  2. 通过修改图片 alpha 值,对图片透明度进行修改
  3. 对图片的大小处理,获取最终的图片尺寸
  4. 根据调整后的前景图片尺寸,调整背景的尺寸
  5. 对前景边界框进行处理,得到合理的边界坐标

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def xywh2xyxyxyxy(x,y,w,h):
return [[x,y],[x+w,y],[x+w,y+h],[x,y+h]]

def merge_image(fore_image, back_image, pos=(0,0),alpha=1.0,factor=1.0,padding=False,func=lambda x:x):
"""将两个图片进行合并,支持自定义透明度 alpha, 缩放factor, 背景图缩放开关 padding, 前景图回调函数 func

Args:
fore_image (PIL.Image): 前景图
back_image (PIL.Image): 背景图
pos (tuple, list): 左上角位置. Defaults to (0,0).
alpha (float, optional): 前景透明度. Defaults to 1.0.
factor (float, optional): 前景缩放因子. Defaults to 1.0.
padding (bool, optional): 背景自适应缩放. Defaults to False.
func (Functional, optional): 前景处理回调函数. Defaults to lambdax:x.

Returns:
PIL.Image: 合并后的图片
"""
assert alpha <= 1.0, "Alpha must be less or equel than 1.0"
final_image = back_image.copy()
fore_image_temp = fore_image.copy()

# 设置缩放因子 透明度
fore_h, fore_w = fore_image_temp.size
print(fore_w,fore_h)
fore_new_w = int(fore_w / factor)
fore_new_h = int(fore_h / factor)
for i in range(fore_new_w):
for k in range(fore_new_h):
color = fore_image_temp.getpixel((i, k))
color = color[:-1] + (int((color[-1]*alpha)%255), )
fore_image_temp.putpixel((i, k), color)
fore_image_temp = fore_image_temp.resize((fore_new_w, fore_new_h), Image.Resampling.LANCZOS)
fore_image_temp = func(fore_image_temp)

# 背景图不够大将会被resize
if padding:
back_limit_w = fore_new_w + pos[0]
back_limit_h = fore_new_h + pos[1]
else:
back_limit_w = fore_new_w
back_limit_h = fore_new_h

# 设置背景图片大小
back_h,back_w = final_image.size
# 设置背景图大小
if back_w < back_limit_w:
back_new_w = back_limit_w
print(f"背景图宽度过小, {back_w} < {back_limit_w}")
if back_h < back_limit_h:
back_new_h = back_limit_h
print(f"背景图高度过小, {back_h} < {back_limit_h}")
final_image = final_image.resize((back_new_w,back_new_h),Image.Resampling.LANCZOS)

final_image.paste(fore_image_temp, pos, mask=fore_image_temp)
fore_final_h, fore_final_w = fore_image_temp.size
back_final_h, back_final_w =final_image.size
label_x = pos[0] if pos[0]>0 else 0
label_y = pos[1] if pos[1]>0 else 0
label_w = pos[0]+fore_final_w-label_x if pos[0]+fore_final_w < back_final_w else back_final_w-label_x
label_h = pos[1]+fore_final_h-label_y if pos[1]+fore_final_h < back_final_h else back_final_h-label_y

return final_image, xywh2xyxyxyxy(label_x,label_y,label_w,label_h)

演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if __name__ == '__main__':
fore_image = Image.open("fore/hh.png").convert("RGBA")
back_image = Image.open("back/bg01.png").convert("RGBA")
final_image,label = merge_image(fore_image, back_image,pos=(100,100),padding=0,alpha=0.5)
##plt 同时显示多幅图像
label = np.array(label)
# print([:,0])

plt.figure()
plt.subplot(1,3,1)
plt.imshow(fore_image)
plt.subplot(1,3,2)
plt.imshow(back_image)
plt.subplot(1,3,3)
plt.imshow(final_image)
plt.scatter(label[:,0],label[:,1])
plt.show()