Python-OpenCV 实现美图秀秀视频剪辑效果【特效】

前言

最近学了一点简单的图像几何变换,想找点什么东西练练手,于是想起了手机上的美图秀秀,找点特效来仿制仿制😜

当然,这里并没有生成gif图片,只是通过imshowwaitKey来展示效果,不过基本原理能实现,找个库再生成gif想必也不难吧

为了避免篇幅过长,本篇只讨论单张图片的特效实现(下一篇讨论两张图片的转场实现)

以下是练习时用到的两张图片素材


001.jpg

002.jpg

一、卡帧

效果对比


美图秀秀【卡帧】

OpenCV实现【卡帧】

实现思路

简单的定时贴图

实现代码

import cv2
import numpy as np

'''读入图像'''
img = cv2.imread("001.jpg")
rows, cols = img.shape[:2]

'''缩放原图80%'''
img = cv2.resize(img, (int(rows * 0.8), int(cols * 0.8)))
rows_new, cols_new = img.shape[:2]

'''准备更大的画布'''
cav_np = np.zeros((rows, cols), np.uint8)
cav = cv2.cvtColor(cav_np, cv2.COLOR_GRAY2BGR)

'''偏移量准备'''
dic = {
    0: (int(cols * 0.08), int(rows * 0.12)),
    1: (int(cols * 0.12), int(rows * 0.08)),
    2: (int(cols * 0.12), int(rows * 0.06)),
    3: (int(cols * 0.16), int(rows * 0.07)),
    4: (int(cols * 0.14), int(rows * 0.10)),
    5: (int(cols * 0.13), int(rows * 0.05)),
    6: (int(cols * 0.14), int(rows * 0.08)),
    7: (int(cols * 0.15), int(rows * 0.10))
}

'''特效展示'''
for i in range(8):
    xs, ys = dic[i]
    xe, ye = (xs + rows_new, ys + cols_new)
    cav[xs: xe, ys: ye] = img
    cv2.imshow("show", cav)
    cv2.waitKey(500)

'''关闭窗口'''
cv2.waitKey(1000)
cv2.destroyAllWindows()

二、竖向开幕/横向开幕

效果对比


美图秀秀【竖向开幕】

OpenCV实现【竖向开幕】

美图秀秀【横向开幕】

OpenCV实现【横向开幕】

实现思路

