亚马逊云AI大语言模型应用下的创新Amazon Transcribe的使用

Transcribe简介

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

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

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

AWS Transcribe 服务于利用机器学习来识别语音文件中的声音,然后将其转化为文本。目前支持英语和西班牙文语音。必须将语音文件保存在S3中,输出结果也会被保存在S3中。

  • 输入声音文件,支持 flac、mp3、mp4 和 wav 文件格式。长度不能超过2小时。

  • 指定语言。

几个特色功能:

  • 发音者识别(speaker identification):Transcribe 能区别一个语音文件中的多个说话者。支持2到10个发音者

  • 支持多声道(channel identification): 如果声音文件中有多声道

  • 支持字典(vocabulary):比如不能识别的单词,特定领域不常用的单词

体系架构

  1. 监测到S3存储桶中的文件变化,触发lambda函数;

  2. lambda函数调用Transcribe服务,生成视频对应的文本(json格式);

  3. 对文本进行格式转换,生成字幕文件格式(srt);

  4. 上传字幕文件到存储桶。

控制台操作展示

  • 登录自己的AWS账号进入到aws控制管理台, 然后搜索 Transcribe 进入到这个管理后台

  • 点击 Create job 按钮就可以使用AWS的语音转文本服务,根据提示添加必备的参数设置

api接口

  • StartTranscriptionJob:开始一个转换任务

  • ListTranscriptionJobs:获取任务列表

  • GetTranscriptionJob:获取任务

  • CreateVocabulary:创建字典

  • DeleteVocabulary:删除字典

  • GetVocabulary:获取字典

  • ListVocabularies:获取字典列表

  • UpdateVocabulary:上传字典

python使用Transcribe演示实例

类型1

import time
import boto3
​
transcribe = boto3.client(('transcribe'))
job_name = "testTranscribeJob100"
job_uri = "https://s3.dualstack.us-east-1.amazonaws.com/*****/hellosammy.mp3"
​
transcribe.start_transcription_job(TranscriptionJobName=job_name, Media={'MediaFileUri': job_uri}, MediaFormat='mp3', LanguageCode='en-US')
​
while True:
    status = transcribe.get_transcription_job(TranscriptionJobName = job_name)
    if status['TranscriptionJob']['TranscriptionJobStatus'] in ['COMPLETED', "FAILED"]:
        break
​
    print("Job not ready yet...")
    time.sleep(5)
​
print(status)

类型2

  • 安装 Python 的程序包

pip3 install boto3
pip3 install amazon_transcribe
pip3 install websocket-client
  • import 部分

import hashlib
import hmac
import urllib.parse
from datetime import datetime
import time
import ssl
import json
import websocket
import _thread
from amazon_transcribe.eventstream import EventStreamMessageSerializer
from amazon_transcribe.eventstream import EventStreamBuffer
from boto3.session import Session
  • 创建URL 函数

def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
 
def getSignatureKey(key, dateStamp, region, serviceName):
    kDate = sign(("AWS4" + key).encode("utf-8"), dateStamp)
    kRegion = sign(kDate, region)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, "aws4_request")
    return kSigning
 
