作者: 昼夜
关键词: 图像替换、作图
在日常的电商工作中,更换商品图是我们比较常见的场景。目前我们现有的商品图更换是通过拿到底图psd文件,再通过PS软件的指令完成替换,这样也非常的便捷。但是在实际的工作中客户可能会遇到一些特殊的情况需要能够根据原有的商品图片(没有底图psd文件的情况下)直接进行更换,方便快速匹配上对应的活动。那么怎么通过常见格式的图片(jpg、png等)来直接进行文件替换呢,我们可以通过以下的操作来实现。
在这个方法中,我们应用到专门处理图像与计算机视觉的一个库--OpenCV,该库提供的透视变换、图像融合等功能可以让我们非常快速的把相关的图片放入对应的位置,同时 OpenCV提供的特征检测和匹配功能也可以帮助找到原始图像中的对应特征点,即素材图片的覆盖区域,两者结合就可以快速的完成图像的替换,具体的实现原理如下:
首先与图片进行交互,通过鼠标在图片上的双击操作获取对应的坐标点,依次按照左上、左下、右下、右上的顺序点击我们需要覆盖的区域范围,获取到对应的坐标并记录到数据表格中。
随后,读取底图和替换素材文件然后根据用户交互获取的区域位置信息,计算透视变换矩阵,并使用该矩阵将要替换的图片进行透视变换,使其与原始图片中的区域位置相匹配,再使用多边形填充的方法将嵌入区域的像素值全部置成0,然后将变换后的图片插入到对应区域的位置,完成素材的替换,最后保存替换后的图片到指定路径,整一个快速替换的操作就完成了。我们来看一下具体的实现。
交互操作函数具体逻辑:
1.读取传入的底图信息
2.定义回调函数,响应鼠标的点击事件,记录对应的坐标并显示在图像上
3.记录键盘事件,当用户按到q键的时候结束整个操作,关闭图像展示窗口并返回坐标信息
def adjust_pic(original_pic):
square_list = []
# 读取底图信息
img = cv2.imread(original_pic)
def mouse_event(event,x,y,flags,param):
location_list = []
# 双击左键显示图像的坐标
if event == cv2.EVENT_LBUTTONDBLCLK:
print('坐标与像素值的对应关系-(', x, ',', y, '):', img[y, x]) # 坐标(x,y)对应的像素值应该是img[y,x]
location_list.append(x)
location_list.append(y)
square_list.append(location_list)
# 拼接坐标信息并在图像上显示出来
text = '(' + str(x) + ',' + str(y) + ')' + str(img[y,x])
cv2.putText(img,text,(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,0,0),1)
cv2.namedWindow('image',cv2.WINDOW_NORMAL) # 创建一个窗口
cv2.setMouseCallback('image',mouse_event) # 设置鼠标回调
# 展示一下,按下q键退出操作
while True:
cv2.imshow('image',img)
if cv2.waitKey(1)==ord('q'):
break
cv2.destroyAllWindows()
return square_list交互函数具体效果展示:

替换函数具体逻辑:
1.读取对应替换的和底图,获取到图片的形状参数
2.将嵌入区域的位置坐标转换成numpy的数组
3.根据坐标数组计算变换矩阵并完成素材图变换,将底图嵌入区域的像素都变成0之后调整形状并填入变换后的素材图
4.保存图片到对应的路径下
def exchange_pic(square_list, original_pic, pic, save_path):
# 读取要替换的图片与底图,并获取对应图片的参数
img1 = cv2.imread(pic)
img2 = cv2.imread(original_pic)
h1,w1,c1 = img1.shape
h2,w2,c2 = img2.shape
# 嵌入的原图的四角坐标(img1) 逆时针坐标
src = np.float32([[0,0],[0,h1-1],[w1-1,h1-1],[w1-1,0]])
# 待嵌入图片区域的位置坐标(img2)
dst = np.float32(square_list)
M = cv2.getPerspectiveTransform(src,dst) # 透视变换矩阵
# 进行透视变换
new_img1 = cv2.warpPerspective(img1,M,(w2,h2))
# 调整嵌入图片的形状,并将嵌入区域的像素填成0
dst = dst.astype(int)
cv2.fillConvexPoly(img2,dst,[0,0,0])
new_img = cv2.add(img2,new_img1) # 把变换后的图片覆盖到对应区域
# 展示一下
# cv2.imshow('new_img',new_img)
# cv2.waitKey(0)
# 保存图像
cv2.imwrite(save_path, new_img)
cv2.destroyAllWindows()在影刀中的实现逻辑:
1.新建模块将上述代码写入
2.当用户选择手动选区的时候,调用交互函数跳出弹窗让用户在图像中选择对应区域,随后将区域坐标记录到数据表格中;当客户选择上一次记录的时候,从数据表格中调取最近一次的记录作为本次替换的坐标
3.将获取到的坐标信息与具体的图片信息传入替换函数中完成图像的覆盖操作

在示例中,test_pic为我们的底图,intence_pic_1与intence_pic_2为替换素材,简单演示一下,底图如下,右侧是更换之后的结果。


需要注意在选取位置时要按照逆时针的方向选择区域(左上、左下、右下、右上)。