用Python+OpenCV截取视频中所有含有字幕的画面

1、需求背景

有的视频文件的字幕已经压制到了视频的图像中,不能单独提取出字幕文件。网上的 “提取视频字幕” 网站多为提取视频中的字幕文件,而非识别视频图像中的字幕。少数通过OCR技术识别画面中字幕的工具需要在线运行、运行速度较慢,或者需要收费,使用不够灵活。

我希望实现在视频中提取字幕截图的程序,需要解决两个关键问题:一是如何判断视频中是否出现了字幕,二是如何确定字幕何时发生变化。然后在有字幕且字幕刚刚发生变化时,截取并保存字幕图片。

为了解决这两个问题,我的主要思路是利用视频中字幕的固定位置和特殊颜色进行提取。通常情况下,字幕会出现在视频的特定区域,并且其颜色与周围背景颜色不同、并使用固定颜色。我们可以通过指定字幕所在的区域坐标和颜色来提取字幕图像。

然后通过判断在指定区域内符合指定颜色的像素值是否超过阈值,来判断是否存在字幕。而通过判断两帧画面之间的像素差异数是否超过阈值,来判断字幕是否变化

通过这种方式,并根据实际情况调整参数,可以达到比较合适的漏检率和误检率,从而实现较好的字幕提取效果。

2、设计思路

2.1 读取视频的迭代器函数 VideoIter

函数实现从视频中迭代获取指定时间范围内的帧图像,它接受4个参数:file表示视频文件路径,start_time表示开始时间(单位秒,后同),end_time表示结束时间,step_time表示迭代步长。其中start_timeend_time可以设置为负数,表示为相对于总时长的倒数时间位置,step_time设置为负数时表示为倒序迭代视频中的图像。

函数从视频的start_time开始时间开始截取图像,每间隔step_time步长时间捕获一张图像,将帧图像作为生成器(yield)的输出,直到达到end_time结束时间停止。

2.2 图像预处理函数 ProcessImage

函数实现从传入图像中裁剪指定区域、并根据给定的颜色和浮动值进行颜色分割,它接受4个参数:img表示输入的图像,area表示要裁取的区域边界坐标(格式为 (x1, y1, x2, y2)),color表示要分割的颜色(格式为 (r, g, b)),float表示颜色分割时的容差范围值。

函数设计从视频画面中裁取字幕出现位置的区域,然后根据设定的字幕颜色和允许容差匹配出文字区域的蒙版。最后,使用OpenCV的方法根据颜色阈值对图像进行转换,并将函数处理过的图像返回。

2.3 提取视频中字幕图像的主函数 ExtractSubtitle

函数实现从视频中提取字幕图像,它接受7个参数:file表示视频文件路径,area表示字幕所在区域的边界坐标(格式为 (x1, y1, x2, y2)),color表示字幕的颜色(格式为 (r, g, b)),float表示颜色判断时的容差范围值,count_thresh表示像素计数阈值,diff_thresh表示像素差异阈值,delay表示显示图像的延迟时间。

函数通过循环调用VideoIter函数迭代读取视频中的帧图像,并使用ProcessImage函数处理图像,将字幕部分提取出来。在处理过程中,函数统计处理后图像中非零像素的数量,并计算当前图像与上一帧图像的像素差异。如果像素数量和像素差异数值均超过了各自设定的阈值,就将当前帧图像显示输出(作为调试)、并将视频帧图像保存到视频文件同名的文件夹中。文件命名包含帧序号、像素数量、和像素差异数(用于调试参考)。

3、实现代码

