CAM++ API接口怎么写?Flask封装代码实例
1. 为什么需要API接口?
你已经用上了科哥开发的CAM++说话人识别系统,界面友好、功能清晰,点点鼠标就能完成语音比对和特征提取。但如果你是个开发者,或者想把这套能力集成到自己的项目里——比如做个声纹登录系统、智能门禁、语音客服身份核验——那光靠网页操作就不够用了。
这时候,你就需要一个API接口。
API(Application Programming Interface)就像是给这个系统装了个“遥控器”,让你不用打开浏览器,也能远程调用它的核心能力:说话人验证、特征提取。而Flask,就是我们用来打造这个遥控器的工具。
本文不讲大道理,只干一件事:手把手教你如何用Flask把CAM++的功能封装成可调用的HTTP接口,并提供完整可运行的代码示例。小白也能看懂,照着做就能用。
2. 系统能力拆解:我们能封装什么?
在动手前,先搞清楚CAM++到底能做什么。从用户手册来看,它有两个核心功能:
- 说话人验证:输入两段音频,判断是不是同一个人
- 特征提取:输入一段音频,输出192维的Embedding向量
我们的目标是:让别人通过发个HTTP请求,就能拿到这两个结果。
比如这样:
curl -X POST http://localhost:5000/verify \ -F "audio1=@voice1.wav" \ -F "audio2=@voice2.wav"返回:
{ "similarity": 0.8523, "is_same_speaker": true, "threshold": 0.31 }这才是真正的“工程化落地”。
3. Flask快速入门:三步搭起服务架子
Flask是一个轻量级Python Web框架,几行代码就能启动一个Web服务。我们不需要复杂的前端,只要能接收文件、调用模型、返回JSON就行。
3.1 安装依赖
确保你已经在系统中安装了Flask:
pip install flask3.2 最简服务示例
先写个最简单的“Hello World”服务,确认环境没问题:
from flask import Flask app = Flask(__name__) @app.route('/') def home(): return "CAM++ API服务已启动!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)保存为app.py,运行:
python app.py访问http://你的IP:5000,看到文字说明服务起来了。
4. 封装特征提取接口:上传音频 → 返回Embedding
我们先实现第一个功能:上传一段音频,返回192维特征向量。
4.1 接口设计
- URL:
/extract - 方法: POST
- 参数: 音频文件
audio - 返回: JSON格式的Embedding数组
4.2 核心代码实现
from flask import Flask, request, jsonify import numpy as np import os from werkzeug.utils import secure_filename # 假设你已经有了CAM++的推理函数 # 这里用mock函数模拟实际调用 def extract_embedding(audio_path): """ 模拟调用CAM++模型提取特征 实际使用时替换为真实模型加载和推理代码 """ # 加载预训练模型(伪代码) # model = load_model('/root/speech_campplus_sv_zh-cn_16k/model.pth') # 读取音频并预处理 # audio_data = read_audio(audio_path, sample_rate=16000) # 推理得到192维向量 # embedding = model.inference(audio_data) # 这里用随机数代替真实输出,仅用于演示 np.random.seed(42) # 固定种子,保证每次结果一致 embedding = np.random.randn(192).astype(np.float32) return embedding.tolist() # 转为Python list以便JSON序列化 app = Flask(__name__) app.config['UPLOAD_FOLDER'] = '/tmp/audio_uploads' os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) @app.route('/extract', methods=['POST']) def api_extract(): if 'audio' not in request.files: return jsonify({'error': '缺少音频文件'}), 400 file = request.files['audio'] if file.filename == '': return jsonify({'error': '未选择文件'}), 400 # 保存上传的文件 filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(filepath) try: # 调用特征提取函数 embedding = extract_embedding(filepath) # 清理临时文件(可选) # os.remove(filepath) return jsonify({ 'success': True, 'filename': filename, 'embedding_dim': 192, 'embedding': embedding }) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)4.3 如何测试?
保存为extract_api.py,运行后执行:
curl -X POST http://localhost:5000/extract \ -F "audio=@/path/to/your/audio.wav"你会看到类似这样的返回:
{ "success": true, "filename": "test.wav", "embedding_dim": 192, "embedding": [0.4967, -0.1382, ..., 0.2345] }注意:当前
extract_embedding是模拟函数。要真正跑通,你需要把CAM++的模型加载和推理逻辑接入进来,这部分可以参考ModelScope官方文档或科哥提供的脚本。
5. 封装说话人验证接口:比对两段语音
接下来实现更实用的功能:上传两段音频,返回相似度和判定结果。
5.1 接口设计
- URL:
/verify - 方法: POST
- 参数:
audio1,audio2,threshold(可选) - 返回: 相似度分数 + 是否同一人
5.2 完整代码实现
from flask import Flask, request, jsonify import numpy as np import os from werkzeug.utils import secure_filename # 模拟特征提取函数(实际应对接真实模型) def extract_embedding(audio_path): np.random.seed(hash(os.path.basename(audio_path)) % (2**32)) # 不同文件不同结果 return np.random.randn(192).astype(np.float32) def cosine_similarity(emb1, emb2): emb1_norm = emb1 / np.linalg.norm(emb1) emb2_norm = emb2 / np.linalg.norm(emb2) return np.dot(emb1_norm, emb2_norm) app = Flask(__name__) app.config['UPLOAD_FOLDER'] = '/tmp/audio_uploads' os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) @app.route('/verify', methods=['POST']) def api_verify(): if 'audio1' not in request.files or 'audio2' not in request.files: return jsonify({'error': '请上传两个音频文件'}), 400 file1 = request.files['audio1'] file2 = request.files['audio2'] if file1.filename == '' or file2.filename == '': return jsonify({'error': '文件名不能为空'}), 400 threshold = float(request.form.get('threshold', 0.31)) # 默认阈值0.31 # 保存文件 path1 = os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file1.filename)) path2 = os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file2.filename)) file1.save(path1) file2.save(path2) try: # 提取特征 emb1 = extract_embedding(path1) emb2 = extract_embedding(path2) # 计算相似度 similarity = cosine_similarity(emb1, emb2) # 判断是否同一人 is_same = bool(similarity > threshold) # 删除临时文件(生产环境可配置保留) # os.remove(path1) # os.remove(path2) return jsonify({ 'similarity': round(float(similarity), 4), 'threshold': threshold, 'is_same_speaker': is_same, 'message': '是同一人' if is_same else '不是同一人' }) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)5.3 测试命令
curl -X POST http://localhost:5000/verify \ -F "audio1=@speaker1_a.wav" \ -F "audio2=@speaker1_b.wav" \ -F "threshold=0.31"返回示例:
{ "similarity": 0.8523, "threshold": 0.31, "is_same_speaker": true, "message": "是同一人" }6. 生产优化建议:让API更稳定好用
上面的代码能跑通,但在真实场景中还需要一些改进。
6.1 文件类型校验
防止用户上传非音频文件:
def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'wav', 'mp3', 'm4a', 'flac'} # 在接收文件时检查 if not allowed_file(file.filename): return jsonify({'error': '不支持的文件格式'}), 4006.2 音频格式统一处理
推荐使用pydub自动转码为16kHz WAV:
pip install pydubfrom pydub import AudioSegment def convert_to_wav(input_path, output_path): audio = AudioSegment.from_file(input_path) audio = audio.set_frame_rate(16000).set_channels(1) audio.export(output_path, format='wav')6.3 错误码规范化
定义统一错误码,便于前端处理:
ERRORS = { 1001: "缺少音频文件", 1002: "文件格式不支持", 1003: "音频处理失败", 1004: "模型推理异常" }6.4 日志记录
加日志方便排查问题:
import logging logging.basicConfig(level=logging.INFO) app.logger.info(f"接收到验证请求: {file1.filename} vs {file2.filename}")7. 总结:你已经拥有了一个可用的声纹API服务
我们一步步完成了以下工作:
- 理解了CAM++的核心能力
- 用Flask搭建了Web服务基础
- 实现了特征提取和说话人验证两个关键API
- 提供了完整的可运行代码
- 给出了生产级优化建议
你现在可以把这个API集成到任何系统中:
- App端上传录音做身份核验
- 后台批量处理历史录音做聚类分析
- 和数据库结合构建声纹库
- 作为微服务部署在Docker中
记住:技术的价值不在于多复杂,而在于能不能解决问题。这个小小的Flask服务,可能就是你下一个AI产品的起点。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。