sherpa-onnx实现ASR(语音转文字)和 TTS(文字转语音)
目录
一、什么是 sherpa-onnx
二、核心特点 & 优势
三、架构与工作流程
四、demo
一、什么是 sherpa-onnx
-
sherpa-onnx 是开源项目,由 k2‑fsa 组织维护,定位为 “部署框架”中 sherpa 的一个子项目。 K2 FSA+2K2 FSA+2
-
它的核心目标是:支持“语音处理”相关任务(如:语音识别/ASR、语音合成/TTS、说话人识别/Diarization、VAD、关键词唤醒、语言识别等)在多种设备/平台上本地运行,而 无需联网。 K2 FSA+2GitHub+2
-
它使用的是 ONNX 格式模型 + ONNX Runtime 作为推理引擎,从而具备跨平台、较好兼容性的特点。 K2 FSA+1
-
对比:在官网文档中提到,sherpa(原版)相比于 sherpa-onnx 安装更复杂、平台支持更窄;而 sherpa-onnx 的安装、平台兼容性、部署门槛更低。 K2 FSA+1
二、核心特点 & 优势
在你博客中,这部分可写成“为什么选择 sherpa-onnx”的理由。
-
本地推理,无需联网
如官网所述:“During speech recognition, it does not need to access the Internet. Everything is processed locally on your device.” K2 FSA
对于隐私敏感、网络受限、或实时性要求高的应用场景,优势明显。 -
多平台/多语言绑定支持
支持 x86、x86_64、32/64 bit ARM、RISC-V 等多种 CPU 架构。支持 Linux、macOS、Windows、Android、iOS、嵌入式板卡等。 GitHub+1
同时提供 C++、C、Python、JavaScript、Go、C#、Swift、Rust 等语言接口。 GitHub -
支持多种语音任务
除了典型的语音识别(ASR),还支持:语音合成(TTS)、说话人分离/识别、语音增强、关键词检测、语言识别、音频标签等。 K2 FSA+1 -
丰富的预训练模型
在文档中列出大量预训练模型(ASR、TTS、CTC、Paraformer、Transducer、标签任务等),包括中英、日韩、粤语、俄语等多语言版本。 K2 FSA+1 -
与 ONNX Runtime 结合,部署门槛低
利用 ONNX 模型 + Runtime 加上统一接口,部署更方便。安装方式也比较友好。 K2 FSA+1
三、架构与工作流程
-
模型路径:通常先在研究/训练框架(如 Icefall、TensorFlow、PyTorch)中训练/微调模型,再导出为 ONNX 格式。官方文档提及“Please refer to … export-onnx.html for how to export models to ONNX format.” K2 FSA
-
推理框架:使用 ONNX Runtime 作为推理后端。sherpa-onnx 封装了一定的预处理、后处理逻辑,以及语音任务专用的 “解码器” 逻辑(如 Transducer joiner 、CTC 、Paraformer 等)
-
多任务结构:
-
对于 流式 ASR(Streaming),模型通常是 transducer/zipformer 对应结构,要求低延迟。
-
对于 非流式/离线 ASR,则可使用更大的模型、处理完整语音文件。
-
对于 TTS/增强/标签任务,流程类似:音频或文本输入 → 模型推理 → 输出。
-
-
部署-平台适配:sherpa-onnx 提供跨平台代码、语言绑定、以及针对嵌入式(如 ARM、RISC-V)或移动端(Android、iOS)优化。参见 “Build for iOS/Android” 文档。 K2 FSA+1
-
典型应用流程(ASR)大致如下:
-
音频采集(或 WAV / microphone)
-
VAD / 分段(可选)
-
前处理(feature extraction, e.g. fbank)
-
模型推理(ONNX 模型)
-
解码(transducer joiner/CTC)
-
后处理(比如去重、拼接、标点、大小写)
-
输出文本/标识说话人/标签等
-
四、demo
import wave
import numpy as np
import sherpa_onnx
from flask import Flask, request, jsonify
import ioapp = Flask(__name__)encoder_model_path = '/home/project_python/whisper/sherpa-onnx-streaming-zipformer-multi-zh-hans-2023-12-12/encoder-epoch-20-avg-1-chunk-16-left-128.onnx'
decoder_model_path = '/home/project_python/whisper/sherpa-onnx-streaming-zipformer-multi-zh-hans-2023-12-12/decoder-epoch-20-avg-1-chunk-16-left-128.onnx'
joiner_model_path = '/home/project_python/whisper/sherpa-onnx-streaming-zipformer-multi-zh-hans-2023-12-12/joiner-epoch-20-avg-1-chunk-16-left-128.onnx'
tokens_path = '/home/project_python/whisper/sherpa-onnx-streaming-zipformer-multi-zh-hans-2023-12-12/tokens.txt'
recognizer = sherpa_onnx.OnlineRecognizer.from_transducer(encoder=encoder_model_path,decoder=decoder_model_path,joiner=joiner_model_path,tokens=tokens_path,sample_rate=16000,feature_dim=80,provider="cpu"
)def read_wave(wave_data):"""Args:wave_data:Bytes of a wave file. It should be single channel and each sample shouldbe 16-bit. Its sample rate does not need to be 16kHz.Returns:Return a tuple containing:- A 1-D array of dtype np.float32 containing the samples, which arenormalized to the range [-1, 1].- sample rate of the wave file"""with wave.open(io.BytesIO(wave_data), 'rb') as f:assert f.getnchannels() == 1, f.getnchannels()assert f.getsampwidth() == 2, f.getsampwidth() # it is in bytesnum_samples = f.getnframes()samples = f.readframes(num_samples)samples_int16 = np.frombuffer(samples, dtype=np.int16)samples_float32 = samples_int16.astype(np.float32)samples_float32 = samples_float32 / 32768return samples_float32, f.getframerate()@app.route('/transcribe', methods=['POST'])
def transcribe():if 'file' not in request.files:return jsonify({'error': 'No file part'}), 400file = request.files['file']if file.filename == '':return jsonify({'error': 'No selected file'}), 400if file:wave_data = file.read()samples, sample_rate = read_wave(wave_data)streams = []s = recognizer.create_stream()s.accept_waveform(sample_rate, samples)tail_paddings = np.zeros(int(0.66 * sample_rate), dtype=np.float32)s.accept_waveform(sample_rate, tail_paddings)s.input_finished()streams.append(s)while True:ready_list = []for s in streams:if recognizer.is_ready(s):ready_list.append(s)if len(ready_list) == 0:breakrecognizer.decode_streams(ready_list)results = [recognizer.get_result(s) for s in streams]return jsonify({'results': results})if __name__ == '__main__':app.run(host='0.0.0.0', port=8079)
参考文献:https://github.com/k2-fsa/sherpa-onnx
