亚马逊AWS官方博客

使用 Amazon Transcribe 为视频增加中文字幕

背景介绍

语音识别技术,也被称为自动语音识别(Automatic Speech Recognition,简称ASR),其目标是将人类的语音中的词汇内容转换为计算机可读的输入,例如按键、二进制编码或者字符序列。语音识别技术已经发展了几十年,直到2009年,Hinton把人工智能深度学习解决方案引入语音识别中,语音识别才取得了巨大突破。

Amazon Transcribe 是一项自动语音识别 (ASR) 服务,使开发人员能够轻松地为其应用程序添加语音转文本功能。自从在 re:Invent 2017 发布以来,越来越多的用户将语音识别功能添加到其应用程序和设备中。2019年8月,Amazon Transcribe推出对中文普通话的支持。更加另用户兴奋的是,在中国的北京区域(BJS)和宁夏区域(ZHY)也已支持该项服务。

在日常工作学习中,我们往往需要对一个视频文件增加字幕。传统的方法需要将视频中的对话用文字记录下来,通常采用记事本等工具保存文字记录,然后借助一些工具软件为文字加上时间轴,然后进行人工校对,整个过程需要耗费大量的时间和精力。是否有更快捷的方法呢?下面我们分享一个使用Amazon Transcribe为视频自动增加字幕的示例。

体系架构

示例的总体架构如下图所示:

  1. 用户上传视频文件到S3存储桶;
  2. 监测到S3存储桶中的文件变化,触发lambda函数;
  3. lambda函数调用Transcribe服务,生成视频对应的文本(json格式);
  4. 对文本进行格式转换,生成字幕文件格式(srt);
  5. 上传字幕文件到存储桶。

实现过程

1. 创建S3存储桶

首先在AWS管理控制台进入”S3“服务,点击“Create bucket”, 输入存储桶的名称,点击“Create”按钮创建一个s3存储桶。

在刚建立的存储桶中,点击“Create folder”按钮,输入文件目录名称“video”,然后点击“Save”按钮。然后进入到“video”目录,点击“Create folder”按钮,输入文件目录名称“output”,点击“Save”按钮。此时,您在存储桶中创建了“video”目录,后面的lambda函数将监测video目录中的文件变化。在“video”目录下的“output”目录用来存储生成的字幕文件。

2. 创建IAM角色

每个Lambda函数都有一个与之关联的IAM角色。此角色定义允许该功能与其进行交互的其他AWS服务。在本示例中,您需要创建一个IAM角色,授予您的Lambda函数权限,以便与Transcribe服务以及在上一步中创建的S3服务进行交互。

在AWS管理控制台中,单击“Services”,然后选择“IAM”。在左侧导航栏中选择“Role”,然后选择“Create new role”,依次选择“AWS Service”,“Lambda”作为角色类型,然后单击“Next:Permissions”按钮,在“Filter”文本框中键入“AWSLambdaBasicExecutionRole”,然后选中该角色旁边的复选框,然后在“”中输入“AmazonTranscribeFullAccess”,选中该角色旁边的复选框,点击“Next:Review”,中角色名称中输入“extractSubtitleRole”,点击“Create role”。

3. 创建Lambda函数

在AWS管理控制台进入“Lambda”服务,点击“Create Function”按钮。

在“Function name”中填写函数名称,在“Runtime”的选择框中选择“Python 2.7”,在“Permissions”中选择“Use an existing role”,然后选择刚刚创建的角色名称,点击“Create function”按钮完成函数的创建。在此示例中,我们选择了Python 2.7作为开发环境,并为该Lambda函数赋予了上一步创建的角色。

4. 配置触发条件

在Lambda的函数配置页面,点击“Add Trigger”按钮添加触发条件。

在触发条件配置页面,在“Bucket”下拉列表中选择刚刚创建的存储桶名称,在“Event”下拉列表中选择“Put”,在“Prefix”中输入“videos/”,在“Suffix”中输入“.mp4”,然后点击“add”按钮,完成触发条件的添加。该触发条件设置监视刚刚创建存储桶的video目录中扩展名为.mp4的文件,如果是put操作,将触发该lambda函数。