import os
import cv2
import numpy as npdef imshow(img, delay=1, title=''):cv2.imshow('', img)cv2.setWindowTitle('', title)cv2.waitKey(delay)def imwrite(file, im):cv2.imencode('.jpg', im)[1].tofile(file)def VideoIter(file, start_time, end_time, step_time):cap = cv2.VideoCapture(file)fps = cap.get(cv2.CAP_PROP_FPS)total_time = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) / fpsif np.signbit(start_time):start_time += total_timeif np.signbit(end_time):end_time += total_timefor current_time in np.arange(start_time, end_time, step_time):cap.set(cv2.CAP_PROP_POS_FRAMES, int(fps * current_time))ret, img = cap.read()if not ret:breakyield imgcap.release()def ProcessImage(img, area, color, float):x1, y1, x2, y2 = areaimg = img[y1:y2, x1:x2]r, g, b = colorrgb1 = (r - float, g - float, b - float)rgb2 = (r + float, g + float, b + float)img = cv2.inRange(img, rgb1, rgb2)return imgdef ExtractSubtitle(file, area, color, float, count_thresh, diff_thresh, delay):folder = os.path.splitext(file)[0]os.makedirs(folder, exist_ok=True)img1 = Nonediff = 0for id, img in enumerate(VideoIter(file, 0, -1, 1), 1):img2 = ProcessImage(img, area, color, float)count = cv2.countNonZero(img2)if img1 is not None:diff = cv2.countNonZero(img1 ^ img2)img1 = img2if count > count_thresh and diff > diff_thresh:imshow(img2, delay=delay, title=f'count={count}, diff={diff}')save_path = f'{folder}/img_{id:06}@count={count}@diff={diff}.jpg'imwrite(save_path, img)if __name__ == '__main__':video_path = '三体.S01E01.HD1080P.mp4'area_xyxy = (564, 722, 1328, 784)subtitle_rgb = (250, 250, 250)floating_range = 10count_thresh = 1000diff_thresh = 400delay = 1ExtractSubtitle(video_path, area_xyxy, subtitle_rgb, floating_range, count_thresh, diff_thresh, delay)

函数运行前有若干参数需要确定,首先是字幕出现的位置字幕的颜色

截取若干典型的视频中包含字幕的图像,然后通过图画板确定字幕出现的位置,用取色器获得字幕的RGB值。

读取字幕位置坐标:

读取字幕位置坐标

字幕颜色取色:

字幕颜色取色

另外两个需要设定的参数,是判断图像中存在字幕的阈值、和字幕发生变化的阈值。很显然,这两个数值都应大于0。

其中,存在字幕的阈值设置得越低,则会有越多的本不包含字幕,但是由于背景色中存在和字幕相同颜色而被误判断成为含有字幕的图片。

发生变化的阈值设置得越低,则会有越多的相同的字幕画面由于图片压缩或背景差异,有少量像素存在区别,被误判断成字幕已经发生变化,导致截取出多余的字幕画面图像

在具体调试时,可以先将这两个参数设定为0,然后运行程序。在弹出的可视化窗口中,在标题中可以看到我设置的这两个参数的计算数值。

结合实际裁取出的图像,我们可以适当地设计这两个门限参数的数值,通过调整参数并观察提取结果,以获得较好的字幕图像截取效果。

4、运行效果

测试运行60分钟的网飞《三体》第一集,一共提取出了600张截图,有比较良好的漏检率和误检率。如果按照1秒钟看5张图的话,2分钟就可以看完60分钟的第一集。

程序运行结果:

程序运行结果

拼合字幕图片:

import glob
import cv2
import numpy as npimread = lambda file: cv2.imdecode(np.fromfile(file, np.uint8), -1)
imwrite = lambda file, img: cv2.imencode('.jpg', img)[1].tofile(file)def MergeSubtitles(folder, h1, h2):imgs = []for path in glob.glob(f'{folder}/*.jpg'):img = imread(path)imgs.append(img[h1:h2])final_image = cv2.vconcat(imgs)imwrite(f'{folder}.jpg', final_image)if __name__ == '__main__':MergeSubtitles('三体.S01E01.HD1080P', 722, 784)

最终图片效果:

截图效果展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法训练营第31天|LeetCode 455.分发饼干 376.摆动序列 53.最大子序列和

LeetCode 455.分发饼干 题目链接&#xff1a; LeetCode 455.分发饼干 解题思路&#xff1a; 从孩子小到大&#xff0c;找到最小能满足孩子的饼干&#xff0c;之后看最多能满足几个孩子。 代码&#xff1a; class Solution { public:int findContentChildren(vector<in…

蓝桥杯练习笔记(十八)

蓝桥杯练习笔记&#xff08;十八&#xff09; 一、用辅助栈来优化递归深度过大的问题 输入示例 0000100010000001101010101001001100000011 0101111001111101110111100000101010011111 1000010000011101010110000000001011010100 0110101010110000000101100100000101001001 0…

新增用户信息应该考虑什么?

前置数据校验 1. 用户的信息的合法性&#xff0c;如手机号&#xff0c;邮箱等jsr303检验 2. 用户数据脱敏处理&#xff0c;如密码 3. 短信验证码验证 &#xff08;随机数生成 redis 短信服务&#xff09; 4. 用户信息的账号唯一性校验 5. 给用户授权&#xff0c;如角色或其它…

QT打包生成.exe可执行文件

QT打包生成.exe可执行文件 程序运行图标如何设置快捷方式显示图标QT打包成可执行文件将可执行文件打包成安装包程序运行图标 如何生成如下图标? 首先将你的图标(ico文件)放入当前工程目录,即含有.pro文件的同级目录 右击项目,选择ADD New,选择Qt Resource File, 这是一个…

吴恩达2022机器学习专项课程(一) 5.7 检测梯度下降是否收敛

问题预览/关键词 什么是梯度下降收敛&#xff1f;哪些方法可以检测梯度下降是否收敛&#xff1f;什么是学习曲线&#xff1f;曲线上升代表什么&#xff1f;什么原因造成的&#xff1f;如何检测梯度下降是否收敛&#xff1f;多少次迭代&#xff0c;梯度下降会收敛&#xff1f;什…

C++:初步接触C++(2)

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习C&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 文章目录 内联函数1.概念2.特性 auto关键字1.auto简介2.auto的使用细则3.auto不能推导的场景 基于范围…

算法刷题记录 Day40

算法刷题记录 Day40 Date: 2024.04.06 kamma 56. 多重背包 #include<bits/stdc.h> using namespace std;int main(){int n, c;while(cin>>c>>n){vector<int> weights(n, 0);vector<int> values(n, 0);vector<int> knums(n, 0);for(int …

一文掌握python面向对象

面向对象编程(Object-Oriented Programming,简称OOP)是一种重要的编程范式,它将现实世界的问题抽象成类和对象,通过封装、继承和多态等特性实现程序设计的模块化与灵活性。Python作为一门支持OOP的高级语言,其面向对象的特性使得代码更加简洁、易读且易于维护。本文旨在全…

RTThread studio 驱动开发

rtthread 驱动使用的两种情况 rtthread studio 自动生成 由 RT Thread Studio 自动生成&#xff0c;无需修改任何文件或者简单定义几个宏即可直接使用的驱动&#xff0c;如 GPIO&#xff0c;UART&#xff0c;I2C&#xff0c;SPI&#xff0c;SDIO 和 ETH 等。 使用 RT-Thread S…

Python - 基础知识

变量 语法&#xff1a; a 10 #意思是创建一个a变量&#xff0c;把10存放到a变量中。 #这里说明一下&#xff0c;#在Python中是是注释符 print(a) #print是打印函数 变量是一个可以发生改变的量&#xff0c;变量是用来区分不同数据的&#xff0c;可以指向一个内存空间&…

如何定位和优化程序CPU、内存等性能之巅