def create_pre_signed_url(region, language_code, media_encoding, sample_rate):
    # 获得access key和secret key
    credentials = Session().get_credentials()
    access_key_id = credentials.access_key
    secret_access_key = credentials.secret_key
 
    method = "GET"
    service = "transcribe"
    endpoint = "wss://transcribestreaming." + region + ".amazonaws.com:8443"
    host = "transcribestreaming." + region + ".amazonaws.com:8443"
    algorithm = "AWS4-HMAC-SHA256"
 
    t = datetime.utcnow()
    amz_date =t.strftime('%Y%m%dT%H%M%SZ')
    datestamp =t.strftime('%Y%m%d')
 
    canonical_uri = "/stream-transcription-websocket"
 
    canonical_headers = "host:" + host + "\n"
    signed_headers = "host"
 
    credential_scope = datestamp + "/" + region + "/" + service + "/" + "aws4_request"
 
    canonical_querystring = "X-Amz-Algorithm=" + algorithm
    canonical_querystring += "&X-Amz-Credential=" + urllib.parse.quote_plus(access_key_id + "/" + credential_scope)
    canonical_querystring += "&X-Amz-Date=" + amz_date
    canonical_querystring += "&X-Amz-Expires=300"
    canonical_querystring += "&X-Amz-SignedHeaders=" + signed_headers
    canonical_querystring += "&language-code="+ language_code +"&media-encoding=" + media_encoding +"&sample-rate=" + sample_rate
 
    # Zero length string for connecting
    payload_hash = hashlib.sha256(("").encode('utf-8')).hexdigest()
 
    canonical_request = method + '\n' \
                        + canonical_uri + '\n' \
                        + canonical_querystring + '\n' \
                        + canonical_headers + '\n' \
                        + signed_headers + '\n' \
                        + payload_hash
 
    string_to_sign = algorithm + "\n" \
                     + amz_date + "\n" \
                     + credential_scope + "\n" \
                     + hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
 
    signing_key = getSignatureKey(secret_access_key, datestamp, region, service)
 
    signature = hmac.new(signing_key, string_to_sign.encode("utf-8"),
                         hashlib.sha256).hexdigest()
 
    canonical_querystring += "&X-Amz-Signature=" + signature
 
    request_url = endpoint + canonical_uri + "?" + canonical_querystring
 
    return request_url
  • main 函数

def main():
    url = create_pre_signed_url("us-east-1", "en-US", "pcm", "16000")
    ws = websocket.create_connection(url, sslopt={"cert_reqs": ssl.CERT_NONE})
 
    _thread.start_new_thread(loop_receiving, (ws,))
    print("Receiving...")
    send_data(ws)
 
    while True:
        time.sleep(1)
main()
  • loop_receiving 函数

该函数位于 main 函数上方。它将接收 Amazon Transcribe Streaming Service 的返回数据,并且打印出来。

def loop_receiving(ws):
    try:
        while True:
            result = ws.recv()
 
            if result == '':
                continue
 
            eventStreamBuffer = EventStreamBuffer()
 
            eventStreamBuffer.add_data(result)
            eventStreamMessage = eventStreamBuffer.next()
 
            stream_payload = eventStreamMessage.payload
 
            transcript = json.loads(bytes.decode(stream_payload, "UTF-8"))
 
            print("response:",transcript)
 
            results = transcript['Transcript']['Results']
            if len(results)>0:
                for length in range(len(results)):
                    if 'IsPartial' in results[length]:
                        print('IsPartial:', results[length]['IsPartial'])
 
                    if 'Alternatives' in results[length]:
                        alternatives = results[length]['Alternatives']
                        if len(alternatives)>0:
                            for sublength in range(len(alternatives)):
                                if 'Transcript' in alternatives[sublength]:
                                    print('Transcript:', alternatives[sublength]['Transcript'])
 
 
    except Exception as e:
        if 'WebSocketConnectionClosedException' == e.__class__.__name__:
            print("Error: websocket connection is closed")
        else:
            print(f"Exception Name: {e.__class__.__name__}")
  • send_data 函数

该函数位于 main 函数上方。它将发送音频数据到 Amazon Transcribe Streaming Service。其中 testFile 变量是测试音频文件地址,测试音频为 pem 格式,英语,采样率为16000。

def send_data(ws):
 
    testFile = "xxx.pem"
 
    bufferSize = 1024*16
 
    stream_headers = {
        ":message-type": "event",
        ":event-type": "AudioEvent",
        ":content-type": "application/octet-stream",
    }
 
    eventstream_serializer = EventStreamMessageSerializer()
 
    with open(testFile, "rb") as source:
        while True:
            audio_chunk = source.read(bufferSize)
            # 将音频数据进行编码
            event_bytes = eventstream_serializer.serialize(stream_headers, audio_chunk)
 
            ws.send(event_bytes, opcode = 0x2) # 0 x 2 send binary
 
            # end with b'' data bytes
            if len(audio_chunk) == 0:
                break

java使用Transcribe演示实例

