利用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,等待安装。

  1. aip报错
    解决办法:pip install baidu-aip

  2. 在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.32t_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提取视频中的字幕的主要内容,如果未能解决你的问题,请参考以下文章

Python | 某大学mooc字幕下载

格式工厂合并 mp4 和 srt,并利用 python 按照字幕剪辑视频,将其分割为若干小段

如何映射用ffmpeg和视频字幕提取的帧? (帧精度问题)

什么软件可以把Mp4视频里的字幕提取出来?

是否可以使用带有 ffmpeg 的字幕轨道提取视频轨道中的对话?

python实战项目惊呆了,竟然能剪辑视频!