uniapp - 接入科大讯飞语音评测

欢迎关注微信公众号:FSA全栈行动 👋

一、简介

科大讯飞语音评测可以对字、词、句、篇章等题型进行多维度评分(准确度、流畅度、完整度、声韵调型等),支持中文和英文。最新的流式版使用 webSocket 调用接口,开发者可以边录音边上边音频数据(录音与评测同时进行),可以缩短用户等待评测结果的时间,大大提高用户体验。

  • 语音评测官方介绍:https://www.xfyun.cn/services/ise
  • 语音评测(流式版)API 文档:https://www.xfyun.cn/doc/Ise/IseAPI.html

科大讯飞语音评测提供了多种平台的 SDK 与 Demo,但是没有提供微信小程序版本的 SDK 与 Demo,不过,科大讯飞语音评测【WebApi】有提供浏览器版本的 Demo,理论上可以对它进行适当的改造,就能运行到微信小程序环境下,所以本文的主要内容就是对科大讯飞语音评测【WebApi】的浏览器版本 Demo 进行改造和封装。

温馨提示:着急伸手拿最终成品的,可以直接滚到 【文章末尾】 查看。

二、分析

  • API 文档调用示例:https://www.xfyun.cn/doc/Ise/IseAPI.html#调用示例
  • 语音评测流式 API demo js 语言:https://xfyun-doc.cn-bj.ufileos.com/static/16546792913730431/ise_ws_js_demo.zip

打开科大讯飞语音评测 API 文档页面,在 调用示例 章节处,找到 语音评测流式API demo js语言 并下载,解压后使用 vscode 打开,查看 index.js,可以看到开头有如下注释:

// 1. websocket连接:判断浏览器是否兼容,获取websocket url并连接,这里为了方便本地生成websocket url
// 2. 获取浏览器录音权限:判断浏览器是否兼容,获取浏览器录音权限,
// 3. js获取浏览器录音数据
// 4. 将录音数据处理为文档要求的数据格式:采样率16k或8K、位长16bit、单声道;该操作属于纯数据处理,使用webWork处理
// 5. 根据要求(采用base64编码,每次发送音频间隔40ms,每次发送音频字节数1280B)将处理后的数据通过websocket传给服务器,
// 6. 实时接收websocket返回的数据并进行处理// ps: 该示例用到了es6中的一些语法,建议在chrome下运行

通过这个注释说明,可以直观的了解到该 js demo 的大致流程,下面会按照注释里的顺序,逐个分析,并对应到微信小程序里的实现。

注意:因为本人使用的是 uniapp 开发,所以以下关于微信小程序代码实现的部分,并非 传统的 js + wxss + wxml,而是 vue3 + typescript

1、创建 webSocket

官方 demo 是运行在浏览器环境下的,而不同的浏览器对 webSocket 的创建方式不太一样,所以这里做兼容:

// 连接websocket
connectWebSocket() {return getWebSocketUrl().then(url => {let iseWSif ('WebSocket' in window) {iseWS = new WebSocket(url)} else if ('MozWebSocket' in window) {iseWS = new MozWebSocket(url)} else {alert('浏览器不支持WebSocket')return}this.webSocket = iseWSiseWS.onopen = e => {...}iseWS.onmessage = e => {...}iseWS.onerror = e => {...}iseWS.onclose = e => {...}})
}

微信小程序内创建 webSocket 就很简单了,使用 uni.connectSocket 即可:

/* socket相关 */
private socketTask: UniApp.SocketTask | null = null;async connect() {const url = await this.getWebSocketUrl();const newUrl = encodeURI(url);this.socketTask = uni.connectSocket({url: newUrl,// 如果希望返回一个 socketTask 对象,需要至少传入 success / fail / complete 参数中的一个complete: () => {},});this.socketTask.onOpen((res) => {...});this.socketTask.onMessage((res) => {...});this.socketTask.onError((err) => {...});this.socketTask.onClose(() => {...});
}

注意:这里有一个坑,uni.connectSocket() 如果不指定 success/fail/complete 参数中的一个,则返回的不是 SocketTask,而是一个 Promise!我们需要的是 SocketTask,所以这里指定了一个空的 complete 回调函数。

  • 论坛帖子:https://ask.dcloud.net.cn/question/63162
  • 官方文档:https://uniapp.dcloud.net.cn/api/request/websocket.html

上述代码中有一处十分重要的函数调用,即 getWebSocketUrl(),它负责生成科大讯飞语音评测【WebApi】的 webSocket 链接,涉及到参数加密:

import CryptoJS from "crypto-js";
import { Base64 } from "js/base64js.js";/*** 获取websocket url* 该接口需要后端提供,这里为了方便前端处理*/
function getWebSocketUrl() {return new Promise((resolve, reject) => {// 请求地址根据语种不同变化var url = "wss://ise-api.xfyun.cn/v2/open-ise";var host = "ise-api.xfyun.cn";var apiKeyName = "api_key";var apiKey = API_KEY;var apiSecret = API_SECRET;var date = new Date().toGMTString();var algorithm = "hmac-sha256";var headers = "host date request-line";var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/open-ise HTTP/1.1`;var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);var signature = CryptoJS.enc.Base64.stringify(signatureSha);var authorizationOrigin = `${apiKeyName}="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;var authorization = btoa(authorizationOrigin);url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;resolve(url);});
}

js/base64js.js 是 demo 工程里 js 目录下的一个文件(采用 CommonJs 导入规范),而 crypto-js 是使用 npm 安装的一个第三方模块,理论上只需要将这 2 个模块直接拷贝或 npm 安装集成到项目中就好了,但是我的这个工程用的构建工具是 Vite,只支持 ES 模块导入规范,并且项目中使用了 TypeScript,所以,为了更好的编码体验,这里替换为支持 ES 模块导入规范且支持 TypeScript 的另外 2 个模块(js-base64crypto-es),与 demo 中的那 2 个模块功能完全相同:

// npm i crypto-es -S
// npm i js-base64 -S
import CryptoES from "crypto-es";
import { Base64 } from "js-base64";/*** @returns 生成wss链接*/
protected getWebSocketUrl(): Promise<string> {if (this.apiKey === "" || this.apiSecret === "") {throw new Error("apiKey、apiSecret must not be empty !!!");}return new Promise<string>((resolve, reject) => {// 请求地址根据语种不同变化let url = "wss://ise-api.xfyun.cn/v2/open-ise";const host = "ise-api.xfyun.cn";const date = (new Date() as any).toGMTString();const apiKeyName = "api_key";const algorithm = "hmac-sha256";const headers = "host date request-line";const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/open-ise HTTP/1.1`;const signatureSha = CryptoES.HmacSHA256(signatureOrigin, this.apiSecret);const signature = CryptoES.enc.Base64.stringify(signatureSha);const authorizationOrigin = `${apiKeyName}="${this.apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;const authorization = Base64.encode(authorizationOrigin);url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;resolve(url);});
}

Date#toGMTString() 是一个过时的方法,在 TypeScript 中不被识别,从而导致编译不通过,除了像上述代码中通过强转 any 来规避外,还可以在 src/env.d.ts 文件中进行如下声明解决,interface Date { toGMTString(): string; },如果工程中频繁使用该方法的话,建议用第二种方法,这样就不必每处都写一次强转代码了。

注意:getWebSocketUrl() 函数内在生成 webSocket url 时,会使用到 API_KEYAPI_SECRET,这 2 个参数需要你自己注册一个科大讯飞的开发者账号之后,在开发者账号后台获取,为了防止被别人盗用,这个 url 的生成逻辑应该放置到自己的业务后端去实现。

2、录音上下文与权限

录音上下文的创建,以及获取录音权限的方式在不同浏览器环境中各不相同,所以官方 Demo 中做了大量兼容判断:

// 初始化浏览器录音
recorderInit() {navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia// 创建音频环境try {this.audioContext = new (window.AudioContext || window.webkitAudioContext)()this.audioContext.resume()} catch (e) {if (!this.audioContext) {alert('浏览器不支持webAudioApi相关接口')return}}// 获取浏览器录音权限if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {navigator.mediaDevices....then(stream => { getMediaSuccess(stream) }).catch(e => { getMediaFail(e) })} else if (navigator.getUserMedia) {navigator.getUserMedia(...,stream => { getMediaSuccess(stream) },function(e) { getMediaFail(e) })} else {...alert('无法获取浏览器录音功能,请升级浏览器或使用chrome')this.audioContext && this.audioContext.close()return}// 获取浏览器录音权限成功的回调let getMediaSuccess = stream => {console.log('getMediaSuccess')// 创建一个用于通过JavaScript直接处理音频this.scriptProcessor = this.audioContext.createScriptProcessor(0, 1, 1)this.scriptProcessor.onaudioprocess = e => {// 去处理音频数据...}// 创建一个新的MediaStreamAudioSourceNode 对象,使来自MediaStream的音频可以被播放和操作this.mediaSource = this.audioContext.createMediaStreamSource(stream)// 连接this.mediaSource.connect(this.scriptProcessor)this.scriptProcessor.connect(this.audioContext.destination)...}let getMediaFail = (e) => {alert('请求麦克风失败')this.audioContext && this.audioContext.close()this.audioContext = undefined...}
}

而在微信小程序环境下,获取录音上下文与权限就简单多了,通过 uni.getRecorderManager() 即可获取录音上下文(管理器),然后调用 start(option) 时会自动询问用户是否授权录音权限:

const recordManager = uni.getRecorderManager();/*** 开始录音*/
const startRecord = () => {recordManager.onStart(() => {console.log("recorder start");...});recordManager.onPause(() => {console.log("recorder pause");});recordManager.onStop((res) => {// tempFilePath	String	录音文件的临时路径console.log("recorder stop", res);...});recordManager.onError((err) => {// errMsg	String	错误信息console.log("recorder err", err);});recordManager.onFrameRecorded((res) => {// frameBuffer	ArrayBuffer	录音分片结果数据// isLastFrame	Boolean	当前帧是否正常录音结束前的最后一帧const { frameBuffer } = res;...});recordManager.start(option);
};

3、获取录音数据

浏览器的录音数据是通过 scriptProcessor.onaudioprocess 回调函数获得的:

// 获取浏览器录音权限成功的回调
let getMediaSuccess = (stream) => {...this.scriptProcessor.onaudioprocess = (e) => {// 去处理音频数据transWorker.postMessage(e.inputBuffer.getChannelData(0));};...
};

微信小程序的录音数据是通过 recordManager.onFrameRecorded() 回调函数获得:

recordManager.onFrameRecorded((res) => {// frameBuffer	ArrayBuffer	录音分片结果数据// isLastFrame	Boolean	当前帧是否正常录音结束前的最后一帧const { frameBuffer } = res;pushAudioData(frameBuffer); // 将每一帧音频保存起来
});

4、录音数据格式

  • 语音评测接口要求:https://www.xfyun.cn/doc/Ise/IseAPI.html#接口要求

从上面的 语音评测接口要求 中可以知道,科大讯飞语音评测接口对录音数据的格式是有要求的:

内容说明
音频属性采样率 16k、位长 16bit、单声道
音频格式pcm、wav、mp3(需更改 aue 的值为 lame)、speex-wb;
音频大小音频数据发送会话时长不能超过 5 分钟

官方 Demo 中,通过 scriptProcessor.onaudioprocess 回调拿到的录音数据是双声道的 PCM 数据,而接口要求的是单声道,所以通过代码 e.inputBuffer.getChannelData(0) 提取出第 1 个声道的数据,并交给 transWorker 去处理成 采样率 16k位长 16bit 的 PCM 数据:

// index.js
this.scriptProcessor.onaudioprocess = (e) => {// 去处理音频数据transWorker.postMessage(e.inputBuffer.getChannelData(0));
};// transcode.worker.js
(function(){self.onmessage = function(e){transAudioData.transcode(e.data)}let transAudioData = {transcode(audioData) {let output = transAudioData.to16kHz(audioData)output = transAudioData.to16BitPCM(output)...},to16kHz(audioData) {...return newData},to16BitPCM(input) {...return dataView},}
})()

微信小程序中在启动录音时需要传入一个配置参数 option,在这个 option 中我们可以指定采样率、通道数等配置,之后通过 recordManager.onFrameRecorded() 回调拿到的就已经是 采样率 16k位长 16bit 的单声道 PCM 数据了:

const recordManager = uni.getRecorderManager();
const option = {duration: duration, // 录音的时长,单位 ms,最大值 600000(10 分钟)sampleRate: 16000, // 采样率(pc不支持)numberOfChannels: 1, // 录音通道数// encodeBitRate: 48000, // 编码码率(默认就是48000)frameSize: 1, // 指定帧大小,单位 KB。传入 frameSize 后,每录制指定帧大小的内容后,会回调录制的文件内容,不指定则不会回调。暂仅支持 mp3、pcm 格式。format: "pcm", // 音频格式,默认是 aac
}
const startRecord = () => {...recordManager.onFrameRecorded((res) => {// frameBuffer	ArrayBuffer	录音分片结果数据// isLastFrame	Boolean	当前帧是否正常录音结束前的最后一帧const { frameBuffer } = res;pushAudioData(frameBuffer); // 将每一帧音频保存起来});recordManager.start(option);
};

注意:微信小程序录音时长最大值为 10 分钟,而科大讯飞语音评测录音时长最大值为 5 分钟!!

5、发送 webSocket 数据

官方 Demo 中,使用 1 个 audioData 数组来存放每一帧经过 transWorker 处理过的 PCM "散装" 数据(音频流数据);在 webSocket 连接开启的 500ms 之后,开始发送音频流数据:

class IseRecorder {constructor({ language, accent, appId } = {}) {// 记录音频数据this.audioData = []transWorker.onmessage = function (event) {self.audioData.push(...event.data) // GitLqr: 注意这里是 "散装" 的}...}recorderStart() {this.audioContext.resume()this.connectWebSocket()}// 连接websocketconnectWebSocket() {return getWebSocketUrl().then(url => {...iseWS.onopen = e => {// 重新开始录音setTimeout(() => {this.webSocketSend()}, 500)}})}// 初始化浏览器录音recorderInit() {...// 获取浏览器录音权限成功的回调let getMediaSuccess = stream => {this.scriptProcessor.onaudioprocess = e => {// 去处理音频数据transWorker.postMessage(e.inputBuffer.getChannelData(0));}...}}
}

再来看 webSocketSend() 的具体实现,就是不断从 audioData 数组中取出 "1280字节的" PCM 数据,经过 Base64 编码后发送给科大讯飞接口;PCM 数据只会在 第一帧中间帧 进行发送,当 PCM 数据消费完之后,还需要再发送一个 最后帧 通知科大讯飞语音评测接口数据流已经全部发送完成;此外,需要注意第一帧的几个重要参数:

  • category:题型。read_sentence 指的是句子朗读,read_chapter 指的是篇章朗读,等等。
  • auf:音频采样率。不填默认就是 audio/L16;rate=16000
  • ent:中文填 cn_vip;英文填 en_vip
  • text:待评测文本 utf8 编码,需要加 utf8bom 头。

注意:官方 Demo 的对比句子是 where are you,所以 ent 填写的是 en_vip,所以,如果实际业务使用的是中文,则需要注意调整参数值。另外,第一帧参数中有用到 appid,这个跟前面的 API_KEY、API_SECRET 一样,都是从开发者账号后台拿到的。

  // 向webSocket发送数据webSocketSend() {let audioData = this.audioData.splice(0, 1280)var params = {common: { app_id: this.appId },business: {...category: 'read_sentence', // read_syllable/单字朗读,汉语专有 read_word/词语朗读  read_sentence/句子朗读 https://www.xfyun.cn/doc/Ise/IseAPI.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8Bauf: 'audio/L16;rate=16000',ent: 'en_vip',text: '\uFEFF' + 'where are you'},data: {status: 0, encoding: 'raw', data_type: 1,data: this.toBase64(audioData),},}this.webSocket.send(JSON.stringify(params))this.handlerInterval = setInterval(() => {// websocket未连接if (this.webSocket.readyState !== 1) {this.audioData = []clearInterval(this.handlerInterval)return}// 最后一帧if (this.audioData.length === 0) {if (this.status === 'end') {this.webSocket.send(JSON.stringify({business: { cmd: 'auw', aus: 4, aue: 'raw' },data: { status: 2, encoding: 'raw', data_type: 1, data: '', },}))this.audioData = []clearInterval(this.handlerInterval)}return false}audioData = this.audioData.splice(0, 1280)// 中间帧this.webSocket.send(JSON.stringify({business: { cmd: 'auw', aus: 2, aue: 'raw' },data: {status: 1, encoding: 'raw', data_type: 1,data: this.toBase64(audioData),},}))}, 40)}

微信小程序中的数据发送流程跟官方 Demo 差不多,不过对 PCM 数据的存储方式不太一样,在启动录音的时候传入的 option 参数中,frameSize 指定了 onFrameRecorded() 回调的 PCM 帧大小,比如指定为 1(k),那么每次回调时附带的就是 "1024"字节的 PCM 数据,而官方 Demo 中对 PCM 进行拆散(存储时)和切割(发送时)的操作在微信小程序中是没必要的,无形中增加了复杂度。于是我这里干脆使用了一个 audioDataList 数组,将 PCM 数据以一帧帧(每帧 1024 字节)的形式进行存放,发送时,再一帧一帧的取出:

// index.vue
const recordManager = uni.getRecorderManager();
const option = {...frameSize: 1, // 指定帧大小,单位 KB。传入 frameSize 后,每录制指定帧大小的内容后,会回调录制的文件内容,不指定则不会回调。暂仅支持 mp3、pcm 格式。format: "pcm", // 音频格式,默认是 aac
}
const startRecord = () => {...recordManager.onFrameRecorded((res) => {// frameBuffer	ArrayBuffer	录音分片结果数据// isLastFrame	Boolean	当前帧是否正常录音结束前的最后一帧const { frameBuffer } = res;pushAudioData(frameBuffer); // 将每一帧音频保存起来});recordManager.start(option);
};// ise-xfyun/index.ts
export default class IseXfyun {private audioDataList: ArrayBuffer[] = []; // 音频流数据/*** 添加语音数据* @param frameBuffer 帧数据*/pushAudioData(frameBuffer: any) {if (frameBuffer) {this.audioDataList.push(frameBuffer);}}/*** 发送语音数据*/private sendAudioData() {const audioData = this.audioDataList.splice(0, 1);const params = {common: { app_id: this.appId },business: {...category: this.category,ent: "cn_vip", // 中文text: "\uFEFF" + this.chapter,},data: {status: 0, encoding: "raw", data_type: 1,data: this.toBase64(audioData[0]),},};this.socketTask.send({ data: JSON.stringify(params) });this.handlerInterval = setInterval(() => {// websocket未连接if (!this.socketTask) { return this.clearHandlerInterval(); }// 最后一帧if (this.audioDataList.length === 0) {const params = ...;this.socketTask.send({ data: JSON.stringify(params) });this.audioDataList = [];return this.clearHandlerInterval();}// 中间帧const audioData = this.audioDataList.splice(0, 1);const params = {business: { cmd: "auw", aus: 2, aue: "raw" },data: {status: 1, encoding: "raw", data_type: 1,data: this.toBase64(audioData[0]),},};this.socketTask.send({ data: JSON.stringify(params) });}, 40);}private toBase64(buffer: ArrayBuffer) {return uni.arrayBufferToBase64(buffer);}...
}

注意:这里的 toBase64() 也与官方 Demo 中的实现不一样,官方 Demo 使用的是 js/base64js.js 中 Base64 来对数据进行编码,但是实际测试在微信小程序环境中编码会出问题,具体表现为调用科大讯飞语音评测接口时,会返回 68675 错误码,改用 uni.arrayBufferToBase64() 产出的 base64 则可被科大讯飞正常识别。

  • 接口错误码:https://www.xfyun.cn/doc/Ise/IseAPI.html#错误码

6、接收 webSocket 消息

官方 Demo 在创建好 webSocket 之后,指定了消息回调处理函数,具体处理逻辑交由 result(resultData) 实现,回调次数为多次,比如:帧数据接收成功消息,数据流中途错误消息,以及最终的评测结果消息。需要注意的是,如果在中途收到了科大讯飞接口的错误消息,那么应该直接停止数据流发送并提示给用户,因为一旦接口返回了错误消息,那么后续发送的中间帧数据科大讯飞接口是不会受理的,纯属浪费用户的流量和时间:

// 连接websocket
connectWebSocket() {return getWebSocketUrl().then(url => {let iseWS...iseWS.onmessage = e => {this.result(e.data)}})
}result(resultData) {// 识别结束let jsonData = JSON.parse(resultData)if (jsonData.data && jsonData.data.data) {let data = Base64.decode(jsonData.data.data)let grade = parser.parse(data, {attributeNamePrefix: '',ignoreAttributes: false})// 显示成绩...}if (jsonData.code === 0 && jsonData.data.status === 2) {this.webSocket.close()}if (jsonData.code !== 0) {this.webSocket.close()console.log(`${jsonData.code}:${jsonData.message}`)}
}

微信小程序接收并处理 webSocket 消息的逻辑与官方 Demo 类似,不多赘述:

import { Base64 } from "js-base64";
import { XMLParser } from "fast-xml-parser";export default class IseXfyun {/*** 连接科大讯飞wss接口*/async connect() {this.socketTask = uni.connectSocket(...);this.socketTask.onMessage((res) => {this.onMessage(res.data.toString());});...}private onMessage(resultData: string) {const jsonData = JSON.parse(resultData);this.log("收到消息:", jsonData);if (jsonData.data && jsonData.data.data) {const data = Base64.decode(jsonData.data.data);const parser = new XMLParser({attributeNamePrefix: "",ignoreAttributes: false,allowBooleanAttributes: true,});const grade = parser.parse(data);// 显示成绩...}if (jsonData.code === 0 && jsonData.data.status === 2) {this.disconnect();}if (jsonData.code !== 0) {this.callback.onError(jsonData);this.log(`${jsonData.code}:${jsonData.message}`);this.disconnect();}}...
}

三、封装

以上就是对科大讯飞语音评测官方 js Demo 代码逻辑的逐步分析,官方 Demo 把录音与 webSocket 接口调用写在一个文件中,感觉比较混乱,为了更好的复用,我将 录音 与 语音评测 两者进行拆分,把 语音评测 的部分封装到 IseXfyun 类中,大致类结构如下,内容基本上就是上面分析过程中微信小程序端的实现,为缩短篇幅,方法体内代码以省略号表示:

import { Base64 } from "js-base64";
import { XMLParser } from "fast-xml-parser";
import CryptoES from "crypto-es";/*** 科大讯飞 语音评测(流式版)封装** 注意:需要设置wss域名白名单 wss://ise-api.xfyun.cn* 建议:apiKey、apiSecret 不要存放在本地,应该放在后端,防止被破解。* 最佳做法:继承 IseXfyun,重写 getWebSocketUrl(),从后端获取 socket 链接,即 webSocketUrl 的计算规则由后端实现。** @author GitLqr* @since 2022-08-30*/
export default class IseXfyun {/*** 科大讯飞的语音评测只支持 16k 16bit 单通道* @param duration 录音时长* @returns 录音配置*/getAudioRecordOption(duration: number) { ... }/*** 添加语音数据* @param frameBuffer 帧数据*/pushAudioData(frameBuffer: any) { ... }/*** @returns 生成wss链接*/protected getWebSocketUrl(): Promise<string> { ... }/*** 连接科大讯飞wss接口*/async connect() { ... }/*** 断开连接*/disconnect() { ... }/*** 发送语音数据*/private sendAudioData() { ... }private onMessage(resultData: string) { ... }
}

IseXfyun 与 Uniapp 完整版 Demo 的具体代码可访问下面仓库进行查看:

  • Uniapp 版 科大讯飞语音评测 Demo:https://github.com/GitLqr/UniappIseXfyun

注意:微信小程序需要配置域名白名单 wss://ise-api.xfyun.cn

Uniapp版 科大讯飞语音评测Demo

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

目标检测算法——YOLOv5/YOLOv7改进之结合​PP-LCNet(轻量级CPU网络)

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 ​PP-LCNet——轻量级且超强悍的CPU级骨干网络&#xff01;&#xff01; &#xff08;一&#xff09;前沿介绍 1.PP-LCNet主要模块 2.相关实验结果 &#xff08;二&#xff09;YOLOv5/YOLOv7改进之结…

从Blip2到Segment Anything视觉语义金字塔+ChatGPT= 把图片变文本段落, 8G显存即可Run...

关注公众号&#xff0c;发现CV技术之美 8G GPU显存即可以运行 Twitter链接&#xff1a;https://twitter.com/awinyimgprocess/status/1646225454599372800?s46&tHvOe9T2n35iFuCHP5aIHpQ 代码链接&#xff1a;https://github.com/showlab/Image2Paragraph https:// 01 …

目标检测算法——YOLOv5/YOLOv7改进之结合​ASPP(空洞空间卷积池化金字塔)

&#x1f496;&#x1f496;>>>加勒比海带&#xff0c;QQ2479200884<<<&#x1f496;&#x1f496; &#x1f340;&#x1f340;>>>【YOLO魔法搭配&论文投稿咨询】<<<&#x1f340;&#x1f340; ✨✨>>>学习交流 | 温澜潮…

从BLIP-2到SAM视觉语义金字塔+ChatGPT= 把图片变文本段落, 8G显存即可Run

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【图像分割】微信技术交流群 作者&#xff1a;老大 |&#xff08;已授权转载&#xff09;编辑&#xff1a;CVer https://zhuanlan.zhihu.com/p/621503837 8G GPU显存即可以运…

问一下ChatGPT:DIKW金字塔模型

经常看到这张DIKW金字塔模型图&#xff0c;还看到感觉有点过份解读的图&#xff0c;后面又加上了insight&#xff0c;impact等内容。 Data&#xff1a;是数据&#xff0c;零散的、无规则的呈现到人们眼前&#xff0c;如果你只看到这些数字&#xff0c;如果没有强大的知识背景&a…

QCon: 工程师成长的金字塔思维

QCon 线下会议终于来了&#xff0c;但这次的分享有一些意外&#xff0c;不是性能、架构、内核、缓存&#xff0c;而不是AI实践相关的内容&#xff0c;论坛的主题是“工程师成长实践”&#xff0c;而我的topic 是“工程师成长的金字塔思维”。本以为这是一个非热门话题&#xff…

拉普拉斯金字塔

原文 高斯金字塔G0层下采样后为G1&#xff0c;用G0减去G1的上采样&#xff0c;就得到了拉普拉斯层L0. 高斯金字塔G1层上采样后与拉普拉斯金字塔L0层相加后就得到了G0层。 import numpy as np import cv2 as cv from matplotlib import pyplot as pltimg cv.imread(left_01.png…

微信称不会推出「已读」功能;马斯克宣布成立 AI 公司 xAI;GPT-4 架构曝光,有 1.8 万亿参数|极客头条

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&…

百度版本gactgpt即将来临,gpt人工智能机器横空出世

百度版本gactgpt即将来临&#xff0c;gpt人工智能机器横空出世&#xff0c;“一言”为定&#xff01;百度版ChatGPT确认&#xff01;李彦宏OKR曝光&#xff0c;率先应用于收索业务 gactCBT 大获&#xff0c;当下极有可能成为人工智能的 iPhone 时刻。为了在这场人工智能竞赛中…

解锁实盘交易,打通量化系统交易环节!股票量化分析工具QTYX-V2.3.9

前言 本期我们把股票量化分析工具QTYX的“交易”子页面给解锁了&#xff0c;这样一来&#xff0c;就打通了量化交易系统从分析到交易的完整过程&#xff0c;注意当前的QTYX版本升级到了2.3.9。 QTYX完整架构如下所示&#xff0c;其中包括了QTYX股票量化分析系统和实盘机器人两部…

自己做量化交易软件(43)小白量化实战16--利用小白量化金融模块在恒生PTrade交易系统(交易端)上仿大智慧指标回测及实战交易设计

自己做量化交易软件(43)小白量化实战16–利用小白量化金融模块在恒生PTrade交易系统(交易端)上仿大智慧指标回测及实战交易设计 量化产品PTrade(Personalise Trade)交易系统采用事件驱动式回测平台&#xff0c;与聚宽量化平台类似的量化框架&#xff0c;内置Ta-lib金融模块&am…

量化投资 | 量化交易平台工具汇总

量化投资必备手册&#xff0c;分享30个量化交易平台给你们&#xff0c;转需&#xff0c;不用谢&#xff01; 1. 掘金量化 量化交易系统官方网址&#xff1a;https://www.myquant.cn/ 2、国泰君安量化交易系统 量化交易系统网址&#xff1a;https://quant.gtja.com/ 3. 量化云…

初学Linux—解决开机卡在用户登录界面的问题

1.Linux关机之后开机界面卡在登录界面没有反应&#xff1b; 可能是我删除了某个文件… 2.解决办法&#xff1a; ctrlaltF2 ;切换终端 su root ;切换到超级用户 然后会弹出输入密码提示&#xff0c;直接输入超级用户的密码&#xff08;注意&#xff1a;linux中密码不会回显&am…

centos6.8开机一直卡在登录界面,无法登录

** **后面发现&#xff0c;问题出在&#xff0c;前几天配置的 java的jdk&#xff0c;还有mysql的环境变量上。 ** 解决问题的方法 使用CtrlAIt F3&#xff0c;竟然命令控制面板&#xff08;但是&#xff0c;进去后&#xff0c;你使用指令&#xff0c;比如&#xff0c;vi,ls -…

Ubuntu开机后卡在登陆界面进去不

系统&#xff1a;Ubuntu18.03 系统跑代码卡死了&#xff0c;导致鼠标键盘都用不了&#xff0c;直接强制关机后&#xff0c;卡在登陆界面进不去 步骤&#xff1a; 1. 系统启动后&#xff0c;在BIOS引导界面按"ESC"键进入选择菜单&#xff0c;选择第一个"*Ubun…

记一次绕过安全狗与360艰难提权

0x01 前言背景 端午短暂休息三天&#xff0c;复工之后朋友又丢给我一个Webshell&#xff0c;在打台球途中了解了一下这个奇怪的shell&#xff0c;说是无法执行命令&#xff0c;经过测试发现只能执行dir命令&#xff0c;确实奇怪&#xff0c;草草打了几局台球就回去拿起电脑开日…

为什么我骂360弱智 周鸿祎却说“谢谢”

好吧&#xff0c;我承认我标题党了&#xff01; 不过&#xff0c;有图有真相&#xff01; 事情的起因是我在360开发者平台&#xff0c;提交了我们的客户端“缘创派”&#xff0c;这是一个给互联网创业者找创业合伙人的应用。之前上传的是1.0版本&#xff0c;最近更新了功能更为…

曾遭周鸿祎全网封杀的 360 猛将 :草根打工到 36 岁身家上亿的逆袭!

作者 | 云游小生 来源 | 纯洁的微笑&#xff08;ID:keeppuresmile&#xff09; 猎豹六周年&#xff0c;傅盛在内部演讲&#xff1a; “有部电视剧叫《奋斗》&#xff0c;名字很好&#xff0c;帅哥靓女&#xff0c;青春励志。 但看了开头&#xff0c;我就看不下去了。作为一个奋…

360 软件误报 与流氓战斗到底

写了个音乐贺卡软件&#xff0c;却被360报病毒 估计是设计到了一个音乐贺卡打包功能 这是非常令人气愤的 辛苦的工作&#xff0c;创意的想法&#xff0c;终于实现&#xff0c;最后却被这些杀毒的流氓误报&#xff0c;害的别人不能使用。 于是在360官方提交了误报检测 检测…