import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.Upload;
import com.amazonaws.services.transcribe.AmazonTranscribe;
import com.amazonaws.services.transcribe.AmazonTranscribeClientBuilder;
import com.amazonaws.services.transcribe.model.*;
import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
 
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
/**
 * 代码注意: 生成job只保存到S3,所以要保存到本地的思路是先保存到S3,然后下载,
 * 但是期间需要时间,所以不断的查询是否存在在不断的遍历循环比较慢
 *
 * @author DELL
 * @Desc 将本地MP3上传到S3, 然后再转文本,保存json 文件到本地
 * 基本步骤:
 * 1.建立S3客户端连接
 * 2.上传本地音频到S3库中,并返回一个S3地址
 * 3.上传MP3对应的备用单词词库
 * 3.在Amazon Transcribe 创建一个job,将生成的json 文件保存到MP3同级目录
 */
public class Mp3ToJsonUtils {
 
    // 执行文件配置信息
    private static String FILE_TYPE = "mp3";
    // S3配置信息
    private static String AWS_ACCESS_KEY = "自己生成";
    private static String AWS_SECRET_KEY = "自己生成";
    private static final String BUCKET_NAME = "自己生成";
    private static final String JOB_BUCKET_NAME = "自己生成";
    // Aws对象信息
    private static AmazonS3 s3;
    private static TransferManager tx;
    private static AmazonTranscribe amazonTranscribe;
    private static BasicAWSCredentials awsCredentials;
 