准备黑色画布,控制好时间向画布上贴图,要控制好速度越来越慢即加载量越来越少
要求如下: 在 3 秒 内 , 50 % → 0 % , 非 线 性 减 少 ( 越 来 越 慢 ) 在3秒内,50\% → 0\%,非线性减少(越来越慢) 350%0%线
可以设置简单的多项式函数 y = 50 % 3 n ( 3 − t ) n ( n ≥ 2 ) 其 中 , { t 时 间 ( 单 位 : 秒 ) y 从 中 间 位 置 向 上 , 未 加 载 部 分 的 高 度 百 分 比 \begin{aligned} & y = \frac{50\%}{3^n}(3-t)^n \quad (n \geq 2)\\ \\ & 其中, \begin{cases} t & 时间(单位:秒)\\ y & 从中间位置向上,未加载部分的高度百分比 \end{cases} \end{aligned} y=3n50%(3t)n(n2){ty
那么就可以知道 pt = ( 0.5 − y ) ⋅ height 其 中 , pt 表 示 在 t 时 刻 , 需 要 从 中 间 向 上 方 加 载 的 高 度 \begin{aligned} & \text{pt} = (0.5 - y) \cdot \text{height} \\ \\ & 其中,\text{pt}表示在t时刻,需要从中间向上方加载的高度 \end{aligned} pt=(0.5y)heightptt

这边取五次函数( n = 5 n = 5 n=5)、每 30 30 30 毫秒加载一次

Tips:经过测试, n ‾ \bm{\underline{n}} n 取值越大,动画展示越快(越早加载完整张图片)

实现代码

  • 竖向开幕
    import cv2
    import numpy as np
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''画布准备'''
    cav_np = np.zeros((rows, cols), np.uint8)
    cav = cv2.cvtColor(cav_np, cv2.COLOR_GRAY2BGR)
    
    '''特效展示'''
    load_f = 30
    n = 5
    half = int(rows / 2)
    xs1, xs2 = half, half
    xe1, xe2 = half, half
    for t in range(3000 // load_f):
        load_percent = 0.5 / (3 ** n) * ((3 - t * load_f / 1000) ** n)
        load_height = int((0.5 - load_percent) * rows)
        xe1, xe2 = half - load_height, half + load_height
        cav[xe1:xs1, :] = img[xe1:xs1, :]
        cav[xs2:xe2, :] = img[xs2:xe2, :]
        xs1, xs2 = xe1, xe2
        cv2.imshow("show", cav)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(1000)
    cv2.destroyAllWindows()
    
  • 横向开幕
    import cv2
    import numpy as np
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''画布准备'''
    cav_np = np.zeros((rows, cols), np.uint8)
    cav = cv2.cvtColor(cav_np, cv2.COLOR_GRAY2BGR)
    
    '''特效展示'''
    load_f = 30
    n = 5
    half = int(rows / 2)
    ys1, ys2 = half, half
    ye1, ye2 = half, half
    for t in range(3000 // load_f):
        load_percent = 0.5 / (3 ** n) * ((3 - t * load_f / 1000) ** n)
        load_height = int((0.5 - load_percent) * rows)
        ye1, ye2 = half - load_height, half + load_height
        cav[:, ye1:ys1] = img[:, ye1:ys1]
        cav[:, ys2:ye2] = img[:, ys2:ye2]
        ys1, ys2 = ye1, ye2
        cv2.imshow("show", cav)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(1000)
    cv2.destroyAllWindows()
    

三、渐隐/渐显

效果对比


美图秀秀【渐隐】

OpenCV实现【渐隐】

美图秀秀【渐显】

OpenCV实现【渐显】

实现思路

准备图像,控制好时间改变图像强度(亮度),要控制好速度越来越快
要求如下: 渐 隐 : 100 % → 0 % , 减 少 渐 显 : 0 % → 100 % , 增 长 \begin{aligned} & 渐隐:100\% → 0\%,减少 \\ & 渐显:0\% → 100\%,增长 \end{aligned} 100%0%0%100%
可以设置简单的多项式函数 渐 隐 : y = 100 % − 100 % 3 n t n ( n ≥ 1 ) 渐 显 : y = 100 % − 100 % 3 n ( 3 − t ) n ( n ≥ 1 ) 其 中 , { t 时 间 ( 单 位 : 秒 ) y 亮 度 百 分 比 \begin{aligned} & 渐隐:y = 100\%-\frac{100\%}{3^n}t^n \quad (n \geq 1)\\ \\ & 渐显:y = 100\%-\frac{100\%}{3^n}(3-t)^n \quad (n \geq 1)\\ \\ & 其中, \begin{cases} t & 时间(单位:秒)\\ y & 亮度百分比 \end{cases} \end{aligned} y=100%3n100%tn(n1)y=100%3n100%(3t)n(n1){ty
这边渐隐取 n = 1 n = 1 n=1、渐显取 n = 1.5 n = 1.5 n=1.5、总时间 2 2 2 秒、每 40 40 40 毫秒加载一次

Tips:经过测试, n ‾ \bm{\underline{n}} n 取值越大,动画展示越快(因此,可以将动画的起点时间设置在40%的时间左右,跳过高次多项式的慢速增长阶段)

实现代码

  • 渐隐
    import cv2
    import numpy as np
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''特效展示'''
    load_f = 40
    n = 1
    time = 2
    for t in range(time * 1000 // load_f):
        sc = 1 - 1 / (time ** n) * (t * load_f / 1000) ** n
        img_show = cv2.multiply(img, (1, 1, 1, 1), scale=sc)
        cv2.imshow("show", img_show)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(500)
    cv2.destroyAllWindows()
    
  • 渐显
    import cv2
    import numpy as np
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''特效展示'''
    load_f = 40
    n = 1.5
    time = 2
    for t in range(time * 1000 // load_f):
        sc = 1 - 1 / (time ** n) * (time - t * load_f / 1000) ** n
        img_show = cv2.multiply(img, (1, 1, 1, 1), scale=sc)
        cv2.imshow("show", img_show)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(500)
    cv2.destroyAllWindows()
    

四、推近/拉远

效果对比


美图秀秀【推近】

OpenCV实现【推近】

美图秀秀【拉远】

OpenCV实现【拉远】

实现思路

准备图像,控制好时间展示resize后的图像局部,速度越来越慢
要求如下: 左 侧 推 近 长 度 : 0 % → 20 % , 非 线 性 增 长 ( 越 来 越 慢 ) 左 侧 拉 远 长 度 : 20 % → 0 % , 非 线 性 减 少 ( 越 来 越 慢 ) \begin{aligned} & 左侧推近长度:0\% → 20\%,非线性增长(越来越慢) \\ & 左侧拉远长度:20\% → 0\%,非线性减少(越来越慢) \\ \end{aligned} 0%20%线20%0%线
可以设置简单的多项式函数 推 近 : y = 33 % − 33 % 3 n ( 3 − t ) n ( n ≥ 2 ) 拉 远 : y = 33 % 3 n ( 3 − t ) n ( n ≥ 2 ) 其 中 , { t 时 间 ( 单 位 : 秒 ) y 左 侧 推 近 / 拉 远 百 分 比 \begin{aligned} & 推近:y = 33\%-\frac{33\%}{3^n}(3-t)^n \quad (n \geq 2)\\ \\ & 拉远:y = \frac{33\%}{3^n}(3 - t)^n \quad (n \geq 2)\\ \\ & 其中, \begin{cases} t & 时间(单位:秒)\\ y & 左侧推近/拉远百分比 \end{cases} \end{aligned} y=33%3n33%(3t)n(n2)y=3n33%(3t)n(n2){ty/
如此繁琐地自己去寻找这样的函数关系有点麻烦,因此我们需要定义一个函数,给定 a % → b % mode = { faster 越来越快 slower 越来越慢 \color{#AA66FF} \begin{aligned} & \bm{a\% → b\%} \\ \\ & \textbf{mode} = \begin{cases} \textbf{faster} & \textbf{越来越快} \\ \\ \textbf{slower} & \textbf{越来越慢} \\ \end{cases} \end{aligned} a%b%mode=fasterslower越来越快越来越慢
可以生成一个根据当前时间 t \bm{t} t 计算当前到达百分比 c % \bm{c\%} c% 的函数

草稿纸上简单归纳计算,得到公式如下:
y = sgn ( a % − b % ) ⋅ δ % ( time ) n ⋅ ( time − t ) n + b % mode = slower y = sgn ( b % − a % ) ⋅ δ % ( time ) n ⋅ t n + a % mode = faster 其中, δ % = ∣    a % − b %    ∣ \color{#AA66FF} \begin{aligned} & \bm{y =} \textbf{sgn}\bm{(a \% - b \%) \cdot \frac{\delta \%}{(\textbf{time})^n} \cdot (\textbf{time} - t)^n + b \%} & \textbf{mode} = \textbf{slower} \\ \\ & \bm{y =} \textbf{sgn}\bm{(b \% - a \%) \cdot \frac{\delta \%}{(\textbf{time})^n} \cdot t^n + a \%} & \textbf{mode} = \textbf{faster} \\ \\ & \textbf{其中,} \bm{\delta\% = | \; a\% - b\% \; |} \end{aligned} y=sgn(a%b%)(time)nδ%(timet)n+b%y=sgn(b%a%)(time)nδ%tn+a%其中,δ%=a%b%mode=slowermode=faster

再以 faster \textbf{faster} faster为基准,高度概括一下,即为
如果mode为slower,交换 a 和 b ,令      t   =   time   -   t y = sgn ( b % − a % ) ⋅ δ % ( time ) n ⋅ t n + a % \color{#AA66FF} \begin{aligned} & \textbf{如果\text{mode}为\text{slower},交换}\bm{a}\textbf{和}\bm{b}\textbf{,令 \; t = \text{time} - t} \\ \\ & \bm{y =} \textbf{sgn}\bm{(b \% - a \%) \cdot \frac{\delta \%}{(\textbf{time})^n} \cdot t^n + a \%} \\ \end{aligned} 如果modeslower,交换ab,令 t = time - ty=sgn(b%a%)(time)nδ%tn+a%

以下为百分比多项式计算函数生成器的代码:

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

这边推近取 n = 4 n = 4 n=4、拉近取 n = 3 n = 3 n=3、总时间 2 2 2 秒,每 40 40 40 毫秒加载一次

实现代码

  • 推近
    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
    
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''特效展示'''
    load_f = 40
    tim = 2
    percent_func = percent_func_gen(a=0, b=0.2, time=tim, n=4, mode="slower")
    for t in range(tim * 1000 // load_f + 1):
        percent = percent_func(t * load_f / 1000)
        xs, xe = int(percent * rows), int((1 - percent) * rows)
        ys, ye = int(percent * cols), int((1 - percent) * cols)
        img_show = img[xs:xe, ys:ye]
        img_show = cv2.resize(img_show, (rows, cols))
        cv2.imshow("show", img_show)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(500)
    cv2.destroyAllWindows()
    
  • 拉远
    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
    
    
    '''读入图像'''
    img = cv2.imread("001.jpg")
    rows, cols = img.shape[:2]
    
    '''特效展示'''
    load_f = 40
    tim = 2
    percent_func = percent_func_gen(a=0.2, b=0, time=tim, n=3, mode="slower")
    for t in range(tim * 1000 // load_f + 1):
        percent = percent_func(t * load_f / 1000)
        xs, xe = int(percent * rows), int((1 - percent) * rows)
        ys, ye = int(percent * cols), int((1 - percent) * cols)
        img_show = img[xs:xe, ys:ye]
        img_show = cv2.resize(img_show, (rows, cols))
        cv2.imshow("show", img_show)
        cv2.waitKey(load_f)
    
    '''关闭窗口'''
    cv2.waitKey(500)
    cv2.destroyAllWindows()
    

五、方形开幕

效果对比


美图秀秀【方形开幕】

OpenCV实现【方形开幕】

实现思路

准备画布,控制好时间展示相应大小的图像局部,速度越来越快
要求如下: 局 部 正 方 形 的 半 宽 度 : 0 % → 50 % , 非 线 性 增 长 ( 越 来 越 快 ) \begin{aligned} & 局部正方形的半宽度:0\% → 50\%,非线性增长(越来越快) \\ \end{aligned} 0%50%线

这边取 n = 5 n = 5 n=5、总时间 1 1 1 秒,每 10 10 10 毫秒加载一次

实现代码

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


'''读入图像'''
img = cv2.imread("001.jpg")
rows, cols = img.shape[:2]

'''画布准备'''
cav_np = np.zeros((rows, cols), np.uint8)
cav = cv2.cvtColor(cav_np, cv2.COLOR_GRAY2BGR)

'''特效展示'''
load_f = 30
tim = 3
percent_func = percent_func_gen(a=0, b=0.5, time=tim, n=5, mode="faster")
rows_half = rows // 2
cols_half = cols // 2
for t in range(tim * 1000 // load_f + 1):
    percent = percent_func(t * load_f / 1000)
    width, height = int(percent * rows), int(percent * cols)
    xs, xe = rows_half-width, rows_half+width
    ys, ye = cols_half-height, cols_half+height
    cav[xs:xe, ys:ye] = img[xs:xe, ys:ye]
    cv2.imshow("show", cav)
    cv2.waitKey(load_f)

'''关闭窗口'''
cv2.waitKey(500)
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


'''读入图像'''
img = cv2.imread("001.jpg")
rows, cols = img.shape[:2]

'''灰度准备'''
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray_bgr = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)

'''特效展示'''
load_f = 20
tim = 1
percent_func = percent_func_gen(a=0, b=1, time=tim, n=1, mode="slower")
ys, ye = 0, 0
for t in range(tim * 1000 // load_f + 1):
    percent = percent_func(t * load_f / 1000)
    width = int(percent * cols)
    ye = width
    img_gray_bgr[:, ys:ye] = img[:, ys:ye]
    ys = ye
    cv2.imshow("show", img_gray_bgr)
    cv2.waitKey(load_f)

'''关闭窗口'''
cv2.waitKey(500)
cv2.destroyAllWindows()
  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

God-Excious

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值