前言
上一篇 我们讨论了单张图片的特效实现,这一篇我们讨论两张图片的转场实现
以下是练习时用到的两张图片素材(当然,只是简单练习嘛,图像尺寸都是一样的,还都是正方形)
![]() 001.jpg |
![]() 002.jpg |
一、闪黑
效果对比
![]() 美图秀秀【闪黑】 |
![]() OpenCV实现【闪黑】 |
实现思路
思路很简单,就是把上一篇的渐隐和渐显组合起来使用就行
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''特效展示'''
load_f = 20
tim = 0.2
percent_func1 = percent_func_gen(a=1, b=0, time=tim, n=1, mode="null")
percent_func2 = percent_func_gen(a=0, b=1, time=tim, n=1, mode="null")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func1(t * load_f / 1000)
img_show = cv2.multiply(img1, (1, 1, 1, 1), scale=percent)
cv2.imshow("show", img_show)
cv2.waitKey(load_f)
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func2(t * load_f / 1000)
img_show = cv2.multiply(img2, (1, 1, 1, 1), scale=percent)
cv2.imshow("show", img_show)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()
二、上移/下移/左移/右移
这里以左移为例
效果对比
![]() 美图秀秀【左移】 |
![]() OpenCV实现【左移】 |
实现思路
思路也很简单,左右拼接好形成一张图,然后利用仿射变换平移、在有限大小的画布内展示即可
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''画布准备'''
img = np.hstack([img1, img2])
'''特效展示'''
load_f = 20
tim = 0.3
percent_func = percent_func_gen(a=0, b=1, time=tim, n=2, mode="faster")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func(t * load_f / 1000)
x = int(percent * cols)
M = np.float32([[1, 0, -x], [0, 1, 0]])
res = cv2.warpAffine(img, M, (rows, cols))
cv2.imshow("show", res)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()
三、向上擦除/向下擦除/向左擦除/向右擦除
这里以向下擦除为例
效果对比
![]() 美图秀秀【向下擦除】 |
![]() OpenCV实现【向下擦除】 |
实现思路
以第一张图为背景,将第二张图的局部不断覆盖
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''特效展示'''
load_f = 20
tim = 0.3
percent_func = percent_func_gen(a=0, b=1, time=tim, n=1, mode="null")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func(t * load_f / 1000)
height = int(percent * rows)
img1[:height, :] = img2[:height, :]
cv2.imshow("show", img1)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()
四、横向拉幕/竖向拉幕
这里以横向拉幕为例
效果对比
![]() 美图秀秀【横向拉幕】 |
![]() OpenCV实现【横向拉幕】 |
实现思路
思路很简单,就是把上一篇的横向开幕的画布换成第二张图片即可
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''特效展示'''
load_f = 20
tim = 0.3
half = int(rows / 2)
percent_func = percent_func_gen(a=0, b=0.5, time=tim, n=1, mode="null")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func(t * load_f / 1000)
width = int(percent * rows)
ys, ye = half - width, half + width
img1[:, ys:ye] = img2[:, ys:ye]
cv2.imshow("show", img1)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()
五、旋转
效果对比
![]() 美图秀秀【旋转】 |
![]() OpenCV实现【旋转】 |
实现思路
拼接好左上、右上、左下、右下四张图作为一张图,利用仿射变换的旋转实现效果
对图一: { 左上 原图 右上 左右翻转 左下 上下翻转 右下 上下左右翻转 对图一: \begin{cases} 左上 & 原图 \\ 右上 & 左右翻转 \\ 左下 & 上下翻转 \\ 右下 & 上下左右翻转 \\ \end{cases} 对图一:⎩ ⎨ ⎧左上右上左下右下原图左右翻转上下翻转上下左右翻转
对图二: { 左上 左右翻转 右上 原图 左下 上下左右翻转 右下 上下翻转 对图二: \begin{cases} 左上 & 左右翻转 \\ 右上 & 原图 \\ 左下 & 上下左右翻转 \\ 右下 & 上下翻转 \\ \end{cases} 对图二:⎩ ⎨ ⎧左上右上左下右下左右翻转原图上下左右翻转上下翻转
要想和美图秀秀做到很相近的效果,需要能够确定好旋转中心,这一点有点难,暂定图像中心为旋转中心
同时,为了避免旋转过程中边角出现黑边,将图像再次扩充为 3 × 3 3 \times 3 3×3 倍
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''画布准备'''
img1_ru = cv2.flip(img1, 1)
img1_ld = cv2.flip(img1, 0)
img1_rd = cv2.flip(img1, -1)
img1_u = np.hstack([img1, img1_ru])
img1_d = np.hstack([img1_ld, img1_rd])
img1_res_tmp = np.vstack([img1_u, img1_d])
img1_res_tmp = np.hstack([img1_res_tmp] * 3)
img1_res = np.vstack([img1_res_tmp] * 3)
img2_lu = cv2.flip(img2, 1)
img2_rd = cv2.flip(img2, 0)
img2_ld = cv2.flip(img2, -1)
img2_u = np.hstack([img2_lu, img2])
img2_d = np.hstack([img2_ld, img2_rd])
img2_res_tmp = np.vstack([img2_u, img2_d])
img2_res_tmp = np.hstack([img2_res_tmp] * 3)
img2_res = np.vstack([img2_res_tmp] * 3)
res_rows, res_cols = img1_res.shape[:2]
'''特效展示'''
load_f = 20
tim = 0.2
angle_all = 150
point1 = (rows * 3, cols * 3)
point2 = (rows * 3, cols * 4)
percent_func1 = percent_func_gen(a=0, b=1, time=tim, n=4, mode="faster")
percent_func2 = percent_func_gen(a=1, b=0, time=tim, n=4, mode="slower")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func1(t * load_f / 1000)
angle = percent * angle_all
M1 = cv2.getRotationMatrix2D(point1, angle, 1)
res = cv2.warpAffine(img1_res, M1, (res_rows, res_cols))
M2 = np.float32([[1, 0, -cols * 2], [0, 1, -rows * 2]])
res = cv2.warpAffine(res, M2, (rows, cols))
cv2.imshow("show", res)
cv2.waitKey(load_f)
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func2(t * load_f / 1000)
angle = -percent * angle_all
M2 = cv2.getRotationMatrix2D(point2, angle, 1)
res = cv2.warpAffine(img2_res, M2, (res_rows, res_cols))
M2 = np.float32([[1, 0, -cols * 3], [0, 1, -rows * 2]])
res = cv2.warpAffine(res, M2, (rows, cols))
cv2.imshow("show", res)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()
六、上下切割/左右切割
这里以左右切割为例
效果对比
![]() 美图秀秀【左右切割】 |
![]() OpenCV实现【左右切割】 |
实现思路
上下两个部分分别使用仿射变换进行不同的平移操作
实现代码
import cv2
import numpy as np
def percent_func_gen(a, b, time, n, mode):
"""
高次多项式计算函数生成器
:param a: 起始百分比(如:0.25)
:param b: 结束百分比
:param time: 动画持续时间
:param n: 多项式次数
:param mode: faster(越来越快)、slower(越来越慢)
:return: 每个时刻到达百分比的计算函数
"""
if mode == "slower":
a, b = b, a
delta = abs(a - b)
sgn = 1 if b - a > 0 else (-1 if b - a < 0 else 0)
def percent_calc(ti):
if mode == "slower":
ti = time - ti
return sgn * delta / (time ** n) * (ti ** n) + a
return percent_calc
'''读入图像'''
img1 = cv2.imread("001.jpg")
img2 = cv2.imread("002.jpg")
rows, cols = img1.shape[:2]
'''画布准备'''
img_u = np.hstack([img1, img2])
img_d = np.hstack([img2, img1])
'''特效展示'''
load_f = 20
tim = 0.4
percent_func = percent_func_gen(a=0, b=1, time=tim, n=3, mode="slower")
for t in range(int(tim * 1000) // load_f + 1):
percent = percent_func(t * load_f / 1000)
x = int(percent * cols)
M1 = np.float32([[1, 0, -x], [0, 1, 0]])
res1 = cv2.warpAffine(img_u, M1, (rows, cols))[:rows // 2, :]
M2 = np.float32([[1, 0, x - cols], [0, 1, 0]])
res2 = cv2.warpAffine(img_d, M2, (rows, cols))[rows // 2:, :]
res = np.vstack([res1, res2])
cv2.imshow("show", res)
cv2.waitKey(load_f)
'''关闭窗口'''
cv2.waitKey(1500)
cv2.destroyAllWindows()