5. Lambda内存和超时配置

在刚创建的Lambda函数中,我们需要配置了内存的大小和执行超时。由于Lambda函数会调用Transcribe服务进行文字提取,因此不需要修改内容的大小,默认值为128MB。示例中我们采用的视频文件的时长均在一分钟内,Transcribe的处理时间通常不会超过一分钟,在这里我们设置超时时长“Timeout”为10分钟。

6. 导入Lambda函数

打开附件中的python文件,将其内容粘贴在Lambda函数实现的区域,点击右上角的“Save”按钮。

Lambda的实现主要包括以下几个步骤:

6.1. 参数获取

从event对象中和系统变量中获取相关参数信息。

  • region:当前区域,示例中使用的是区域是us-east-1
  • bucket_name:存储桶名称,您刚刚创建的存储桶名称
  • sourceS3Key:视频文件的key值。示例中监测video目录下的mp4类型的文件,key值为video/sample.mp4
  • fn:根据sourceS3Key提取文件名。示例中fn为mp4
  • dir:根据sourceS3Key提取目录名。示例中dir为video

6.2. 调用Transcribe任务

  • 为每个任务job_name创建唯一的标识
  • 调用starttranscriptionjob,下面的代码中介绍了每个参数以及含义
  • 由于调用的job是异步任务,我们通过轮训的方法检测job的返回结果

    #生成转换任务的时间戳
now=int(time.time())
job_name = “conv-“+str(now)

  ”’
    启动转换任务
    MediaFileUri:媒体路径,示例采用s3的路径
    MediaFormat:媒体格式,目前支持mp3,mp4,wav,flac
    LanguageCode:媒体的语言编码,我们的视频是中文的,设置为zh-CN
    ”’

    transcribe = boto3.client('transcribe')
    transcribe.start_transcription_job(
        TranscriptionJobName=job_name,
        Media={'MediaFileUri': job_uri},
        MediaFormat='mp4',
        LanguageCode='zh-CN'
    )

    #转换需要一定的时间,这里进行轮训检测处理结果。您也可以通过控制台查看任务状态。

   while True:
        status = transcribe.get_transcription_job(TranscriptionJobName=job_name)
        if status['TranscriptionJob']['TranscriptionJobStatus'] in ['COMPLETED', 'FAILED']:
            break
        print("Processing...")
        time.sleep(5)

 

  • job的返回结果

6.3. 生成srt字幕文件

我们可以看到,在json数组中,包含每个字(或者词语)的开始时间,结束时间,置信度等信息。下面我们需要把json数组转换成字幕文件。常见的字幕格式有srt, ssa, ass, idx+sub,其中srt, ssa, ass是文本格式,idx+sub是图形格式。本示例中,我们将使用srt格式。srt的格式非常简单:一句时间代码 + 一句字幕。

6.4. 转换为srt格式字幕

函数process()将上一步的json文件转换成srt格式的字幕文件,具体处理过程如下。

def process(items):
    i=1
    output=''
    isStart=False
    isEnd=False
    start_time=0
    end_time=0
    msg=''
    for index, item in enumerate(items):
 
        if (not item.has_key('start_time')):
            msg=msg+item['alternatives'][0]['content']
        else:
            end_time=float(item['end_time'])
 
        if (end_time-start_time>4.0 or index+1==len(items)):
            isEnd=True
 
        if (not isStart and item.has_key('start_time')):
            isStart=True
            start_time=float(item['start_time'])
            msg=msg+item['alternatives'][0]['content']
            output=output+str(i)+'\n'
            continue
           
        if (isStart and not isEnd and item.has_key('start_time')):
            msg=msg+item['alternatives'][0]['content']
 
        if (isStart and isEnd):
            hour=int(start_time/60/60)
            min=int(start_time/60)-hour*60
            sec=int(start_time)-min*60-hour*60*60
            msec=int((start_time-sec)*1000) 
            e_hour=int(end_time/60/60)
            e_min=int(end_time/60)-e_hour*60
            e_sec=int(end_time)-e_min*60-e_hour*60*60
            e_msec=int((end_time-sec)*1000)
            msg1='{}:{}:{},{} --> {}:{}:{},{}'.format(hour,min,sec,msec, e_hour,e_min,e_sec,e_msec)+'\n'
            output=output+msg1+msg+item['alternatives'][0]['content']+'\n\n'
            i=i+1
            isStart=False
            isEnd=False
            start_time=end_time
            msg=''
 
       