    static {
        //1. 建立连接
        try {
            init_with_key();
        } catch (Exception e) {
            e.printStackTrace();
        }
        awsCredentials = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
        amazonTranscribe = AmazonTranscribeClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).withRegion(Regions.US_EAST_2).build();
    }
 
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        mp3TOJosn("C:\\Users\\DELL\\Desktop\\BK测试数据\\A_Cinderella_Atlas_5.mp3", list);
    }
 
    public static void mp32Josn(String inPath, String savePath, List<String> list) throws Exception {
        String jsonPath = new File(inPath).getParent();
        String name = new File(inPath).getName().replaceAll(" ", "_").replaceAll("-", "_");
        File file = new File(savePath + "\\" + name + ".json");
        //确保上传一次
        if (file.exists()) {
            System.out.println(savePath + "--->已经处理完毕,跳过处理");
            return;
        }
        //2. 上传文件到S3库,得到上传文件对应的S3 URL
        String s3Path = uploadFileToBucket(inPath, BUCKET_NAME);
        String key = new File(s3Path).getName();
        key = key.replaceAll(" ", "_").replaceAll("-", "_");
        //3. 创建Transcription jobs
        createJob(JOB_BUCKET_NAME, FILE_TYPE, key, s3Path);
        //4. 将json文件下载到本地和MP3 同目录
        // 在创建job 生成json 的时候需要一定的时间,先等待一段时间,然后判断存在不存在
        boolean flag = isObjectExit(BUCKET_NAME, key + ".json");
        while (!flag) {
            flag = isObjectExit(BUCKET_NAME, key + ".json");
        }
        amazonS3Downloading(s3, JOB_BUCKET_NAME, key + ".json", savePath + "\\" + key + ".json");
    }
 
    /**
     * 将 MP3 转成 Json文件保存到本地
     *
     * @param inPath
     * @throws Exception
     */
    public static void mp3TOJosn(String inPath, List<String> list) throws Exception {
        String jsonPath = new File(inPath).getParentFile().getParentFile().getParentFile().getAbsolutePath() + "\\json";
        File file1 = new File(jsonPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }
        mp32Josn(inPath, jsonPath, list);
    }
 
    /**
     * 通过在代码中包含 access key id 和 secret access key 连接 aws
     *
     * @throws Exception
     */
    private static void init_with_key() throws Exception {
        AWSCredentials credentials = null;
        credentials = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
        s3 = new AmazonS3Client(credentials);
        //Region usWest2 = Region.getRegion(Regions.US_WEST_2);
        //s3.setRegion(usWest2);
        tx = new TransferManager(s3);
    }
 
    /**
     * 上传一个本地文件(对应位置为path)上传到名为bucketName的bucket
     *
     * @param path       需要上传文件的路径
     * @param bucketName S3中存储文件的桶名称
     *                   return 返回对应上传文件的key
     */
    private static String uploadFileToBucket(String path, String bucketName) {
        String keyName = new File(path).getName();
        File fileToUpload = new File(path);
        if (fileToUpload.exists() == false) {
            System.out.println(path + " not exists!");
            return null;
        }
        PutObjectRequest request = new PutObjectRequest(bucketName, fileToUpload.getName(), fileToUpload);
        Upload upload = tx.upload(request);
        while ((int) upload.getProgress().getPercentTransferred() < 100) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println(path + " MP3上传成功!");
        String s3Path = "s3://" + BUCKET_NAME + "/" + keyName;
        return s3Path;
    }
 
    /**
     * 创建一个 Transcription jobs
     *
     * @param bucketName     S3桶的名称
     * @param fileName       文件类型 eg: mp3, mp4
     * @param jobName        要创建的job的名称
     * @param S3Path         对应S3桶中的对应MP3 或其他路径的S3URL
     * @param vocabularyName Custom vocabulary 对应的名称
     */
    protected static void createJob(String bucketName, String fileName, String jobName, String S3Path) {
        StartTranscriptionJobRequest startTranscriptionJobRequest = new StartTranscriptionJobRequest();
        Media media = new Media();
        media.setMediaFileUri(S3Path);
        // 设置JOb的相应参数  sampling rate 采(抽)样率;
        startTranscriptionJobRequest.withMedia(media)
                .withLanguageCode(LanguageCode.EnUS)
                .withMediaFormat(fileName)
                .withOutputBucketName(bucketName)
                .withSettings(settings)
                .setTranscriptionJobName(jobName);
 
        amazonTranscribe.startTranscriptionJob(startTranscriptionJobRequest);
        GetTranscriptionJobRequest request;
        request = new GetTranscriptionJobRequest();
        request.withTranscriptionJobName(jobName);
        GetTranscriptionJobResult result = amazonTranscribe.getTranscriptionJob(request);
        String status = result.getTranscriptionJob().getTranscriptionJobStatus();
        while (!status.toUpperCase().equals("COMPLETED")) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //System.out.println(status);
            result = amazonTranscribe.getTranscriptionJob(request);
            status = result.getTranscriptionJob().getTranscriptionJobStatus();
            if (status.toUpperCase().equals("FAILED")) {
                System.out.println(result.getTranscriptionJob().getTranscriptionJobName() + "---> is failed");
                System.out.println(result.getTranscriptionJob().getTranscriptionJobName() + "--->" + result.getTranscriptionJob().getFailureReason());
                throw new RuntimeException("transcriobe 失败");
            }
        }
        System.out.println(jobName + "Mp3 Job 生成成功");
    }
    /**
     * 将S3上的文件下载到本地
     *
     * @param s3Client s3客户端
     * @param bucketName 桶名称
     * @param key 文件名称
     * @param targetFilePath 本地路径
     */
    public static void amazonS3Downloading(AmazonS3 s3Client, String bucketName, String key, String targetFilePath) {
        S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, key));
        if (object != null) {
            System.out.println("Content-Type: " + object.getObjectMetadata().getContentType());
            InputStream input = null;
            FileOutputStream fileOutputStream = null;
            byte[] data = null;
            try {
                //获取文件流
                input = object.getObjectContent();
                data = new byte[input.available()];
                int len = 0;
                fileOutputStream = new FileOutputStream(targetFilePath);
                while ((len = input.read(data)) != -1) {
                    fileOutputStream.write(data, 0, len);
                }
                System.out.println(targetFilePath + "json 文件下载文件成功");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
 
    /**
     * 判断名为bucketName的bucket里面是否有一个名为key的object
     *
     * @param bucketName
     * @param key
     * @return
     */
    private static boolean isObjectExit(String bucketName, String key) {
        int len = key.length();
        ObjectListing objectListing = s3.listObjects(bucketName);
        String s = new String();
        for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
            s = objectSummary.getKey();
            int slen = s.length();
            if (len == slen) {
                int i;
                for (i = 0; i < len; i++) {
                    if (s.charAt(i) != key.charAt(i)) {
                        break;
                    }
                }
                if (i == len) {
                    return true;
                }
            }
        }
        return false;
    }
}
 

