声纹识别
功能介绍
通过声音识别说话人身份,仅需采集三段说话人音频注册到声纹库,即可通过1:1、1:N产品实现声纹识别。
音频要求
- 音频时长:最佳 10 秒,最小 1 秒,最大 30 秒
- 支持音频格式:pcm
- 音频采样率:16000Hz
- 位深:16bit
- 声道:单声道
使用方法
1. 创建账号和应用,详见 平台新手指引 ,通过 标贝开放平台 应用/服务获取client_id,client_secret
2. 发送请求获取access_token,详见 获取访问令牌
3. 获取token后,发送创建声纹库请求:按照请求说明发送请求,具体参数详见 创建声纹id。
4. 获取声纹库注册id后,发送声纹注册请求,按照请求说明发送请求,具体参数详见 声纹注册。
5. 注册声纹三次成功后,依据需求进行1:1或1:N验证,发送1:1或1:N验证,具体参数详见1:1请求说明、1:N请求说明 。
创建声纹特征id
功能介绍:对声纹数据进行存储和管理,调用接口返回可用的声纹特征id
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/createid
请求参数
调用接口返回可用的声纹特征id
请求参数采用 json 方式,Content-Type 为 application/json
params字段说明
参数名称 | 类型 | 是否必填项 | 说明 |
---|---|---|---|
access_token | string | yes | 通过 client_id,client_secret 调用授权服务获得见 获取访问令牌 |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/createid -d '{"access_token" : "eyJhb......"}'
响应结果
Content-Type 为 application/json
返回数据为 json 格式,err_msg 字段为 SUCCESS 表示调用成功
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | Int | 错误码 |
log_id | String | 日志标识 |
registerid | String | 声纹特征 id |
返回示例
{ "err_msg": "SUCCESS", "log_id": "1637151763693260", "err_no": 90000, "registerid": "1760157933973465631" }
声纹注册
功能介绍: 保存基础对比音频,最少需要调用 3 次该接口完成注册过程,当某次注册返回失败时,需要重新提交注册,直到注册完成
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/register最少需要调用 3 次该接口完成注册过程,当某次注册返回失败时,需要重新提交注册,直到注册完成
请求参数
请求参数采用 json 方式,Content-Type 为 application/json
params字段说明
参数名称 | 类 型 | 是否必填 | 说明 |
---|---|---|---|
access_token | String | 是 | 通过client_id,client_secret调用授权服务获得见获取访问令牌 |
format | String | 是 | pcm |
audio | String | 是 | 音频数据 base64(单声道,采样率16K,位深16 位,pcm音频,最短1秒,最长30秒) |
registerId | String | 是 | 调用创建声纹id接口获取 |
name | String | 是 | 自定义名字 |
scoreThreshold | Float | 是 | 注册有效分数,不得低于系统默认值15.0,推荐值30.0 |
vad | Int | 否 |
是否打开vad(语音端点检测)处理,默认为关闭 打开vad功能可以提升声纹识 别准确度,但会增加响应时间 1:打开 0:关闭 |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/register -d '{ "access_token": "eyJ...", "format":"wav", "audio":"AAA", "registerId":"5ae...","name":"testa", "scoreThreshold":65.0 } '
响应结果
Content-Type 为 application/json
返回数据为 json 格式,err_msg 字段为 错误信息
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | Int | 错误码 |
log_id | String | 日志标识 |
suc_num | Int | 注册成功次数,为 3 时表示完成注册 |
返回示例
{ "err_msg": "SUCCESS", "log_id": "1637152591998223", "err_no": 90000, "suc_num": 1 }
声纹验证(1:1)
功能介绍: 上传音频与已其中一个声纹库中的音频特征比对,返回是否匹配
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/match
上传音频与已存在特征比对,返回是否匹配
请求参数
请求参数采用 json 方式,Content-Type 为 application/json
params 字段说明
参数名称 | 类型 | 是否必填项 | 说明 |
---|---|---|---|
access_token | String | 是 | 通过 client_id,client_secret 调用授权服务获得见 获取访问令牌 |
format | String | 是 | pcm |
audio | String | 是 | 音频数据 base64(单声道,采样率16K,位深16 位,pcm音频,最短1秒,最长30秒) |
matchId | String | 是 | 待匹配声纹id |
scoreThreshold | Float | 是 | 比对分数设置,得分高于该数值则比对成功,不得低于系统默认值5.0,推荐值15.0 |
vad | Int | 否 |
是否打开vad(语音端点检测)处理,默认为关闭 打开vad功能可以提升声纹识 别准确度,但会增加响应时间 1:打开 0:关闭 |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/match -d '{ "access_token": "eyJ... ", "format":"pcm", "audio":"AAAAAA", "scoreThreshold":60.0, "matchId":"be0..." }'
响应结果
Content-Type 为 application/json
返回数据为 json 格式,err_msg 字段为 错误信息
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | Int | 错误码 |
log_id | String | 日志标识 |
matchStatus | Int | 1 表示比对成功,0 表示比对失败 |
score | String | 比对分数 |
{ "matchStatus": 0, "err_msg": "SUCCESS", "log_id": "1639473151396592", "err_no": 90000, "score": 1.5 }
查询声纹状态码
功能介绍: 查询声纹注册是否成功
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/status
请求参数
请求参数采用 json 方式,Content-Type 为 application/json
params 字段说明
参数名称 | 类型 | 是否必填 | 说明 |
---|---|---|---|
access_token | String | yes | 通过 client_id,client_secret 调用授权服务获得见 获取访问令牌 |
registerId | String | yes | 调用创建声纹id接口获取 |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/status -d '{"access_token" : "eyJ... ","registerId":"53a..."}'
响应结果
content_type 为 application/json
返回数据为 json 格式,err_msg 字段为 错误信息
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | Int | 日志标识 |
log_id | String | 错误码 |
status | Int | 声纹注册次数,3:注册成功,0:未注册 |
返回示例
{ "err_msg": "SUCCESS", "log_id": "1637152702700241", "err_no": 90000, "status": 3 }
删除声纹
功能介绍: 删除已注册的声纹
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/delete请求参数
请求参数采用 json 方式,Content-Type 为 application/json
params 字段说明
参数名称 | 类型 | 是否必填 | 说明 |
---|---|---|---|
access_token | String | yes | 通过 client_id,client_secret 调用授权服务获得见 获取访问令牌 |
registerId | String | yes | 待匹配声纹id |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/delete -d '{"access_token" : "eyJ... ","registerId":"53a..."}'
响应结果
content_type 为 application/json
返回数据为 json 格式,err_msg 字段为 错误信息
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | Int | 错误码 |
log_id | String | 日志标识 |
status | Int | 删除声纹的注册状态,3:注册成功,0:未注册 |
响应示例
{ "err_msg": "SUCCESS", "log_id": "1639473151396592", "err_no": 90000, "status": 1 }
声纹对比(1:N)
功能介绍: 上传音频,比对库中所有音频特征,返回匹配列表
接口类型: REST API
服务接口
https://openapi.data-baker.com/vpr/search用户上传音频,比对库中所有特征,返回匹配的特征列表
请求参数
请求参数采用 json 方式,Content-Type 为 application/json
params 字段说明
参数名称 | 类型 | 是否必填 | 说明 |
---|---|---|---|
access_token | String | 是 | 通过 client_id,client_secret 调用授权服务获得见 获取访问令牌 |
format | String | 是 | pcm |
audio | String | 是 | 音频数据 base64(单声道,采样率16K,位深16 位,pcm音频,最短1秒,最长60秒) |
scoreThreshold | float | 是 | 比对分数设置,得分高于该数值则比对成功,不得低于系统默认值15.0,推荐值30.0 |
listNum | Int | 是 | 比对返回声纹条数 |
vad | Int | 否 |
是否打开vad(语音端点检测)处理,默认为关闭 打开vad功能可以提升声纹识 别准确度,但会增加响应时间 1:打开 0:关闭 |
请求示例
curl -X POST -H"Content-Type:application/json" https://openapi.data-baker.com/vpr/search -d '{"access_token" : "eyJ...","format" : "pcm","audio" : "AAAAAA","scoreThreshold":30.0,"listNum":5}'
响应结果
content_type 为 application/json
返回数据为 json 格式,err_msg 字段为 错误信息
参数名称 | 类型 | 说明 |
---|---|---|
err_msg | String | 错误信息 |
err_no | String | 错误码 |
log_id | String | 日志标识 |
matchList | Array | 返回结果数据 |
matchList参数
参数名称 | 类型 | 说明 |
---|---|---|
pkid | String | 匹配到的声纹特征 id |
score | float | 比对的分数 |
name | String | 声纹关联名字 |
返回示例
{ "log_id": "1637153028866860", "err_msg": "SUCCESS", "matchList": [ { "spkid": "3bc084c0-a0ea-4fe2-9b1b-358c74b21c8b", "score": 63.936468236333987, "name": "1" }, { "spkid": "70532bab-0cce-4d23-af07-4f18bb94b928", "score": 61.937236514386112, "name": "2" }, { "spkid": "7dbf40f6-db6c-4b35-9740-387e3bdbe657", "score": 59.941546517333543, "name": "3" } ], "err_no": 90000 }
JAVA示例代码
package com.databaker.web.asr; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import java.io.File; import java.io.FileInputStream; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 声纹识别RESTFUL API接口调用示例 * 附:声纹识别RESTFUL API文档 【https://www.data-baker.com/specs/file/vpr_api_restful】 * * 注意: * 1.仅作为demo示例,失败重试、token过期重新获取、日志打印等优化工作需要开发者自行完成 * 2.此demo将6个接口一次性封装,请参照接口文档,使用时只需在main中逐一解开方法注释,并补充方法中的参数值即可 * * @author data-baker */ public class VoiceprintRecognitionDemo { /** * 授权:需要在开放平台获取【https://ai.data-baker.com/】 */ private static final String clientId = ""; private static final String clientSecret = ""; /** * 获取token的地址信息 */ public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s"; /** 创建声纹ID API地址 */ private static String createUrl = "https://openapi.data-baker.com/vpr/createid"; /** 声纹注册API地址 */ private static String registUrl = "https://openapi.data-baker.com/vpr/register"; /** 查询声纹状态码API地址 */ private static String queryStatusUrl = "https://openapi.data-baker.com/vpr/status"; /** 删除声纹API地址 */ private static String deleteUrl = "https://openapi.data-baker.com/vpr/delete"; /** 声纹验证(1:1)API地址 */ private static String matchUrl = "https://openapi.data-baker.com/vpr/match"; /** 声纹对比(1:N)API地址 */ private static String searchUrl = "https://openapi.data-baker.com/vpr/search"; public static void main(String[] args){ try { /** 创建声纹ID */ // doCreateLibrary(); /** 声纹注册 */ // doRegist(); /** 查询声纹状态码 */ // doQueryStatus(); /** 删除声纹 */ // doDelete(); /** 声纹验证(1:1) */ // doMatch(); /** 声纹验证(1:N) */ // doSearch(); } catch (Exception e) { e.printStackTrace(); } } public static void doCreateLibrary() { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); String rightResultdesc = "创建声纹库完成,结果信息"; String wrongResultdesc = "创建声纹库失败,结果信息"; sendReqUtil(params,createUrl,rightResultdesc,wrongResultdesc); } public static void doRegist() throws Exception { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); params.put("format","pcm"); params.put("audio",encodeBase64File("")); //获取音频数据地址 params.put("registerId",""); //声纹库id params.put("name","声纹注册测试"); //自定义名字 params.put("scoreThreshold", 65.0); //注册有效分数 String rightResultdesc = "声纹注册完成,结果信息"; String wrongResultdesc = "声纹注册失败,错误信息"; sendReqUtil(params,registUrl,rightResultdesc,wrongResultdesc); } private static void doQueryStatus() { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); params.put("registerId",""); //声纹库id String rightResultdesc = "查询声纹状态码完成,结果信息"; String wrongResultdesc = "查询声纹状态码失败,结果信息"; sendReqUtil(params,queryStatusUrl,rightResultdesc,wrongResultdesc); } private static void doDelete() { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); params.put("registerId",""); //声纹库id String rightResultdesc = "删除声纹完成,结果信息"; String wrongResultdesc = "删除声纹失败,结果信息"; sendReqUtil(params,deleteUrl,rightResultdesc,wrongResultdesc); } public static void doMatch() throws Exception { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); params.put("format","pcm"); params.put("audio",encodeBase64File("")); //获取音频数据地址 params.put("scoreThreshold",30.0); //分数阈值 params.put("matchId",""); //声纹库id String rightResultdesc = "声纹1:1验证完成,结果信息"; String wrongResultdesc = "声纹1:1验证失败,结果信息"; sendReqUtil(params,matchUrl,rightResultdesc,wrongResultdesc); } public static void doSearch() throws Exception { Map<String,Object> params = new HashMap<>(); params.put("access_token",getAccessToken()); params.put("format","pcm"); params.put("audio",encodeBase64File("")); //获取音频数据地址 params.put("scoreThreshold",30.0); //分数阈值 params.put("listNum",5); String rightResultdesc = "声纹1:N验证完成,结果信息"; String wrongResultdesc = "声纹1:N验证失败,结果信息"; sendReqUtil(params,searchUrl,rightResultdesc,wrongResultdesc); } public static String encodeBase64File(String path) throws Exception { File file = new File(path); FileInputStream inputFile = new FileInputStream(file); byte[] buffer = new byte[(int) file.length()]; inputFile.read(buffer); inputFile.close(); return Base64.getEncoder().encodeToString(buffer); } private static void sendReqUtil(Map<String,Object> params,String url,String rightResultdesc,String wrongResultdesc){ try { OkHttpClient client = new OkHttpClient().newBuilder() .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .build(); MediaType mediaType = MediaType.parse("application/json"); RequestBody body = RequestBody.create(JSONObject.toJSONString(params),mediaType); //构造request Request request = new Request.Builder() .url(url) .method("POST", body) .addHeader("Content-Type", "application/json") .build(); Response response = null; response = client.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = JSON.parseObject(response.body().string()); System.out.println(rightResultdesc + ": " + (jsonObject == null ? "" : jsonObject)); } else { System.out.println(wrongResultdesc + ": " + response.body().string()); } } catch (Exception e) { e.printStackTrace(); } } public static String getAccessToken() { String accessToken = ""; OkHttpClient client = new OkHttpClient(); String url = String.format(tokenUrl, clientSecret, clientId); Request request = new Request.Builder().url(url).build(); JSONObject jsonObject; try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { //解析 String resultJson = response.body().string(); jsonObject = JSON.parseObject(resultJson); accessToken = jsonObject.getString("access_token"); } } catch (Exception e) { e.printStackTrace(); } return accessToken; } }
Python示例代码
import requests import json import argparse import base64 import os import time class VoicePrint: # 获取声纹id def get_voiceprint_id(self, access_token): url = "https://openapi.data-baker.com/vpr/createid" headers = {'Content-Type': 'application/json'} data = {'access_token': access_token} response = requests.post(url, data=json.dumps(data), headers=headers) register_id = json.loads(response.text).get("registerid") if json.loads(response.text).get('err_no') == 90000: return register_id else: raise Exception # 声纹注册 def voiceprint_register(self, audio, access_token, name, format, registerId): url = 'https://openapi.data-baker.com/vpr/register' headers = {'Content-Type': 'application/json'} data = {'access_token': access_token, 'format': format, 'audio': audio, 'registerId': registerId, 'name': name, 'scoreThreshold': 30.0} response = requests.post(url, data=json.dumps(data), headers=headers) if json.loads(response.text).get('err_no') == 90000: return True else: print(response.text) return False # 查询声纹状态码 def check_voiceprint_status(self, access_token, register_id): url = 'https://openapi.data-baker.com/vpr/status' headers = {'Content-Type': 'application/json'} data = {'access_token': access_token, 'registerId': register_id} response = requests.post(url, data=json.dumps(data), headers=headers) if json.loads(response.text).get('err_no') == 90000: if json.loads(response.text).get('status') == 3: return "register successfully" else: return "register failed" else: raise Exception # 声纹删除 def remove_voiceprint(self, access_token, registerId): url = 'https://openapi.data-baker.com/vpr/delete' headers = {'Content-Type': 'application/json'} data = {'access_token': access_token, 'registerId': registerId} response = requests.post(url, data=json.dumps(data), headers=headers) if json.loads(response.text).get('err_no') == 90000: return "remove successfully" else: return "remove failed" # 声纹对比 def verification_1VN(self, access_token, file): url = 'https://openapi.data-baker.com/vpr/search' headers = {'Content-Type': 'application/json'} data = {'access_token': access_token, 'format': args.format, 'audio': file, 'listNum': 10, 'scoreThreshold': 65.0} response = requests.post(url, data=json.dumps(data), headers=headers) return response # 声纹认证 def verification_1V1(self, access_token, file, matchId): url = 'https://openapi.data-baker.com/vpr/match' headers = {'Content-Type': 'application/json'} data = {'access_token': access_token, 'format': 'pcm', 'audio': file, 'matchId': matchId, 'scoreThreshold': 65.0} response = requests.post(url, data=json.dumps(data), headers=headers) return response # 获取access_token用于鉴权 def get_access_token(client_secret, client_id): grant_type = "client_credentials" url = "https://openapi.data-baker.com/oauth/2.0/token?grant_type={}&client_secret={}&client_id={}".format( grant_type, client_secret, client_id) response = requests.post(url) access_token = json.loads(response.text).get('access_token') return access_token # 获取命令行输入参数 def get_args(): parser = argparse.ArgumentParser(description='VR') parser.add_argument('-client_secret', type=str, required=True) parser.add_argument('-client_id', type=str, required=True) parser.add_argument('-file_path', type=str, required=True) parser.add_argument('-name', type=str, required=True) parser.add_argument('--format', type=str, required=False, default='pcm') parser.add_argument('--scoreThreshold', type=float, required=False, default=65.0) args = parser.parse_args() return args if __name__ == '__main__': try: args = get_args() vp = VoicePrint() # 获取access_token client_secret = args.client_secret client_id = args.client_id file_path = args.file_path access_token = get_access_token(client_secret, client_id) # 创建声纹特征id registerId = vp.get_voiceprint_id(access_token) num_of_registered_audio = 0 for root, dirs, files in os.walk(file_path): for file in files: with open(os.path.join(file_path, file), 'rb') as f: audio = str(base64.b64encode(f.read()), encoding='utf-8') # 声纹注册 if vp.voiceprint_register(audio, access_token, args.name, args.format, registerId): num_of_registered_audio += 1 if num_of_registered_audio >= 3: print("register successfully") break time.sleep(1) with open('data/1.wav', 'rb') as f: file = str(base64.b64encode(f.read()), encoding='utf-8') # 声纹对比(1 V N) response = vp.verification_1VN(access_token, file) print(response.text) # 声纹验证(1 V 1) # response = vp.verification_1V1(access_token, file, registerId) # print(response.text) # 查询声纹注册状态 # print(vp.check_voiceprint_status(access_token, registerId)) # 删除指定声纹 # print(vp.remove_voiceprint(access_token, registerId)) print('finished') except Exception as e: print(e)
命令行执行
python voiceprint.py -client_secret=您的client_secret -client_id=您的client_id -file_path=文件夹路径 -name=声纹名称
错误码
错误码分类
err_no | 描述 |
---|---|
100xx | 请求参数错误 |
200xx | 链接服务器错误 |
300xx | 业务调用错误 |
400xx | 引擎内部错误 |
500xx | 授权相关错误 |
详细错误码
err_no | 描述 |
---|---|
90000 | 成功 |
10001 | 请求参数错误 |
10002 | 请求体格式错误 |
10003 | 请求头错误 |
20001 | 数据库错误 |
20002 | 资源请求错误 |
20003 | REDIS数据库错误 |
30001 | 语音过长错误 |
30002 | 语音过短错误 |
30003 | 语音数据错误 |
30004 | 调用数量已达上限 |
30005 | 并发超出限制 |
30006 | 声纹ID未找到 |
30007 | 声纹ID不属于该账户 |
30008 | 声纹ID已经注册 |
30009 | 声纹ID未注册 |
30010 | 声纹得分低于阈值 |
30011 | 得分阈值设置低于默认值 |
30012 | 声纹数量已达上限 |
40001 | 创建声纹错误 |
40002 | 提取特征错误 |
40003 | 声纹匹配错误 |
50001 | 校验token错误 |
50002 | token无效错误 |