6.5. 上传结果到S3

最后我们将srt文件上传到s3,本示例中,我们设置了video/output作为其输出的存储路径。

def uploadResult(region,bucket_name,fn,body):
    s3 = boto3.client(service_name='s3',region_name=region)
    s3.put_object(Bucket=bucket_name, Key=fn[0:-4]+'.srt', Body=body)

7. 测试

在AWS管理控制台点击“S3”服务,打开刚创建的存储桶,进入“video”目录,点击“Upload”“Add files”从本地电脑里选择一个视频文件,点击“Upload”。此时就会触发我们刚刚创建的Lambda函数。我们可以在“Amazon Transcribe”观察job的执行情况。点击任何一个job的名称,可以显示job的详细信息。当job的状态显示为“Complete”,进入到S3存储桶的“output”目录,您会惊喜的发现,字幕文件已经生成了。注意,由于Lambda函数有600秒的执行超时限制,尽量不要处理时间超过15分钟的视频。

6

下面我们看一下效果吧。通过一些测试,语音的识别正确率还是比较高的,值得一提的是,Transcribe能够根据语音的停顿自动加上标点。但在人名的识别上,正确率还有待提高。

很多播放软件都支持自动加载字幕,需要把字幕文件和原始的视频文件放在同一目录。如果需要把字幕合成到视频中,可以使用ffmpeg为视频嵌入字幕,如果您使用的是Mac操作系统,也可以使用subler图形化工具把字幕文件嵌入到视频中。

8. 调试

在这里,一些读者一定会由于疏忽遗漏了上面的某些步骤,从而导致Lambda函数执行失败。在CloudWatch中,可以快速查询到Lambda函数的执行日志,方便读者进行调试。

成本分析

最后我们分析一下成本,以美东弗吉尼亚区域(us-east-1)为例:

Lambda实例采用128M内存,每月有3,200,000秒的免费用量,假设处理一段视频需要600秒,免费额度内您可以处理近5000个视频。

Memory (MB) Free tier seconds per month Price per 100ms ($)
128 3,200,000 0.000000208

假设采用S3标准存储,假设处理5000个视频文件,每个文件大小100M,大约需要500G的存储空间,大约需要12$(5000*100/1024*0.023)。字幕文件为文本格式,大小可以忽略。

Volume Size Price
First 50 TB / Month $0.023 per GB
Next 450 TB / Month $0.022 per GB
Over 500 TB / Month $0.021 per GB

Amazon Transcribe API的计价方式是$0.0004/秒,按照上面的假设,每个视频长度为600秒,费用大约需要1200$(5000*600*0.0004)。

按照上面的假设,处理每个视频需要的费用仅仅需要0.24$ ((1200+12)/5000)),折算成人民币,成本不到2块钱。

总结

通过使用Amazon Transcribe,用户可以方便的集成在各种场景中。用户不需要购买服务器,不需要算法实现,仅通过Lambda或者API调用的方式,方便快速的构建自己的ASR应用。

参考文章

  • https://aws.amazon.com/cn/blogs/china/amazon-transcribe-now-supports-mandarin-and-russian/
  • https://docs.aws.amazon.com/zh_cn/transcribe/latest/dg/what-is-transcribe.html
  • https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/transcribe.html

源代码

您可以通过下载source.zip进行验证测试。

本篇作者

张铮

AWS APN 合作伙伴解决方案架构师,主要负责 AWS (中国)合作伙伴的方案架构咨询和设计工作,同时致力于 AWS 云服务在国内的应用及推广。

刘旭东

AWS 解决方案架构师经理,负责基于AWS的云计算方案架构咨询和设计。同时致力于AWS云服务在国内的应用和推广。