如何定位和优化程序CPU、内存等性能之巅 摘要 性能优化指在不影响系统运行正确性的前提下&#xff0c;使之运行得更快&#xff0c;完成特定功能所需的时间更短&#xff0c;或拥有更强大的服务能力。本文将介绍性能优化的基本概念以及如何定位和优化程序中的CPU、内存和IO瓶颈…

信息泄露漏洞的JS整改方案

引言 &#x1f6e1;️ 日常工作中&#xff0c;我们经常会面临线上环境被第三方安全厂商扫描出JS信息泄露漏洞的情况&#xff0c;这给我们的系统安全带来了潜在威胁。但幸运的是&#xff0c;对于这类漏洞的整改并不复杂。本文将介绍几种可行的整改方法&#xff0c;以及其中一种…

指挥中心控制台厂家定制控制台技术规范全方位指南

指挥中心控制台作为现代化管理的重要组成部分&#xff0c;在整个企业的运行中起着重要作用&#xff0c;为了保证指挥中心的正常运行&#xff0c;控制台的定制不可缺少&#xff0c;那么指挥中心控制台厂家定制控制台技术规范是什么? 1. 结构性能规范&#xff1a;控制台需采用优…

kmeans聚类sklearn实现(Python实验)

Kmeans毫无疑问&#xff0c;好用又“便宜”的算法&#xff0c;经常在很多轻量化场景中实现。所谓的“聚类”&#xff08;Clustering&#xff09;&#xff0c;就是通过欧氏距离找哪些点构成一个簇。假设我们空间中有一堆点&#xff0c;通过肉眼大概可以看出有两簇&#xff0c;思…

jmeter压测websocket协议

一、jmeter 安装websocket插件 1、选项--插件管理 2、搜索WebSocket Samplers by Peter Doornbosch插件 进行安装 3、 重启 jmeter 二、jmeter压测websocket协议实战 2.1、以网站为例&#xff1a; websocket在线测试 1、断开连接 2、打开F12&#xff0c;查看WS数据 3、…

DragonIM龙通讯

前言 龙通讯是一款C/S架构的即时通讯软件&#xff0c;实现了用户注册、登录、好友私聊、群聊&#xff08;文字、表情、文件&#xff09;&#xff0c;群文件上传/下载&#xff0c;群公告&#xff0c;朋友圈&#xff08;可点赞和评论&#xff09;&#xff0c;AI聊天&#xff0c;…

剪切助手-高颜值的跨平台剪切板工具

高颜值的跨平台剪切板工具来了&#xff01;&#xff01; http://t.csdnimg.cn/xKB3B 出于我的一些日常使用需求以及在对比了其他剪切板软件后&#xff0c;我决定做一个跨平台的高颜值剪切板工具《剪切助手》&#xff01; 废话不多说&#xff0c;你可以来这里 下载体验 它&…

记录一次云主机云监控客户端故障

场景&#xff1a;客户弹性云主机的云监控客户端本来正常。 但是昨天下午突然不行了&#xff0c;提示故障。 仔细排查发先&#xff0c;ps -ef|grep telescop 进程是在的 于是查看正常的/etc/resolv.conf里面的dns信息&#xff0c;发现故障的少了一天dns信息&#xff0c; 100.…

day76 jquery

知识点: 1 在HTML中引入jQuery 2 jQuery中就绪函数 3 jQuery中选择器 4 使用jQuery获取表单元素的值 及标签中间的内容 5 jQuery中获取标签属性 6 jQuery设置和获取标签样式 ----------------------------------- 一 在HTML中引入jQuery 1/*! jQuery…

RFID涉密载体柜 RFID智能文件柜系统

涉密载体管控RFID智能柜&#xff08;载体柜DW-G101R&#xff09;通过对涉密物资、设备进行RFID唯一标识并放置于RFID设备涉密物资柜柜体&#xff0c;通过定位每台设备每件涉密物资的位置&#xff0c;实现涉密物资审批、自助借还、防盗等出入库全流程自动化管理。主要管理对象移…