1、准备工作
pip install vacm
2、原理介绍
在OpenCV中,VCAM 库是一个用于简化创建三维曲面、定义虚拟摄像机、设置参数以及进行投影任务的工具。它特别适用于实现如哈哈镜等图像变形效果。
一、VCAM 库的功能
VCAM 库的主要功能包括:
-
创建三维曲面:用户可以根据需要定义一个三维曲面,这个曲面将作为虚拟镜面的基础。
-
定义虚拟摄像机:用户可以设置虚拟摄像机的各种参数,如焦距、位置等,以模拟不同的拍摄效果。
-
投影与重映射:将三维曲面上的点投影到虚拟摄像机的成像平面上,并生成相应的二维点。这些二维点可以用于图像的重映射,从而实现图像的变形效果。
二、使用VCAM库实现哈哈镜效果
-
获取图像并创建网格:首先,获取待处理的图像,并创建一个与图像大小相同的网格。这个网格将用于表示三维曲面上的点。
-
定义三维曲面:通过修改网格中每个点的Z坐标值,来定义一个凹凸不平的三维曲面。这可以通过各种数学函数来实现,如正弦函数、余弦函数等。
-
创建虚拟摄像机并投影:使用VCAM库创建一个虚拟摄像机,并将定义好的三维曲面投影到虚拟摄像机的成像平面上。这一步将生成对应的二维点集。
-
图像重映射:根据投影得到的二维点集,使用OpenCV的remap函数对原始图像进行重映射。重映射的过程将根据二维点集的位置关系,将原始图像中的每个像素点映射到新的位置,从而实现图像的变形效果。
三、代码示例
以下是一个使用Python和VCAM库实现哈哈镜效果的示例代码:
import cv2
import numpy as np
from vcam import vcam, meshGen
# 读取图像
img = cv2.imread('your_image.jpg')
H, W = img.shape[:2]
# 创建虚拟摄像机
c1 = vcam(H=H, W=W)
# 创建网格
plane = meshGen(H, W)
# 定义三维曲面(例如,使用正弦函数来创建波浪形的曲面)
plane.Z = 10 * np.sin((plane.X / plane.W) * 2 * np.pi * 10)
# 获取三维曲面上的点
pts3d = plane.getPlane()
# 将三维点投影到二维图像坐标
pts2d = c1.project(pts3d)
# 构建映射函数
map_x, map_y = c1.getMaps(pts2d)
# 应用映射函数进行图像重映射
output = cv2.remap(img, map_x, map_y, interpolation=cv2.INTER_LINEAR)
# 显示结果
cv2.imshow('Funny Mirror', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
四、注意事项
-
安装VCAM库:在使用VCAM库之前,需要确保已经正确安装了该库。如果使用的是Python,可以通过pip等包管理工具进行安装。
-
调整参数:在使用VCAM库时,可以通过调整虚拟摄像机的参数、三维曲面的定义等来实现不同的变形效果。用户可以根据需要进行多次尝试和调整。
-
性能考虑:对于较大的图像或复杂的变形效果,图像重映射的过程可能会比较耗时。因此,在实际应用中需要考虑性能问题,并采取相应的优化措施。
综上所述,OpenCV的VCAM库是一个功能强大的工具,可以用于实现各种有趣的图像变形效果。通过合理使用该库,用户可以轻松地创建出具有创意和趣味性的图像作品。
3、代码实现
# FunnyMirrorsImages.py
import cv2
import numpy as np
import math
from vcam import vcam, meshGen
paths = ["./chess.jpeg", "./1.jpg", "./2.jpg"]
for mode in range(8):
for i, path in enumerate(paths):
# 读取输入图像
img = cv2.imread(path)
img = cv2.resize(img, (300, 300))
H, W = img.shape[:2]
# 创建虚拟相机对象
c1 = vcam(H=H, W=W)
# 创建表面对象
plane = meshGen(H, W)
# 我们生成一面镜子,其中对于每个 3D 点,其 Z 坐标定义为 Z = F(X,Y)
if mode == 0:
plane.Z += 20 * np.exp(-0.5 * ((plane.X * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
elif mode == 1:
plane.Z += 20 * np.exp(-0.5 * ((plane.Y * 1.0 / plane.H) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
elif mode == 2:
plane.Z -= 10 * np.exp(-0.5 * ((plane.X * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
elif mode == 3:
plane.Z -= 10 * np.exp(-0.5 * ((plane.Y * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
elif mode == 4:
plane.Z += 20 * np.sin(2 * np.pi * ((plane.X - plane.W / 4.0) / plane.W)) + 20 * np.sin(
2 * np.pi * ((plane.Y - plane.H / 4.0) / plane.H))
elif mode == 5:
plane.Z -= 20 * np.sin(2 * np.pi * ((plane.X - plane.W / 4.0) / plane.W)) - 20 * np.sin(
2 * np.pi * ((plane.Y - plane.H / 4.0) / plane.H))
elif mode == 6:
plane.Z += 100 * np.sqrt((plane.X * 1.0 / plane.W) ** 2 + (plane.Y * 1.0 / plane.H) ** 2)
elif mode == 7:
plane.Z -= 100 * np.sqrt((plane.X * 1.0 / plane.W) ** 2 + (plane.Y * 1.0 / plane.H) ** 2)
else:
print("Wrong mode selected")
exit(-1)
# 提取生成的 3D 平面
pts3d = plane.getPlane()
# 在虚拟相机中投影(捕捉)平面
pts2d = c1.project(pts3d)
# 为基于网格的扭曲生成映射函数。
map_x, map_y = c1.getMaps(pts2d)
# 生成输出
output = cv2.remap(img, map_x, map_y, interpolation=cv2.INTER_LINEAR)
output = cv2.flip(output, 1)
cv2.imshow("Funny Mirror", output)
cv2.imshow("Input and output", np.hstack((img, np.zeros((H, 2, 3), dtype=np.uint8), output)))
# 取消注释以下行以保存输出
# cv2.imwrite("Mirror-effect-%d-image-%d.jpg"%(mode+1,i+1),np.hstack((img,np.zeros((H,2,3),dtype=np.uint8),output)))
cv2.waitKey(0)
4、效果展示
输入图片
plane.Z += 20 * np.exp(-0.5 * ((plane.X * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
plane.Z += 20 * np.exp(-0.5 * ((plane.Y * 1.0 / plane.H) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
plane.Z -= 10 * np.exp(-0.5 * ((plane.X * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
plane.Z -= 10 * np.exp(-0.5 * ((plane.Y * 1.0 / plane.W) / 0.1) ** 2) / (0.1 * np.sqrt(2 * np.pi))
plane.Z += 20 * np.sin(2 * np.pi * ((plane.X - plane.W / 4.0) / plane.W)) + 20 * np.sin(
2 * np.pi * ((plane.Y - plane.H / 4.0) / plane.H))
plane.Z -= 20 * np.sin(2 * np.pi * ((plane.X - plane.W / 4.0) / plane.W)) - 20 * np.sin(
2 * np.pi * ((plane.Y - plane.H / 4.0) / plane.H))
plane.Z += 100 * np.sqrt((plane.X * 1.0 / plane.W) ** 2 + (plane.Y * 1.0 / plane.H) ** 2)
plane.Z -= 100 * np.sqrt((plane.X * 1.0 / plane.W) ** 2 + (plane.Y * 1.0 / plane.H) ** 2)
5、参考
- https://github.com/kaustubh-sadekar/FunMirrors
- https://github.com/kaustubh-sadekar/VirtualCam
- OpenCV进阶(11)使用 OpenCV实现哈哈镜