不用 PS 抠图,Python + OpenCV 实现自动海报场景替换!
现存在一个问题,就下面图片中的两本书而言,怎样快速让中间边的书本与左边书本对齐(最终效果能实现两张图片重叠(最终结果为右图)),进行的图像转变可旋转、平移、缩放、形变。
本文主要内容就是介绍利用 Opencv 来怎样解决上面的问题,解决这个问题需要三步
确定至少四组对应点坐标
找到一个转换矩阵;
把找到的转换矩阵应用到 Moving Image(需要移动的图片) 上,实现图像对齐
图片旋转、平移、缩放等操作的主要目的,就是要最终实现两图像中点对点一一映射关系,图像映射本质上就是像素点转换
图中标记了其中四组对应点,分别标为不同的颜色,分别标为红、橙、黄和绿四种颜色;比如这里的 和
是就是一组对应点,图片经过转换之后 点 必须映射到 点位置。
涉及图片中点坐标变换,都需要借助于 矩阵 运算,这里探究的图像维度都属于二维,坐标只需要 即可
面向此类转换问题,Homography 转换 ( 3 × 3 矩阵) 可用于解决此类转化问题,用来解决点对点映射问题,Homography 矩阵可写作下列方式:
则 、
作为对应点,则 Homography 的的应用 如下:
而矩阵 H 参数的确定至少需要 4 组对应点,因此在计算 H 时至少要找到 4 组对应点;找到的对应点组数越多,计算得到的 H 会越精确,最终的转换效果也就会越好。
下面用 Opencv + Python 来实现上面图片中的书籍的对齐,
import cv2
import numpy as np
if __name__ =='__main__':
#图片读取
img_src = cv2.imread("D:/book2.jpg")
position_src = np.array([[141,131],[480,159],[493,630],[64,601]],dtype = float)
img_dst = cv2.imread("D:/book1.jpg")
position_dst = np.array([[318,256],[543,372],[316,670],[73,473]],dtype = float)
#计算转换矩阵
h,status = cv2.findHomography(position_src,position_dst)
#对图片进行仿射变换
out_img = cv2.warpPerspective(img_src,h,(img_dst.shape[1],img_dst.shape[0]))
#Display images;
cv2.imshow("Source image",img_src)
cv2.imshow("Destination Image",img_dst)
cv2.imshow("Warped Source Image",out_img)
cv2.waitKey(0)
这里事先已经确定好对应的四个点的坐标,然后把这四个点的坐标带入 cv2.findHomography() 计算出转换矩阵,最后把矩阵应用到两图像中,得到最终的转换结果,
这里提醒一点,warpPerspective 函数进行对图像像素进行矩阵变换时,隐藏了一个参数 Interpolator ,默认为线性插值,功能是防止像素点像素值缺失
上面小案例不方便的一点需要确定对应四个点的坐标,这个步骤是比较繁琐的,下面案例将在程序中加入交互功能,实现某个图片的自动标记点收集、标记点点转换:
首先需要准备两张图片,其中一张为海报,一张为需要替换的海报;关于确定点的坐标时,被替换的图片的坐标非常好确定,只需知道图片的长宽即可;
但的海报图像区域四个点是不好确定的, 这里利用 Opencv 的鼠标回调函数,监视鼠标响应,根据用户点击来收集 PIck 得到的坐标;
def mouse_handler(event,x,y,flags,data):
if event ==cv2.EVENT_LBUTTONDOWN:
cv2.circle(data['im'],(x,y),3,(0,0,255),5,16)
cv2.namedWindow("Image",0)
cv2.imshow("Image",data['im'])
if len(data['points']) <4:
data['points'].append([x,y])
def get_four_points(im):
data = {}
data['im'] = im.copy()
data['points'] = []
# Set the callback function for any mouse event
cv2.namedWindow("Image", 0)
cv2.imshow('Image',im)
#请注意你标记点的数据,是顺时针,需要与pst_src 方向一致
cv2.setMouseCallback("Image",mouse_handler,data)
cv2.waitKey(0)
# Convert array to np.array
#竖直方向堆叠起来;;;
points = np.vstack(data['points']).astype(float)
return points
坐标确定以后,接下来就很简单了,跟上个案例一样,计算变换矩阵,矩阵应用到图像旋转,最终更换海报内容也就轻松完成啦
需要注意一点,坐标 Pick 点的顺序须与记录替换图像顶点顺序一致,否则转换图会有偏差,案例完整代码如下:
if __name__ =='__main__':
img_src = cv2.imread("D:/first-image.jpg")
size = img_src.shape
# 取得四个坐标
pst_src = np.array(
[
[0,0],[size[1]-1,0],
[size[1]-1,size[0]-1],
[0,size[0]-1]
],dtype=float
)
#Read the destination image
img_dst = cv2.imread("D:/times-square.jpg")
print("Click on four corners of bllboard and the press ENTER")
four_point = get_four_points(img_dst)
# Calculate Homography between source and destination points
h,status = cv2.findHomography(pst_src,four_point)
im_temp = cv2.warpPerspective(img_src,h,(img_dst.shape[1],img_dst.shape[0]))
cv2.fillConvexPoly(img_dst,four_point.astype(int),0,16)
#add wraped source image to destination image
img_dst = img_dst + im_temp
cv2.namedWindow("Image", 0)
cv2.imshow("Image",img_dst)
cv2.waitKey(0)
推荐阅读:
本文分享自微信公众号 - Z先生点记(gh_683d048a482a)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Docker三剑客之Swarm集群搭建
1. Docker Swarm 简介 Docker Swarm 是 Docker 官方三剑客项目之一,提供 Docker 容器集群服务,是 Docker 官方对容器云生态进行支持的核心方案。 使用它,用户可以将多个 Docker 主机封装为单个大型的虚拟 Docker 主机,快速打造一套容器云平台。 注意:Docker 1.12.0+ Swarm mode 已经内嵌入 Docker 引擎,成为了 docker 子命令 docker swarm,绝大多数用户已经开始使用 Swarm mode,Docker 引擎 API 已经删除 Docker Swarm。为避免大家混淆旧的 Docker Swarm 与新的 Swarm mode,旧的 Docker Swarm 内容已经删除,请查看 Swarm mode 一节。 2. Docker Swarm mode Docker 1.12 Swarm mode 已经内嵌入 Docker 引擎,成为了 docker 子命令 docker swarm。请注意与旧的 Docker Swarm 区分开来。 Swarm mode 内置 kv 存储功能,提供了众...
- 下一篇
Java8实战——通过行为参数化传递代码
1、初试牛刀:筛选绿苹果 第一个解决方案可能是下面这样的: public static List<Apple> filterGreenApples(List<Apple> inventory){ List<Apple> result=new ArrayList<>(); //仅仅筛选出绿苹果 for (Apple apple : inventory) { if ("green".equals(apple.getColor())){ result.add(apple); } } return result; } 上面代码只针对绿苹果进行筛选,现在,我还想筛选出红苹果,该怎么做呢?简单的解决办法就是重复写一个方法,再改条件为红苹果,但是,要筛选的颜色有多种的情况,这样写会导致代码十分冗余,所以我们第一步尝试将其抽象化。 2、再展身手:把颜色作为参数 public static List<Apple> filterGreenApples(List<Apple> inventory,String colo...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS8编译安装MySQL8.0.19
- Hadoop3单机部署,实现最简伪集群