栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
  • 前言
  • 一、卡帧
  • 二、竖向开幕/横向开幕
  • 三、渐隐/渐显
  • 四、推近/拉远
  • 五、方形开幕
  • 六、灰度渐变

前言

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

当然,这里并没有生成gif图片,只是通过imshow和waitKey来展示效果,不过基本原理能实现,找个库再生成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%,非线性减少(越来越慢) 在3秒内,50%→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%​(3−t)n(n≥2)其中,{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.5−y)⋅height其中,pt表示在t时刻,需要从中间向上方加载的高度​

这边取五次函数( 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(n≥1)渐显:y=100%−3n100%​(3−t)n(n≥1)其中,{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%​(3−t)n(n≥2)拉远:y=3n33%​(3−t)n(n≥2)其中,{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δ%​⋅(time−t)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} ​如果mode为slower,交换a和b,令 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()
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/283251.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号