利用python提取视频中的字幕
Posted qq_43306047
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用python提取视频中的字幕相关的知识,希望对你有一定的参考价值。
利用python提取视频中的字幕
``
一、导包
import base64
import os
import cv2
import requests
import aip
from aip import AipOcr
1.cv2报错
解决办法:打开Anaconda Prompt,输入pip install opencv-python,等待安装。
-
aip报错
解决办法:pip install baidu-aip -
在pycharm中使用anaconda包
File–> Settings–>Project Interpreter
找到Anacondad的安装路径中的python.exe就OK了。
二、解析视频
将视频间隔10帧取图片
def VLink():
video_path = 'D:/Resource/MaxFish.mp4' # 视频地址
images_path = 'D:/Resource/images/' # 图片输出文件夹
interval = 10 # 每间隔10帧取一张图片
num = 1
vid = cv2.VideoCapture(video_path)#打开这个视频
while vid.isOpened():
is_read, frame = vid.read() #按帧读取视频 frame是读取图像 is_read是布尔值。文件读取到结尾返回FALSE
if is_read:
file_name = num
cv2.imwrite(images_path + str(file_name) + '.jpg', frame)
cv2.waitKey(1)
num += 1
else:
break
结果:
三、截取字幕
将图片中的字幕部分截取出来
def tailor(path1,path2,begin ,end,step_size):
for i in range(begin,end,step_size):
fname1 = path1%str(i)
print(fname1)
img = cv2.imread(fname1) #像素
print(img.shape)
cropped = img[650:720, 300:1024] # 裁剪坐标为[y0:y1, x0:x1]
imgray = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY)
thresh = 200
ret, binary = cv2.threshold(imgray, thresh, 255, cv2.THRESH_BINARY)
binary1 = cv2.bitwise_not(binary)
cv2.imwrite(path2 % str(i), binary1)
cropped = img[650:720, 300:1024]这里的截取可能因为照片的大小不同而不同,可以编辑照片,看一下适合字幕截取的位置。例如:
通过 鼠标的移动知道截取图片的位置。
结果:
四、解析图片
解析图片,获得字幕,保存在TXT文档中。
1、
def subtitle(fname,begin,end,step_size):
array =[] #定义一个数组用来存放words
for i in range(begin,end,step_size):
fname1 = fname % str(i) #字幕image D:/Resource/images/img_subtitle/100.jpg
with open(fname1, 'rb') as fp:
image = base64.b64encode(fp.read())
try:
results = requestApi(image)["words_result"] #调用requestApi函数,获取json字符串中的words_result
for item in results:
print(results)
array.append(item['words'])
except Exception as e:
print(e)
text=''
result = list(set(array)) # 去重
result.sort(key=array.index) # 排序
for item in result:
text +=item+'\\n'
2、
# 定义一个函数,用来访问百度API,
def requestApi(img):
general_word_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"
params = "image": img,
"language_type": "CHN_ENG"
access_token = '24.80669db308b385e6f913e40b3fe604d1.2592000.1651237616.282335-25877315'
request_url = general_word_url + "?access_token=" + access_token
headers = 'content-type': 'application/x-www-form-urlencoded'
response = requests.post(request_url, data=params, headers=headers)
results = response.json()
return results
百度智能云:
网址:https://login.bce.baidu.com
点击创建应用,写个名称就创建成功了。
在这里可以看见API Key和Secret Key,我们需要用这两个参数获取
点击左边的导航栏的技术文档---->API文档----->通用场景文字识别------>可以选择标准版
告诉了我们了如何获取Access Token。
复制链接:https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=mGAOwUKl42RM93TAWEmHZ3ff&client_secret=RGlbvPF49FGpqLiMVhFow1xfXp4EAvWAA&
这里的grant_type固定为client_credentials
client_id === API Key
client_secret == Secret Key
将你自己申请的API Key和Secret Key替换掉就可以了,回车获得access_token
这样就可以了。
如果想在代码中查找access_token。可以如下:
def get_access_token():
url = 'https://aip.baidubce.com/oauth/2.0/token'
data =
'grant_type': 'client_credentials', # 固定值
'client_id': 'eFGwDIb*******HucbnPr', # API Key
'client_secret': 'XPxWT2L********PFVCKS6PVih' # Secret Key
res = requests.post(url, data=data)
res = res.json()
print(res)
access_token = res['access_token']
return access_token
问题:KeyError: ‘words_result’
解决办法:
(1)进入百度智能云,点击领取免费资源
(2)进入后,会有一个通用场景识别,选择“全部”,然后进行领取(这里因为我已经领过了,所以就没有显示了);
(3)领取后,回到刚刚那个界面,查看“资源列表”,可以看到自己已经领取的资源;
(4)如果使用后还出现这种情况,搜索:https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token=24.80669db308b385e6f913e40b3fe604d1.2592000.1651237616.282335-25877322
查看出现的数字:
如果是17,可以实名认证,就会增加调用量。
3、
在D:/Resource/下创建一个subtitle.txt,将提取出来的字幕写进去。
#创建文本
def text_create( msg):
full_path = "D:/Resource/subtitle.txt" # 也可以创建一个.doc的word文档
file = open(full_path, 'w',encoding='utf-8')
file.write(msg)
file.close()
五 、主函数
if __name__ == '__main__':
path1 = 'D:/Resource/images/%s.jpg' # 视频转为图片存放的路径(帧)
path2 = 'D:/Resource/images/img_subtitle/%s.jpg' # 图片截取字幕后存放的路径
print("""
1..裁剪视频
2.图片裁剪
3.提取字幕
""")
choose = input()
begin = 100
end = 1000
step_size = 10
if choose == '1': #视频中提取图片
VLink()
if choose == '2': #提取字幕
tailor(path1, path2, begin, end, step_size)
if choose == '3': #提取字
subtitle(path2, begin, end, step_size)
借鉴:https://blog.csdn.net/qq_39783601/article/details/105748486
如何映射用ffmpeg和视频字幕提取的帧? (帧精度问题)
【中文标题】如何映射用ffmpeg和视频字幕提取的帧? (帧精度问题)【英文标题】:How to map frame extracted with ffmpeg and subtitle of a video? (frame accuracy problem) 【发布时间】:2019-11-12 15:12:09 【问题描述】:想为使用 ffmpeg 提取的帧生成文本文件,如果有的话,包含帧的字幕,在我也使用 ffmpeg 刻录字幕的视频上。
我使用带有pysrt
的python 脚本来打开subrip 文件并生成文本文件。
我正在做的是每个帧都由 ffmpeg 用帧号命名,然后由于它们以恒定速率提取,我可以使用公式 t1 = fnum/fps
轻松检索帧的时间位置,其中fnum
是使用文件名检索的帧数,fps
是传递给 ffmpeg 以进行帧提取的频率。
即使我使用相同的字幕文件来检索时间轴中的文本位置,也就是视频中使用的那个,我仍然会遇到准确性错误。大多数我有一些文本文件丢失或一些不应该存在。
因为在谈论帧时时间并不是真正连续的,所以我尝试使用带有硬编码字幕的视频的 fps 重新校准 t
,让我们将 fps vfps
称为视频 fps(我已确保视频 fps字幕刻录前后相同)。我得到公式:t2 = int(t1*vfps)/vfps
。
它仍然不是 100% 准确。
例如,我的视频是 30fps (vfps=30
),我以 4fps (fps=4
) 提取帧。
提取的第 166 帧 (fnum=166
) 没有显示字幕。在 subrip 文件中,前一个字幕以t_prev=41.330
结尾,下一个字幕从t_next=41.400
开始,这意味着t_sub
应该满足:t_prev < t_sub and t_sub < t_next
,但我无法做到这一点。
我试过的公式:
t1 = fnum/fps # 41.5 > t_next
t2 = int(fnum*vfps/fps)/vfps # 41.5 > t_next
# is it because of a indexing problem? No:
t3 = (fnum-1)/fps # 41.25 < t_prev
t4 = int((fnum-1)*vfps/fps)/vfps # 41.23333333 < t_prev
t5 = int(fnum*vfps/fps - 1)/vfps # 41.466666 > t_next
t6 = int((fnum-1)*vfps/fps + 1)/vfps # 41.26666 < t_prev
使用的命令:
# burning subtitles
# (previously)
# ffmpeg -r 25 -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# now:
ffmpeg -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# frames extraction
ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner
为什么会发生这种情况,我该如何解决?
我注意到的一件事是,如果我提取原始视频和字幕的帧,对帧进行差异处理,结果不仅是字幕,背景也有变化(这不应该发生)。如果我两次使用相同的视频做相同的体验,差异为null,这意味着帧提取是一致的。
区别代码:
ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner
ffmpeg -i no_sub.mp4 -vf fps=4 extracted_no_sub/%05.bmp -hide_banner
for img in no_sub/*.bmp; do
convert extracted/$img##*/ $img -compose minus -composite diff/$img##*/
done
谢谢。
【问题讨论】:
ffmpeg -r 25 -i nosub.mp4 --> 这将重新定时帧并破坏原始时间戳。除非您知道自己在做什么,否则您不想这样做。 不是真的...我想在不改变帧率的情况下刻录字幕。所以我先试试这个:ffmpeg -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
但是它把帧率从25改成了30,所以我手动设置了帧率,好像不对。我不是 ffmpeg 的大用户。我在 Google 上搜索了如何使用 ffmpeg 保留帧率并找到了这个 superuser.com/questions/460332/…,但它没有被接受的答案。
分享您最初尝试的日志。
我的错,原来的 fps 是 30,所以我第一次尝试刻录字幕并没有改变帧速率。然而问题依然存在。并且在字幕刻录前后进行帧减法显示出比字幕更多的差异。请让我提醒一下,这不是我要解决的问题,但可能是我的问题的原因。你想看什么日志?
还值得注意的是,在示例中,区间的边界乘以帧提取得到:t_prev*fps=165.32
和 t_next*fps=165.6
,这意味着,如果 ffmpeg 提取的帧是1/fps
的因素,那么我不应该让第 166 帧位于两个字幕之间,而是显示第二个字幕(或者前一个字幕)。如果通过视频 fps 进行校正也是一样的:int(t_next*vfps)*fps/vfps=165.2
, int(t_next*vfps)*fps/vfps=165.6
【参考方案1】:
您可以提取具有准确时间戳的帧,因此
ffmpeg -i nosub.mp4 -vf subtitles=sub.srt,settb=AVTB,select='if(eq(n\,0)\,1\,floor(4*t)-floor(4*prev_t))' -vsync 0 -r 1000 -frame_pts true extracted/%08d.bmp
这将从每四分之一秒中提取第一帧。输出文件名长度为 8 个字符,其中前 5 位是秒,后三位是毫秒。您可以根据最大文件持续时间更改字段大小。
【讨论】:
您能否详细介绍一下参数?我也收到一个错误:Missing ')' or too many args in 'if(eq(n,0)'
settb 过滤器用于将时基设置为 1 微秒,选择过滤器表达式将选择第一帧,然后选择最接近上一季度秒的每一帧。 -r 1000 将时间戳转换为毫秒精度(否则文件名会变得更长)。 vsync 确保 ffmpeg 不会改变时间戳。 frame_pts 将时间戳打印到文件名中。
编辑 cmd 以转义所有逗号以上是关于利用python提取视频中的字幕的主要内容,如果未能解决你的问题,请参考以下文章
格式工厂合并 mp4 和 srt,并利用 python 按照字幕剪辑视频,将其分割为若干小段