效果演示

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023-11-14 LeetCode每日一题(阈值距离内邻居最少的城市)

2023-11-14每日一题 一、题目编号 1334. 阈值距离内邻居最少的城市二、题目链接 点击跳转到题目位置 三、题目描述 有 n 个城市&#xff0c;按从 0 到 n-1 编号。给你一个边数组 edges&#xff0c;其中 edges[i] [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的…

阿里达摩院开源DAMO-YOLO

1.简介 DAMO-YOLO是一个兼顾速度与精度的目标检测框架&#xff0c;其效果超越了目前的一众YOLO系列方法&#xff0c;在实现SOTA的同时&#xff0c;保持了很高的推理速度。DAMO-YOLO是在YOLO框架基础上引入了一系列新技术&#xff0c;对整个检测框架进行了大幅的修改。具体包括…

c语言从入门到实战——基于指针的数组与指针数组

基于指针的数组与指针数组 前言1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 前言 指针的数组是指数组中的元素都是指针类型&#xff0c;它们指向某种数据类型的变量。 1. 数组名的理解 我们在使用指针…

Excel-快速将公式运用到一整列

先在该列的第一个单元格里写好公式&#xff0c;然后单击该单元格 在图中标示的地方输入我们需要填充的单元格区域 同时按住Ctrl和Enter键&#xff0c;这时需要填充的单元格区域就都被选中了 然后单击一下图中公式的后面&#xff0c;再次按下Ctrl和Enter键&#xff0c;这样就完…

第3章:搜索与图论【AcWing】

