用Python实现将滑动屏幕保存的录屏视频自动拼接为长截图

一、需求背景

如果你有一台苹果手机,想要截取超过一个屏幕高度的长截图,需要多次截屏之后,再使用另外一个APP,才能将多张图像拼接成一整张长截图

「Picsew」长截屏软件:1
长截屏软件

如果你用的是安卓手机,可以使用系统自带的长截图功能。

但是有的APP不支持长截屏怎么办?有的画面比长截图能截取的范围还要长怎么办?长截图滚动的速度太慢了,还有更快的办法吗?

我就遇到了这样的场景,我希望获取微信步数排行榜单里的数据,但是没有找到抓包的接口,所以只能先截屏、然后再做图像识别

但是微信步数排行的榜单实在太长了,截取完成一次要截好几屏,并且长截屏的速度真的太慢了。

于是我想到一个方法,先录制屏幕滚动的视频,然后再拼图长截屏,会不会更快一点呢?

二、录屏视频

需要注意以最高的画质和帧率屏幕录像,录屏画质越高,之后拼图的画质自然就越高。

并且录屏滚动的速度可以慢一点,避免两帧之间的画面移动的距离太远,为后面的拼图带来困难。

三、设计思路

计算每两帧图像之间的偏移量,是能够合成长截图的基础。而能够找到两张图像的偏移量,首先要找到一种方法评估两张图像的重合度

所以3.4节虽然是程序运行的第4个步骤,但却我首先尝试解决的问题,我首先介绍这个环节:

3.4 计算重合度

将视频中的每一帧图像读取为矩阵,将两帧图像的矩阵偏移后相减,如果得到的值全部为,那么这个偏移量就是这两帧图像偏移的距离

但是事与愿违,视频中的画质是有损失的。图像重叠区域的像素并不精确相等,只是近似一致。所以需要找到一种评估两个区域像素相似程度的指标。

将重叠区域的像素数值相减,然后再取绝对值的平均值,作为评估相似程度的依据,数值越小则说明相似程度越高

def overlay(d1, d2, d):
    '''计算重叠部分差异绝对值的平均值'''
    L1 = d1.shape[0]
    L2 = d2.shape[0]
    dd1 = d1[max(0,  d):min(L1, L2+d)]
    dd2 = d2[max(-d, 0):min(L1-d, L2)]
    return np.abs(- 1 * dd2 + dd1).mean() # safe plus

3.3 计算偏移量

将两张图像从上到下滑动匹配一遍,找到重合度最高、也就是差值绝对值的平均值最小的那一次,认为是两帧图像的相对偏移量:

def offset(img1, img2):
    '''计算两张图片的平移偏移量'''
    height = img1.shape[0]
    avg = np.inf
    for h in range(- 100, height // 2): # 搜寻范围
        avg1 = overlay(img1, img2, h)
        if avg > avg1:
            avg = avg1
            dh = h
    return dh, avg

这个方法得到了不错的运行结果,但是我很快就发现了问题——运行效率太低

计算两帧图像的数据需要好几秒钟,视频一秒就可以有60帧,十几秒就可能有上千帧。总时间太长了,还有别的更好的办法吗?

3.5 模板匹配

最近在学习图像识别算法,了解到有一个叫做模板匹配的算法2。Python的图形处理cv2库内建这种算法,参数method有3种可选的匹配模式,本例中设置为了cv2.TM_SQDIFF_NORMED,其他模式经测试运行速度和效果也基本一致。

计算运行720×1306分辨率的图像,速度大约可以达到每秒计算15帧

def match(image, templ, method):
    '''模板匹配图像位置'''
    th, tw = templ.shape[:2]
    result = cv2.matchTemplate(image, templ, method)
    result2 = (result - result.min()) / (result.max() - result.min())
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    return min_loc if method == cv2.TM_SQDIFF_NORMED else max_loc

3.1 读取图像

使用迭代器逐帧读取视频中的图像数据:

def imiter(file, st=None, ed=None):
    '''封装的视频读取迭代器'''
    cap = cv2.VideoCapture(file)
    while cap.isOpened():
        ret, img = cap.read()
        if not ret:
            cap.release()
            return
        yield img[st:ed]

3.2 计算不变区域

屏幕录像中,顶部状态栏和底部导航栏有时会占用一部分高度,但这不是我们需要拼接的内容。我在函数中设置了sted参数,可以手动设定需要裁切的高度

也可以自动测量,逐一读取每行的像素数据,计算每行的方差,找到数值突变的那个部分,就是需要裁切的起始结束位置。

def repeat(file):
    '''计算视频中存在变化的区域起止范围'''
    data = []
    for img in imiter(file):
        h, w, n = img.shape
        data.append(img.reshape((h, -1)).mean(1))
    data = np.array(data)
    var = data.var(0)
    where = np.where(var > var.mean())[0]
    return where.min(), where.max() + 1

3.6 图像拼接

综合前面的方法,就可以计算出录屏视频中各帧图像的偏移数值序列,将图像拼接成完整的长截图。

图像拼接有两种策略:

3.6.1 底部拼接

第一种方法,将新一帧的追加内容直接拼接在图像的底部

def join_bottom(file, gaps, st=None, ed=None):
    '''滚动拼接到图片底部'''
    imgs = imiter(file, st, ed)
    data = next(imgs)
    for n, (img, gap) in enumerate(zip(imgs, gaps)):
        data = np.concatenate([data, img[img.shape[0] - gap:]])
    return data

3.6.2 平均拼接

第二种方法,将重叠区域的图像平均后再保存。对于屏幕录屏来说,每一帧的画质是有损失的,进行平均处理后,画质会有明显的提升:

def join_avg(file, gaps, st=None, ed=None):
    '''加权拼贴图像'''
    imgs = imiter(file, st, ed)
    data = next(imgs).astype(np.int16)
    data = np.concatenate([data, np.zeros((sum(gaps), *data.shape[1:]), np.int16)]) # 扩展填0
    weight = np.zeros_like(data)
    height = data.shape[0]
    st = 0
    for n, (img, gap) in enumerate(zip(imgs, gaps)):
        st += gap
        ed = min(height, st + img.shape[0])
        data  [st:ed] += img[:ed-st]
        weight[st:ed] += np.ones_like(img[:ed-st])
    return data // weight

不过这种方法,一旦某两帧的偏移值计算出现错误,拼接就会错位,导致保存图像出现重影,并且不容易被看出来。

四、实现代码

把之前的内容串起来,就能得到录屏视频生成长截图的程序,可以留给读者作为课后练习。

我也把完整的程序、和测试用例上传到了网盘,有需要的同学可以自行下载运行:

https://download.csdn.net/download/weixin_39804265/84993982

五、最终结果

测试了一段12秒的录屏视频,总共908帧,合成了720×50557分辨率的长截图,相当于39个屏幕高度,总共用时62.98秒,平均每秒处理14.4帧图像

而之前用的滑动对齐法,同样的908帧的视频,总共用时,呃。。7853秒,也就是2小时11分钟。。耗时为模板匹配方法的124.7倍,平均每帧用时8.66秒

使用手机录制示例:

屏幕录像

程序拼接效果示例:

拼接效果


  1. Picsew——iPhone上的截图工具: https://baijiahao.baidu.com/s?id=1660058225659119872 ↩︎

  2. python实现模板匹配: https://www.cnblogs.com/april0315/p/13741888.html ↩︎

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要通过Python和OpenCV快速拼接出完整的长截屏图片,我们可以按照以下步骤进行: 首先,使用Python中的手机录屏工具(如Android Debug Bridge,简称ADB)来录制手机屏幕视频。首先需要在电脑上安装并配置好ADB工具。 其次,使用Python中的OpenCV库来读取录制好的视频文件。可以使用OpenCV的VideoCapture函数来从视频文件中提取每一图像。 然后,根据视频数和分辨率,确定长截屏图片的尺寸。也就是将所有的图像按序拼接为一个完整的长截屏图片。 接下来,创建一个空白的长截屏图片,使用OpenCV的imwrite函数将第一图像保存到长截屏图片中。 对于其余的图像,可以使用OpenCV的addWeighted函数将它们逐一拼接到长截屏图片的尾部。根据移动设备的屏幕方向和滑动方向,可以选择水平或垂直拼接。 最后,保存生成的长截屏图片。 需要注意的是,在拼接过程中,可能出现图像大小不匹配的情况。可以使用OpenCV的resize函数来调整每个图像的大小,使其与长截屏图片的大小一致。 另外,在实际运行过程中,可能还需要根据手机录制视频率和间隔等参数来调整拼接速度和图片质量。 综上所述,通过Python和OpenCV,我们可以实现快速拼接出完整的长截屏图片。这样就可以方便地将手机屏幕内容保存为单个长图,便于查看和分享。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值