freeswitch对接FunASR实时语音听写

使用阿里funasr作为freeswitch的ASR语音识别服务 要使用unimrcp 必须先编译一个正常的unimrcpserver ,再修改里面的asr语音识别模块。 阅读详情
AI摘要

本文介绍了如何使用Docker镜像部署FunASR服务端,包括启动步骤和模型加载,以及使用Java编写的WebSocket客户端进行语音识别。同时提供了服务器性能配置建议和相关文档链接。

1、镜像启动

通过下述命令拉取并启动FunASR软件包的docker镜像:

sudo docker pull \
  registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7
mkdir -p ./funasr-runtime-resources/models
sudo docker run -p 10096:10095 -it --privileged=true \
  -v $PWD/funasr-runtime-resources/models:/workspace/models \
  registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7

服务端启动

docker启动之后,启动 funasr-wss-server-2pass服务程序:

cd FunASR/runtime
nohup bash run_server_2pass.sh \
  --download-model-dir /workspace/models \
  --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx \
  --model-dir damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx  \
  --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx  \
  --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx \
  --lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst \
  --itn-dir thuduj12/fst_itn_zh --certfile 0 \
  --hotword /workspace/models/hotwords.txt > log.txt 2>&1 &

# 如果您想关闭ssl,增加参数:--certfile 0 这里不需要ssl 需要加参数
# 如果您想使用时间戳或者nn热词模型进行部署,请设置--model-dir为对应模型:
#   damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx(时间戳)
#   damo/speech_paraformer-large-contextual_asr_nat-zh-cn-16k-common-vocab8404-onnx(nn热词)
# 如果您想在服务端加载热词,请在宿主机文件./funasr-runtime-resources/models/hotwords.txt配置热词(docker映射地址为/workspace/models/hotwords.txt):
#   每行一个热词,格式(热词 权重):阿里巴巴 20(注:热词理论上无限制,但为了兼顾性能和效果,建议热词长度不超过10,个数不超过1k,权重1~100)

 运行命令之后就会下载社区的模型  启动成功之后会有端口显示

接下来就可以做语音翻译了

2.获取freeswitch音频发送到Funasr 可以参考之前获取的

freeswitch智能外呼系统搭建流程_freeswitch 外联模式的流程-优快云博客

3.java 用ws 连接就行了 官方的样例

//
// Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
// Reserved. MIT License  (https://opensource.org/licenses/MIT)
//
/*
 * // 2022-2023 by zhaomingwork@qq.com
 */
// java FunasrWsClient
// usage:  FunasrWsClient [-h] [--port PORT] [--host HOST] [--audio_in AUDIO_IN] [--num_threads NUM_THREADS]
//                 [--chunk_size CHUNK_SIZE] [--chunk_interval CHUNK_INTERVAL] [--mode MODE]
package websocket;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.*;
import java.util.Map;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** This example demonstrates how to connect to websocket server. */
public class FunasrWsClient extends WebSocketClient {

  public class RecWavThread extends Thread {
    private FunasrWsClient funasrClient;

    public RecWavThread(FunasrWsClient funasrClient) {
      this.funasrClient = funasrClient;
    }

    public void run() {
      this.funasrClient.recWav();
    }
  }

  private static final Logger logger = LoggerFactory.getLogger(FunasrWsClient.class);

  public FunasrWsClient(URI serverUri, Draft draft) {
    super(serverUri, draft);
  }

  public FunasrWsClient(URI serverURI) {
    
    super(serverURI);
  }

  public FunasrWsClient(URI serverUri, Map<String, String> httpHeaders) {
    super(serverUri, httpHeaders);
  }

  public void getSslContext(String keyfile, String certfile) {
    // TODO
    return;
  }