文章目录 图的概念图的概念图的分类有向图和无向图 连通性连通块重边和自环稠密图和稀疏图参考资料 图的存储方式邻接表代码 邻接矩阵 DFS全排列问题题目描述思路回溯标记剪枝代码时间复杂度 [N 皇后问题](https://www.luogu.com.cn/problem/P1219)题目描述全排列思路 O ( n ! …

Unity--互动组件(Toggle)

1.组件的可交互 2.组件的过渡状态 3.组件的导航 4.Toggle的属性和参数设置 Toggle 切换控制是一个复选框&#xff0c;允许用户打开或关闭的一个选项&#xff1b; ”Toggle的属性和参数&#xff1a;“” Is on&#xff1a;&#xff08;开启&#xff09; 拨动开关是否从一开…

二叉树基础

前言 我们好久没有更新数据结构的博文了&#xff0c;今天来更新一期树&#xff01;前几期我们已经介绍了顺序表、链表&#xff0c;栈和队列等基本的线性数据结构并对其分别做了实现&#xff0c;本期我们再来介绍一个灰常重要的非线性基本结构 ---- 树型结构。 本期内容介绍 树…

计算机 - - - 浏览器网页打开本地exe程序,网页打开微信,网页打开迅雷

效果 在电脑中安装了微信和迅雷&#xff0c;可以通过在地址栏中输入weixin:打开微信&#xff0c;输入magnet:打开迅雷。 同理&#xff1a;在网页中使用a标签&#xff0c;点击后跳转链接打开weixin:&#xff0c;也会同样打开微信。 运用同样的原理&#xff0c;在网页中点击超…

第3关:集合操作100

任务描述相关知识编程要求测试说明 任务描述 本关任务&#xff1a;使用 集合操作解决实际问题 相关知识 1.集合并操作符 可转换为SQL 若R,S的属性名不同&#xff0c;可使用重命名使相应列名一致后进行并操作 例如&#xff1a;R(A,B,C) S(D,E,F) select A,B from R union sel…

【STM32】串口和printf

1.数据通信的基本知识 1.串行/并行通信 2.单工/半双工/全双工通信 类似于【广播 对讲 电话】 不是有两根线就是全双工&#xff0c;而是输入和输出都有对应的数据线。 3.同步/异步通信 区分同步/异步通信的根本&#xff1a;判断是否有时钟信号&#xff08;时钟线&#xff09;。…

开源维修上门服务小程序SAAS系统源码 带完整搭建教程

在现代生活中&#xff0c;家电设备维修往往是一个耗时且繁琐的过程。消费者需要花费大量时间寻找合适的维修人员&#xff0c;并面临服务质量不稳定的风险。同时&#xff0c;对于维修人员来说&#xff0c;寻找客户和接收订单的过程也十分繁琐。因此&#xff0c;开发一款基于小程…

深入理解JVM虚拟机第二十五篇:详解JVM方法的绑定机制静态绑定和动态绑定,早期绑定晚期绑定,并编写代码从字节码角度证明这件事情

大神链接&#xff1a;作者有幸结识技术大神孙哥为好友&#xff0c;获益匪浅。现在把孙哥视频分享给大家。 孙哥链接&#xff1a;孙哥个人主页 作者简介&#xff1a;一个颜值99分&#xff0c;只比孙哥差一点的程序员 本专栏简介&#xff1a;话不多说&#xff0c;让我们一起干翻J…

【MediaFoundation】相关的概念

MF 概览 Media Foundation 提供了两种不同的编程模型&#xff0c;左边展示的是端到端的媒体数据模型&#xff0c;主要用在&#xff1a;播放URL或者文件&#xff0c;以及控制流。 在图表右侧展示的第二种模型中&#xff0c;应用程序可以从源头拉取数据&#xff0c;也可以将数据…

一文了解VR全景拍摄设备如何选择,全景图片如何处理

引言&#xff1a; 在如今的数字化时代&#xff0c;虚拟现实&#xff08;VR&#xff09;技术不仅为我们的生活增添了许多乐趣&#xff0c;也为摄影领域带来了新的摄影方式&#xff0c;那么VR全景拍摄如何选择设备&#xff0c;全景图片又怎样处理呢&#xff1f; 一. VR全景拍摄设…

uniapp项目笔记

1.生成二维码 import uqrCode from /static/erweima.js uqrCode.make({canvasId: qrcode,componentInstance: this,text: JSON.stringify(item.id),size: 150,margin: 0,backgroundColor: #ffffff,foregroundColor: #000000,fileType: jpg,errorCorrectLevel: uqrCode.errorCor…

高质量实时渲染笔记

文章目录 Real-time shadows1 自遮挡问题2 解决阴影detach问题&#xff1f;3 Aliasing4 近似积分5 percentage closer soft shadows(PCSS)percenta closer filtering(PCF)PCSS的思想 6 Variance Soft Shadow Mapping (VSSM)步骤Moment Shadow Mapping 7 Distance field shadow …

LeetCode - 232.用栈实现队列 225.用队列模拟实现栈 (C语言,配图)

目录 232.用栈实现队列 225.用队列模拟实现栈 注&#xff1a;本文是基于C语言实现的代码&#xff0c;所以栈和队列是在力扣上制造实现的&#xff0c;如果你使用C等语言&#xff0c;可以忽略前面相当大部分的代码。 在栈模拟实现栈和队列之前&#xff0c;我们先来复习一下栈和…

Skywalking流程分析_4(插件的加载和不同版本的识别)

插件的结构 之前我们介绍了插件的加载&#xff0c;接下来就是真正开始进行插件的执行了&#xff0c;首先要看下插件的结构是怎么样的&#xff0c;以阿里的druid数据源为例 skywalking-plugin.def: druid-1.xorg.apache.skywalking.apm.plugin.druid.v1.define.DruidPooledCo…

【PG】PostgreSQL高可用方案repmgr部署(非常详细)

目录 简介 1 概述 1.1 术语 1.2 组件 1.2.1 repmgr 1.2.2 repmgrd 1.3 Repmgr用户与元数据 2 安装部署 2.0 部署环境 2.1 安装要求 2.1.1 操作系统 2.1.2 PostgreSQL 版本 2.1.3 操作系统用户 2.1.4 安装位置 2.1.5 版本要求 2.2 安装 2.2.1 软件包安装 2.2…

使用Filebeat+Kafka+Logstash+Elasticsearch构建日志分析系统

随着时间的积累&#xff0c;日志数据会越来越多&#xff0c;当您需要查看并分析庞杂的日志数据时&#xff0c;可通过FilebeatKafkaLogstashElasticsearch采集日志数据到Elasticsearch中&#xff0c;并通过Kibana进行可视化展示与分析。本文介绍具体的实现方法。 一、背景信息 …