  // send json at first time
  public void sendJson(
      String mode, String strChunkSize, int chunkInterval, String wavName, boolean isSpeaking,String suffix) {
    try {

      JSONObject obj = new JSONObject();
      obj.put("mode", mode);
      JSONArray array = new JSONArray();
      String[] chunkList = strChunkSize.split(",");
      for (int i = 0; i < chunkList.length; i++) {
        array.add(Integer.valueOf(chunkList[i].trim()));
      }

      obj.put("chunk_size", array);
      obj.put("chunk_interval", new Integer(chunkInterval));
      obj.put("wav_name", wavName);

      if(FunasrWsClient.hotwords.trim().length()>0)
      {
          String regex = "\\d+";
          JSONObject jsonitems = new JSONObject();
          String[] items=FunasrWsClient.hotwords.trim().split(" ");
          Pattern pattern = Pattern.compile(regex);
          String tmpWords="";
          for(int i=0;i<items.length;i++)
          {

              Matcher matcher = pattern.matcher(items[i]);

              if (matcher.matches()) {

                jsonitems.put(tmpWords.trim(), items[i].trim());
                tmpWords="";
                continue;
              }
              tmpWords=tmpWords+items[i]+" ";

          }

          obj.put("hotwords", jsonitems.toString());
      }

      if(suffix.equals("wav")){
          suffix="pcm";
      }
      obj.put("wav_format", suffix);
      if (isSpeaking) {
        obj.put("is_speaking", new Boolean(true));
      } else {
        obj.put("is_speaking", new Boolean(false));
      }
      logger.info("sendJson: " + obj);
      // return;

      send(obj.toString());

      return;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // send json at end of wav
  public void sendEof() {
    try {
      JSONObject obj = new JSONObject();

      obj.put("is_speaking", new Boolean(false));

      logger.info("sendEof: " + obj);
      // return;

      send(obj.toString());
      iseof = true;
      return;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // function for rec wav file
  public void recWav() {
    String fileName=FunasrWsClient.wavPath;
    String suffix=fileName.split("\\.")[fileName.split("\\.").length-1];
    sendJson(mode, strChunkSize, chunkInterval, wavName, true,suffix);
    File file = new File(FunasrWsClient.wavPath);

    int chunkSize = sendChunkSize;
    byte[] bytes = new byte[chunkSize];

    int readSize = 0;
    try (FileInputStream fis = new FileInputStream(file)) {
      if (FunasrWsClient.wavPath.endsWith(".wav")) {
        fis.read(bytes, 0, 44); //skip first 44 wav header
      }
      readSize = fis.read(bytes, 0, chunkSize);
      while (readSize > 0) {
        // send when it is chunk size
        if (readSize == chunkSize) {
          send(bytes); // send buf to server

        } else {
          // send when at last or not is chunk size
          byte[] tmpBytes = new byte[readSize];
          for (int i = 0; i < readSize; i++) {
            tmpBytes[i] = bytes[i];
          }
          send(tmpBytes);
        }
        // if not in offline mode, we simulate online stream by sleep
        if (!mode.equals("offline")) {
          Thread.sleep(Integer.valueOf(chunkSize / 32));
        }

        readSize = fis.read(bytes, 0, chunkSize);
      }

      if (!mode.equals("offline")) {
        // if not offline, we send eof and wait for 3 seconds to close
        Thread.sleep(2000);
        sendEof();
        Thread.sleep(3000);
        close();
      } else {
        // if offline, just send eof
        sendEof();
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  public void onOpen(ServerHandshake handshakedata) {

    RecWavThread thread = new RecWavThread(this);
    thread.start();
  }

  @Override
  public void onMessage(String message) {
    JSONObject jsonObject = new JSONObject();
    JSONParser jsonParser = new JSONParser();
    logger.info("received: " + message);
    try {
      jsonObject = (JSONObject) jsonParser.parse(message);
      logger.info("text: " + jsonObject.get("text"));
      if(jsonObject.containsKey("timestamp"))
      {
          logger.info("timestamp: " + jsonObject.get("timestamp"));
      }
    } catch (org.json.simple.parser.ParseException e) {
      e.printStackTrace();
    }
    if (iseof && mode.equals("offline") && !jsonObject.containsKey("is_final")) {
      close();
    }
     
    if (iseof && mode.equals("offline") && jsonObject.containsKey("is_final") && jsonObject.get("is_final").equals("false")) {
      close();
    }
  }

  @Override
  public void onClose(int code, String reason, boolean remote) {

    logger.info(
        "Connection closed by "
            + (remote ? "remote peer" : "us")
            + " Code: "
            + code
            + " Reason: "
            + reason);
  }

  @Override
  public void onError(Exception ex) {
    logger.info("ex: " + ex);
    ex.printStackTrace();
    // if the error is fatal then onClose will be called additionally
  }

  private boolean iseof = false;
  public static String wavPath;
  static String mode = "online";
  static String strChunkSize = "5,10,5";
  static int chunkInterval = 10;
  static int sendChunkSize = 1920;
  static String hotwords="";
  static String fsthotwords="";

  String wavName = "javatest";

  public static void main(String[] args) throws URISyntaxException {
    ArgumentParser parser = ArgumentParsers.newArgumentParser("ws client").defaultHelp(true);
    parser
        .addArgument("--port")
        .help("Port on which to listen.")
        .setDefault("8889")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--host")
        .help("the IP address of server.")
        .setDefault("127.0.0.1")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--audio_in")
        .help("wav path for decoding.")
        .setDefault("asr_example.wav")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--num_threads")
        .help("num of threads for test.")
        .setDefault(1)
        .type(Integer.class)
        .required(false);
    parser
        .addArgument("--chunk_size")
        .help("chunk size for asr.")
        .setDefault("5, 10, 5")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--chunk_interval")
        .help("chunk for asr.")
        .setDefault(10)
        .type(Integer.class)
        .required(false);

    parser
        .addArgument("--mode")
        .help("mode for asr.")
        .setDefault("offline")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--hotwords")
        .help("hotwords, splited by space, hello 30 nihao 40")
        .setDefault("")
        .type(String.class)
        .required(false);
    String srvIp = "";
    String srvPort = "";
    String wavPath = "";
    int numThreads = 1;
    String chunk_size = "";
    int chunk_interval = 10;
    String strmode = "offline";
    String hot="";
    try {
      Namespace ns = parser.parseArgs(args);
      srvIp = ns.get("host");
      srvPort = ns.get("port");
      wavPath = ns.get("audio_in");
      numThreads = ns.get("num_threads");
      chunk_size = ns.get("chunk_size");
      chunk_interval = ns.get("chunk_interval");
      strmode = ns.get("mode");
      hot=ns.get("hotwords");
      System.out.println(srvPort);

    } catch (ArgumentParserException ex) {
      ex.getParser().handleError(ex);
      return;
    }

    FunasrWsClient.strChunkSize = chunk_size;
    FunasrWsClient.chunkInterval = chunk_interval;
    FunasrWsClient.wavPath = wavPath;
    FunasrWsClient.mode = strmode;
    FunasrWsClient.hotwords=hot;
    System.out.println(
        "serIp="
            + srvIp
            + ",srvPort="
            + srvPort
            + ",wavPath="
            + wavPath
            + ",strChunkSize"
            + strChunkSize);

    class ClientThread implements Runnable {

      String srvIp;
      String srvPort;

      ClientThread(String srvIp, String srvPort, String wavPath) {
        this.srvIp = srvIp;
        this.srvPort = srvPort;
      }

      public void run() {
        try {

          int RATE = 16000;
          String[] chunkList = strChunkSize.split(",");
          int int_chunk_size = 60 * Integer.valueOf(chunkList[1].trim()) / chunkInterval;
          int CHUNK = Integer.valueOf(RATE / 1000 * int_chunk_size);
          int stride =
              Integer.valueOf(
                  60 * Integer.valueOf(chunkList[1].trim()) / chunkInterval / 1000 * 16000 * 2);
          System.out.println("chunk_size:" + String.valueOf(int_chunk_size));
          System.out.println("CHUNK:" + CHUNK);
          System.out.println("stride:" + String.valueOf(stride));
          FunasrWsClient.sendChunkSize = CHUNK * 2;

          String wsAddress = "ws://" + srvIp + ":" + srvPort;

          FunasrWsClient c = new FunasrWsClient(new URI(wsAddress));

          c.connect();

          System.out.println("wsAddress:" + wsAddress);
        } catch (Exception e) {
          e.printStackTrace();
          System.out.println("e:" + e);
        }
      }
    }
    for (int i = 0; i < numThreads; i++) {
      System.out.println("Thread1 is running...");
      Thread t = new Thread(new ClientThread(srvIp, srvPort, wavPath));
      t.start();
    }
  }
}
 

运行之后java和Funasr控制台 就能识别了

服务器性能配置官方参考。

用户可以根据自己的业务需求,选择合适的服务器配置,推荐配置为:

  • 配置1: (X86,计算型),4核vCPU,内存8G,单机可以支持大约16路的请求
  • 配置2: (X86,计算型),16核vCPU,内存32G,单机可以支持大约32路的请求
  • 配置3: (X86,计算型),64核vCPU,内存128G,单机可以支持大约100路的请求

详细性能测试报告https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/benchmark_onnx_cpp.md

官方文档

https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md

如果模块或流程觉得麻烦可以到 

https://item.taobao.com/item.htm?id=653611115230

 视频教程yuexiajiayan的个人空间-yuexiajiayan个人主页-哔哩哔哩视频

FreeSwitch采用mrcp协议对接科大讯飞asr和tts以及对话系统 FreeSwitch采用mrcp协议对接科大讯飞asr和tts对接过程 阅读详情

相关推荐

freeswitch智能语音开发之ASR

我们可以通过switch_core_media_bug_add(session, "my_detect_speech", key, speech_callback, sth, 0, SMBF_READ_STREAM | SMBF_NO_PAUSE | SMBF_ONE_ONLY, &sth->bug))来绑定识别回调函数,我们只要简单的实现回调函数来获取语音流媒体,把流媒体发送给识别引擎就获取识别结果。dest:switch_asr_interface_t接口中asr_open接口函数传入参数。

vx897857410的博客 1093

基于Freeswitch对接免费的ASR语音识别引擎

目前大部分的只能呼叫中心系统都对接了ASR语音识别引擎,主流的识别引擎有科大讯飞,阿里,百度等大厂的产品,价格不菲,有没有什么免费的引擎可以使用呢,答案是肯定的。

shanghaimoon的专栏 5712

FreeSWITCH与FunASR实时语音转写实战:从Docker部署到WebSocket集成

本文详细介绍了如何通过Docker快速部署FunASR实时语音转写服务,并将其与FreeSWITCH集成,实现通话录音的实时文字转写。从FunASR的Docker部署、双通道识别服务配置,到FreeSWITCH音频流对接和WebSocket客户端开发,提供了完整的实战指南。文章还涵盖了性能优化、问题排查及进阶功能开发,帮助开发者高效实现实时语音听写功能。

weixin_29206669的博客 418

FreeSWITCH FunASR语音识别模块

mod_funasr是一个为 FreeSWITCH 开发的语音识别模块,集成了阿里巴巴的 FunASR(Fun Automatic Speech Recognition)语音识别服务。该模块通过 WebSocket 连接实现实时语音识别功能,支持语音转文本以及相关的语音事件处理。

pzhu1015的专栏 1303

FunASR+FreeSWITCH:实时语音质检系统落地实践

语音识别(ASR)是将电话录音转为文本的关键技术,其核心原理是通过声学模型和语言模型将音频信号映射为文字序列。在客服质检场景中,实时语音识别能实现事中干预,大幅提升质检覆盖率。FunASR 作为开源中文识别引擎,支持流式识别和热词定制,结合 FreeSWITCH 的媒体处理能力,可通过 WebSocket 构建低延迟音频通道。该方案适用于呼叫中心、电话机器人等场景,有效解决传统质检覆盖率低、商业授权昂贵的问题,实现从录音转写到质检规则联动的完整闭环。

weixin_29083649的博客 296

FunASR与FreeSWITCH融合实战:从零构建高精度语音质检系统

本文详细介绍了如何将FunASR与FreeSWITCH融合,构建高精度语音质检系统。通过实时语音听写技术,结合热词定制和多模型切换,实现客服中心等场景的自动化质检,显著提升识别准确率和效率。系统架构涵盖音频采集、识别处理和业务应用三层,支持负载均衡和性能优化,适用于工业级部署。

weixin_29198045的博客 980

FunASR+FreeSWITCH语音质检系统实战:从部署到优化

语音识别与软交换技术是构建自动化质检系统的核心。通过WebSocket协议实现音频流实时传输,结合Docker容器化部署,可大幅降低环境依赖。FunASR提供高精度中文识别,FreeSWITCH处理通话控制,两者结合实现100%通话转写与关键词命中分析。该系统适用于呼叫中心、客服团队等场景,支持实时与离线质检,提升合规检查效率。

weixin_34327223的博客 388

保姆级教程:用Docker快速部署FreeSWITCH的ASR服务(含FunASR、sherpa-ncnn)

本文提供了一份详细的Docker部署FreeSWITCH ASR服务指南,涵盖FunASR和sherpa-ncnn等主流方案。通过容器化技术简化部署流程,实现中文、英文或双语语音识别,适用于智能客服、实时字幕等场景。重点介绍了mod_audio_fork模块集成、性能优化及常见问题解决方案。

weixin_29294975的博客 231

FunASR与FreeSWITCH语音质检系统实战:Docker部署与WebSocket流式识别

语音识别(ASR)与实时通信(RTC)的结合是构建智能语音应用的基础。通过WebSocket协议,音频流可以从通信系统实时传输到识别引擎,实现流式转写。这种技术方案在呼叫中心质检、实时字幕等场景中价值显著,能大幅降低延迟和成本。FunASR作为开源中文ASR工具链,支持流式识别和热词增强,与FreeSWITCH软交换机的媒体分流能力结合,可搭建高准确率、低成本的语音质检系统。本文详细介绍了基于Docker的部署实践,涵盖环境配置、音频流对接、质检规则引擎及性能调优,为中小团队提供可落地的解决方案。

weixin_33912246的博客 299

FreeSWITCH 图形化界面实战:Docker化ASR服务一键部署与集成

本文详细介绍了如何通过Docker容器一键部署ASR(自动语音识别)服务,并将其与FreeSWITCH语音通信系统集成。利用mod_audio_fork模块实现音频流的实时分叉,结合图形化界面和现成的Docker镜像,大幅降低了ASR功能集成的技术门槛和部署复杂度,适用于智能客服、语音质检等场景。

weixin_29210759的博客 66

freeswitch集成离线语音识别funasr

本文介绍了FreeSWITCH对接阿里云离线语音识别FunASR的实现方案。主要包含四个部分:1)安装FreeSWITCH 1.10.5版本;2)配置mod_fun_asr模块,修改ws地址和识别服务地址;3)部署阿里FunASR语音识别服务;4)通过Lua脚本实现语音交互流程控制,包括放音、信号检测、打断处理和结果获取。该方案可用于语音质检、智能客服等场景,相比在线服务可降低成本。文中还提到可扩展NLP模型实现应答检索功能,并提供了模块集成流程图。

mokeily99的博客 1240

FreeSWITCH集成FunASR离线语音识别实战

随着语音技术的普及,离线语音识别成为隐私敏感场景的关键需求。FunASR(Fundamental AI Research Speech)是由阿里巴巴达摩院开源的语音识别工具包,支持离线部署。FreeSWITCH作为开源通信平台,集成FunASR可实现本地化语音交互方案,适用于客服系统、智能家居等场景。FreeSWITCH通过Event Socket或mod_vmr模块与FunASR交互。典型流程为:FreeSWITCH捕获音频流→通过TCP/UDP发送至FunASR服务→返回文本结果→触发后续业务逻辑。

Ptl_q1tn的博客 311

告别虚拟机!用ZYNQ7000和PYNQ 2.6.0打造一个能实时识别人脸的“智能摄像头”

本文详细介绍了如何利用Xilinx ZYNQ7000开发板和PYNQ 2.6.0框架构建实时人脸识别系统。通过硬件加速和嵌入式开发技术,该系统实现了低延迟、低功耗的独立运行能力,适用于边缘计算和物联网应用。文章包含从环境搭建到核心功能实现的完整指南,并提供了性能优化和问题排查的实用技巧。

weixin_42543046的博客 390

【使用Unimrcp和Funasr构建呼叫中心语音识别服务端】

此次使用的是unimrcp1.6版本,先下载unimrcp-deps-1.6.0以及unimrcp-1.6.0进行构建,此处不过多赘述。处理funasr返回的数据,构建xml返回给freeswitch,此次vad使用的是webrtc vad。此步骤,主要是使用websocket去连接funasr服务端,(funasr的部署自行百度),在。成功加载funasr识别模块。创建funasr连接,在。Freeswitch日志。成功启动funasr。

qq_22218713的博客 3298

RAG——RAG 索引(文档解析技术)

本文介绍了RAG系统中文档解析的关键技术,重点分析了如何通过LangChain的Document Loaders处理多种格式文档。文章指出高质量文档解析对RAG系统效果的决定性作用,并详细讲解了PDF、TXT、Word等常见格式的解析方法。作者提供了代码示例和Gitee开源项目链接,特别强调了PDF解析的深入方案,同时介绍了相关依赖库的安装配置。文章还展示了如何定义通用文档加载方法,为开发者提供了实用的技术参考。

小志的博客 751

脑影像分析入门:如何用一般线性模型(GLM)处理fMRI数据?

本文为神经影像研究入门者详细解析了如何运用一般线性模型(GLM)处理fMRI数据。文章从GLM的核心原理出发,逐步拆解了从实验设计、构建稳健设计矩阵、模型估计与统计推断,到高阶分析及多重比较校正的全流程,并指出了常见陷阱与结果解读要点,是掌握fMRI数据分析基础的关键指南。

weixin_29091445的博客 397

FreeSWITCH 简单图形化界面33 - 使用FunASR把通话录音转成文字

FunASR是一款基于深度学习的语音识别工具,它能够将语音信号快速转换为文字,为用户提供便捷的语音交互体验。该工具不仅支持多种语言识别,还具备高准确率、低延迟和灵活定制等特点,广泛应用于语音输入、语音转写、语音助手等场景。

jia198810的博客 1765

FPGA实现PI控制

FPGA实现PI控制

m0_57319166的博客 3888

本地化部署中文ASR的优选方案|FunASR镜像实践分享

本文介绍了如何在星图GPU平台上自动化部署FunASR 语音识别基于speech_ngram_lm_zh-cn 二次开发构建by科哥镜像,实现高效中文语音转写。用户可通过简单配置快速搭建本地化ASR系统,典型应用于会议记录转文字、课程录音生成字幕等场景,兼顾准确性与隐私安全。

weixin_36296444的博客 1209

5分钟搞定单细胞marker基因气泡图:scDotPlot保姆级教程(含Seurat适配)

本文提供了一份详细的scDotPlot R包使用教程,帮助用户在5分钟内完成单细胞marker基因气泡图的绘制。教程涵盖环境准备、Seurat和SingleCellExperiment对象的适配处理、进阶可视化技巧及自动化报告生成,特别适合生物信息学新手和临床研究者快速掌握这一高效的可视化工具。

weixin_29047545的博客 261

如何实现C++动态调用动态链接库(DLL或SO)

使用函数指针来定义函数的类型,以便在动态库中找到的函数能够正确地调用。函数指针的类型必须与函数的签名(参数类型和返回类型)匹配。// 函数指针#ifdef _MSC_VER // Windows环境下的导出声明__declspec。

weixin_48438353的博客 7982

FreeSWITCH 集成 FunASR 离线语音识别:架构、配置与实战指南

在呼叫中心、智能客服等应用场景中,实时语音转录和语音分析的需求日益增长。FreeSWITCH 作为一款强大的开源通信平台,可以通过集成 FunASR 离线语音识别能力,实现本地化、高精度的语音处理。FunASR 是一个开源的语音识别工具包,尤其在离线场景下表现出色,结合 FreeSWITCH 可以构建无需依赖公有云服务的语音应用,从而降低成本,提高数据安全性。尤其当我们需要处理一些敏感数据的时候,离线语音识别就显得尤为重要。

i347b51d的博客 488

使用字体解密技术爬取懂车帝二手车数据

本文介绍了一种针对懂车帝二手车平台的字体加密数据采集方案。通过抓包分析,识别出关键API接口并解析了字体加密机制,成功实现了价格等敏感数据的解密。方案采用requests库发送POST请求获取JSON数据,利用fontTools解析字体映射关系,建立加密字符到真实数字的转换算法。程序支持多页采集,提取车辆名称、价格、年份等12个维度的信息,并自动保存为Excel格式。技术亮点包括动态字体解析、健壮的数据处理和分页优化,为二手车市场分析提供了可靠的数据支持。文中特别强调数据采集的合法合规性,建议仅用于学术研究

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 1309

FunASR多模型串联实战:ASR+VAD+PUNC全链路语音识别部署指南 [特殊字符]

FunASR作为一款**工业级端到端语音识别工具包**,提供了完整的语音识别解决方案。本文将详细介绍如何部署FunASR的ASR(语音识别)、VAD(语音活动检测)和PUNC(标点恢复)多模型串联全链路服务,实现高精度的语音转文字功能。 ## 什么是FunASR多模型串联? 🤔 FunASR多模型串联是指将**语音活动检测(VAD)、自动语音识别(ASR)和标点恢复(PUNC)** 三个核心

gitblog_01060的博客 711

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

上一篇: freeswitch智能外呼系统搭建流程
下一篇: rust 桌面 sip 软电话(基于tauri 、pjsip库)
java_lilin
博客等级 码龄14年 364粉丝 124原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值