【项目管理】Java离线版语音识别-语音转文字

这篇具有很好参考价值的文章主要介绍了【项目管理】Java离线版语音识别-语音转文字。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系统:Win10
Java:1.8.0_333
IDEA:2020.3.4
Gitee:https://gitee.com/lijinjiang01/SpeechRecognition

1.项目前言

最近在做一个鬼畜视频的时候,需要处理大量语音文件,全部都是 wav 格式的,然后我想把这些语音转成文字,不过这些语音有几千条,这时候我就想能不能用 Java 实现。
不过现在主流的语音识别像百度。讯飞好像都不支持 Java 离线版,在查找一些资料后,我准备使用 Vosk

2.Vosk介绍

Vosk 官网:https://alphacephei.com/vosk/
Vosk 是言语识别工具包,Vosk 最大的优点是:

  1. 支持二十+种语言 - 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷兰人,加泰罗尼亚语,阿拉伯, 希腊语, 波斯语, 菲律宾语,乌克兰语, 哈萨克语, 瑞典语, 日语, 世界语, 印地语, 捷克语, 波兰语
  2. 移动设备上脱机工作-Raspberry Pi,Android,iOS
  3. 使用简单的 pip3 install vosk 安装
  4. 每种语言的手提式模型只有是 50Mb, 但还有更大的服务器模型可用
  5. 提供流媒体 API,以提供最佳用户体验(与流行的语音识别 python 包不同)
  6. 还有用于不同编程语言的包装器-java / csharp / javascript等
  7. 可以快速重新配置词汇以实现最佳准确性
  8. 支持说话人识别

至于选择 Vosk 的原因,我想大概因为他们是 Apache-2.0 开源项目吧,而且他们还提供了中文模型,这省了很多事不是么

3.项目开发

3.1 项目准备

这里的项目准备只做一个 wav 语音识别,能够供自己使用就行了
首先,我们需要新建一个 Maven Java 项目,然后导入相关的依赖

<!-- 获取音频信息 -->
<dependency>
    <groupId>org</groupId>
    <artifactId>jaudiotagger</artifactId>
    <version>2.0.3</version>
</dependency>

<!-- 语音识别 -->
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.7.0</version>
</dependency>
<dependency>
    <groupId>com.alphacephei</groupId>
    <artifactId>vosk</artifactId>
    <version>0.3.32</version>
</dependency>

这里除了 vosk 相关依赖,我还导入了 jaudiotagger 这个获取音频信息的依赖,因为等会我们需要自动获取音频的采样率(SampleRate),有兴趣的小伙伴可以看一下我另一篇文章:Java获取Wav文件的采样率SampleRate

那么为什么我需要获取音频的采样率呢?这里我们看下 Vosk 官方给的示例代码:
https://github.com/alphacep/vosk-api/blob/master/java/demo/src/main/java/org/vosk/demo/DecoderDemo.java

package org.vosk.demo;

import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.vosk.LogLevel;
import org.vosk.Recognizer;
import org.vosk.LibVosk;
import org.vosk.Model;

public class DecoderDemo {

    public static void main(String[] argv) throws IOException, UnsupportedAudioFileException {
        LibVosk.setLogLevel(LogLevel.DEBUG);

        try (Model model = new Model("model");
                    InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test.wav")));
                    Recognizer recognizer = new Recognizer(model, 16000)) {

            int nbytes;
            byte[] b = new byte[4096];
            while ((nbytes = ais.read(b)) >= 0) {
                if (recognizer.acceptWaveForm(b, nbytes)) {
                    System.out.println(recognizer.getResult());
                } else {
                    System.out.println(recognizer.getPartialResult());
                }
            }

            System.out.println(recognizer.getFinalResult());
        }
    }
}

这个示例代码里有两个重要点:

  1. model:也就是 new Model(“model”) 这里,这里需要我们指定模型位置
  2. sampleRate:也就是 new Recognizer(model, 16000) 这里,他这里的示例代码写死了 sampleRate 为 16000 Hz,不过每个音频的采样率不可能都一样,我需要识别的音频采样率基本都是 44100 Hz,所以这里我们需要将他改为自动识别

3.2 model 准备

我们需要实现离线语音识别,那么就得将模型下载到本地电脑。下载地址为官网的 Models 模块:https://alphacephei.com/vosk/models
我们直接找到 Chinese 分类,这里有 2 个模型,上面较小的 40 多M的是轻量级模型,适用于手机等移动设备;下面 1 个多G的适用于服务器的,很明显模型越大识别语音正确率越高
【项目管理】Java离线版语音识别-语音转文字
这里我们两个都下载,等会对比下正确率和速率,下载下来是两个压缩包,直接解压到 D 盘,等会选择路径方便(怎么方便怎么来)。
【项目管理】Java离线版语音识别-语音转文字
解压之后如下
【项目管理】Java离线版语音识别-语音转文字

3.3 测试音频准备

音频下载地址:https://download.csdn.net/download/qq_35132089/86723883
测试音频已经上传到 CSDN 的资源库,设置下载积分为0,有兴趣的小伙伴可以下载测试玩玩
这里一共准备了 8 段音频,共 62 个字

01.wav: 保家卫国
02.wav: 这个世界需要希望
03.wav: 我们的勇气绝对不能动摇
04.wav: 德玛西亚
05.wav: 正义要靠法律要么靠武力
06.wav: 为了那些不能作战的人而战
07.wav: 勇往直前
08.wav: 生命不息战斗不止

3.4 代码实现

捋清楚思路,接下来实现就比较简单了,我这里写一个 Swing 的项目,准备到时候选择 wav 文件直接语音识别,或者选择一个文件夹,解析该目录下所有的 wav 音频文件
关键代码:

import com.lijinjiang.beautyeye.ch3_button.BEButtonUI;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.wav.WavFileReader;
import org.vosk.Model;
import org.vosk.Recognizer;
import javax.sound.sampled.AudioSystem;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.io.*;

public class MainFrame {
    private JFrame mainFrame; // 主界面
    private final JPanel contentPanel = new JPanel(null); // 内容面板
    private String modelPath; // 模型位置
    private File chooseFile; // 选择的文件夹或文件
    private JTextField pathField; // 模型位置文本框
    private JTextField fileField; // 文件路径文本框
    private JTextArea displayArea; // 展示区域

    private JLabel timeLabel; // 显示耗时标签

    public MainFrame() {
        modelPath = System.getProperty("user.dir") + "/src/main/resources/vosk-model-small-cn-0.22"; // 初始化模型
        System.out.println(modelPath);
        createFrame();
    }

    /**
     * 创建主窗口
     */
    private void createFrame() {
        mainFrame = new JFrame();
        mainFrame.setTitle("语音识别");
        createOperatePanel();
        createDisplayPane();
        createTimeLabel();
        mainFrame.add(contentPanel);
        mainFrame.setSize(new Dimension(800, 600));
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setVisible(true);
    }

    /**
     * 创建操作面板
     */
    private void createOperatePanel() {
        JButton pathBtn = new JButton("选择模型");
        pathBtn.setLocation(10, 10);
        pathBtn.setSize(new Dimension(80, 36));
        pathBtn.setFocusable(false); // 不绘制焦点
        pathBtn.addActionListener(e -> showChoosePathDialog());


        pathField = new JTextField();
        pathField.setEditable(false);
        pathField.setLocation(100, 10);
        pathField.setSize(new Dimension(250, 36));


        JButton fileBtn = new JButton("选择文件");
        fileBtn.setFocusable(false); // 不绘制焦点
        fileBtn.addActionListener(e -> showChooseFileDialog());
        fileBtn.setLocation(360, 10);
        fileBtn.setSize(new Dimension(80, 36));


        fileField = new JTextField();
        fileField.setEditable(false);
        fileField.setLocation(450, 10);
        fileField.setSize(new Dimension(250, 36));

        // 开始执行按钮
        JButton startBtn = new JButton("执行");
        startBtn.addActionListener(e -> execute());
        startBtn.setUI(new BEButtonUI().setNormalColor(BEButtonUI.NormalColor.green));
        startBtn.setFocusable(false); // 不绘制焦点
        startBtn.setLocation(710, 10);
        startBtn.setSize(new Dimension(70, 36));


        contentPanel.add(pathBtn);
        contentPanel.add(pathField);
        contentPanel.add(fileBtn);
        contentPanel.add(fileField);
        contentPanel.add(startBtn);
    }

    /**
     * 创建展示面板
     */
    private void createDisplayPane() {
        JScrollPane scrollPane = new JScrollPane();
        displayArea = new JTextArea();
        scrollPane.setViewportView(displayArea);
        displayArea.setEditable(false);
        displayArea.setBorder(null);
        scrollPane.setSize(new Dimension(775, 480));
        scrollPane.setLocation(8, 56);
        contentPanel.add(scrollPane);
    }

    private void createTimeLabel() {
        timeLabel = new JLabel();
        timeLabel.setHorizontalAlignment(SwingConstants.RIGHT); // 文本靠右对齐
        timeLabel.setSize(new Dimension(100, 36));
        timeLabel.setLocation(680, 530);
        contentPanel.add(timeLabel);
    }

    /**
     * 选择模型位置
     */
    private void showChoosePathDialog() {
        JFileChooser fileChooser = new JFileChooser(); // 初始化一个文件选择器
        String pathValue = pathField.getText().trim();
        if (pathValue.length() == 0) {
            FileSystemView fsv = fileChooser.getFileSystemView(); // 获取文件系统网关
            fileChooser.setCurrentDirectory(fsv.getHomeDirectory()); // 设置桌面为当前文件路径
        } else {
            // 设置上一次选择路径为当前文件路径
            File file = new File(pathValue);
            File parentFile = file.getParentFile();
            if (parentFile == null) {
                fileChooser.setCurrentDirectory(file);
            } else {
                fileChooser.setCurrentDirectory(parentFile);
            }
        }
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // 可选文件夹和文件
        fileChooser.setMultiSelectionEnabled(false); // 设置可多选
        int result = fileChooser.showOpenDialog(mainFrame);
        if (result == JFileChooser.APPROVE_OPTION) {
            File file = fileChooser.getSelectedFile();
            modelPath = file.getAbsolutePath();
            pathField.setText(modelPath); // 将选择的文件路径写入到文本框
        }
    }

    /**
     * 选择需要转换成文字的文件夹或者文件
     * 文件夹:表示该目录下一层所有 wav 都需要转成文字
     * 文件:表示只需要将该文件转换成文字即可
     */
    private void showChooseFileDialog() {
        JFileChooser fileChooser = new JFileChooser(); // 初始化一个文件选择器
        String fileValue = fileField.getText().trim();
        if (fileValue.length() == 0) {
            FileSystemView fsv = fileChooser.getFileSystemView();
            fileChooser.setCurrentDirectory(fsv.getHomeDirectory()); // 设置桌面为当前文件路径
        } else {
            // 设置上一次选择路径为当前文件路径
            File file = new File(fileValue);
            File parentFile = file.getParentFile();
            if (parentFile == null) {
                fileChooser.setCurrentDirectory(file);
            } else {
                fileChooser.setCurrentDirectory(parentFile);
            }
        }
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); // 可选文件夹和文件
        fileChooser.setMultiSelectionEnabled(false); // 设置可多选
        fileChooser.removeChoosableFileFilter(fileChooser.getAcceptAllFileFilter()); // 不显示所有文件的下拉选
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("wav", "wav"));
        int result = fileChooser.showOpenDialog(mainFrame);
        if (result == JFileChooser.APPROVE_OPTION) {
            chooseFile = fileChooser.getSelectedFile();
            fileField.setText(chooseFile.getAbsolutePath()); // 将选择的文件路径写入到文本框
        }
    }

    /**
     * 开始执行操作
     */
    private void execute() {
        displayArea.setText(""); // 执行后清空显示面板
        if (modelPath == null || 0 == modelPath.length()) {
            JOptionPane.showMessageDialog(mainFrame, "模型位置不能为空", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        if (chooseFile == null) {
            JOptionPane.showMessageDialog(mainFrame, "未选择文件夹或者音频文件", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        long startTime = System.currentTimeMillis();

        // 用于测试进度条的线程
        Thread thread = new Thread() {

            public void run() {
                if (chooseFile.isDirectory()) { // 如果是文件夹,则遍历里面每个文件
                    File[] files = chooseFile.listFiles(pathname -> pathname.getName().endsWith(".wav"));
                    if (files != null) {
                        for (File childFile : files) processFile(childFile);
                    }
                } else {
                    processFile(chooseFile);
                }

            }

        };
        //显示进度条测试对话框
        ProgressBar.show((Frame) null, thread, "语音正在识别中,请稍后...", "执行结束", "取消");
        // 否则直接处理该文件
        long endTime = System.currentTimeMillis();
        String msg = "耗时:" + (endTime - startTime) + "ms";
        timeLabel.setText(msg);
    }

    /**
     * 处理文件:语音转文字
     */
    private void processFile(File file) {
        try (Model model = new Model(modelPath);//该段是模型地址
             InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(file))); //该段是要转的语言文件,仅支持wav
             Recognizer recognizer = new Recognizer(model, getSampleRate(file))) { //该段中12000是语言频率(Hz),需要大于8000,可以自行调整

            int bytes;
            byte[] b = new byte[4096];
            while ((bytes = ais.read(b)) >= 0) {
                recognizer.acceptWaveForm(b, bytes);
            }
            displayArea.append(file.getName() + " ");
            displayArea.append(recognizer.getFinalResult() + System.lineSeparator());
        } catch (Exception e) {
            JOptionPane.showMessageDialog(mainFrame, e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
    }

    /**
     * 获取音频文件的采样率
     */
    private Float getSampleRate(File file) throws Exception {
        WavFileReader fileReader = new WavFileReader();
        AudioFile audioFile = fileReader.read(file);
        String sampleRate = audioFile.getAudioHeader().getSampleRate();
        return Float.parseFloat(sampleRate);
    }
}

4.效果演示

4.1 界面效果

界面效果就是如下图所示

  1. 点击选择模型,就可以指定模型文件夹路径
  2. 点击选择文件,就可以指定需要识别的语音或文件夹
  3. 最后点击执行即可开始语音识别
  4. 识别成功,会将对应音频文件名和识别的文字写在下面的文本域中
  5. 最后还会将使用时间显示在界面右下角

【项目管理】Java离线版语音识别-语音转文字

4.2 单个文件语音识别

4.2.1 轻量模型

这里选择模型选择轻量模型,文件只识别第一个文件
【项目管理】Java离线版语音识别-语音转文字

4.2.2 通用模型

这里模型替换为了通用模型,语音文件不变,然后执行
【项目管理】Java离线版语音识别-语音转文字

4.2.3 两者对比

单个文件语音识别 轻量模型 通用模型
正确率 100% 100%
消耗时间 2021 ms 21093 ms
这里我们可以发现同一个文件,轻量模型只用了 1/10 的时间就识别完成,且成功率均为 100%,不过这也存在这段语言比较简单的原因

4.3 多个语音文件识别

4.3.1 轻量模型

这里的模型还是选择轻量模型,语音文件选择整个语音文件夹
【项目管理】Java离线版语音识别-语音转文字

4.3.2 通用模型

这里模型替换为了通用模型,语音文件不变,然后执行;因为这里耗费时间太长了,GIF 做了抽帧处理
【项目管理】Java离线版语音识别-语音转文字

4.3.3 两者对比

多个文件语音识别 轻量模型 通用模型
正确率 71.43% 84.12%
消耗时间 14332 ms 176040 ms
这里我们可以发现同一个文件,轻量模型只用了 8% 的时间就识别完成,不过这里的正确率只有 71% 左右,通用模型的正确率却有 84% 左右,当然因为其中存在一些专有名词如德玛西亚,两次都没有识别出来

5.项目总结

综合比较下来我们基本可以得出结论:如果对正确率要求没那么高的情况下,轻量模型完全符合我们的要求;而且通用模型的时间消耗确实太大了,可能需要一些方法来减少时间的消耗。文章来源地址https://www.toymoban.com/news/detail-443058.html

到了这里,关于【项目管理】Java离线版语音识别-语音转文字的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java版工程项目管理系统源码+系统管理+系统设置+项目管理+合同管理+二次开发em

    ​   鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管理的提升提出了

    2024年02月13日
    浏览(53)
  • 企业工程项目管理系统源码-专注项目数字化管理-Java工程管理-二次开发

        工程项目各模块及其功能点清单 一、系统管理     1、数据字典:实现对数据字典标签的增删改查操作     2、编码管理:实现对系统编码的增删改查操作     3、用户管理:管理和查看用户角色     4、菜单管理:实现对系统菜单的增删改查操作     5、角色管理:管理

    2024年02月07日
    浏览(55)
  • Java版spring cloud 本工程项目管理系统源码-全面的工程项目管理

     ​  工程项目各模块及其功能点清单 一、系统管理     1、数据字典:实现对数据字典标签的增删改查操作     2、编码管理:实现对系统编码的增删改查操作     3、用户管理:管理和查看用户角色     4、菜单管理:实现对系统菜单的增删改查操作     5、角色管

    2024年02月15日
    浏览(41)
  • JAVA项目--银行管理系统

    1、项目描述 银行管理系统:实现登录、注册,登录后的用户可以进行存款、取款、转账、查询余额操作 2、分析 创建银行类Bank:用于存储银行卡信息,并创建银行系统的主窗体 创建银行卡类BankCard:初始化银行卡信息 创建用户操作类User:进行存款、取款、转账、查询余额

    2024年02月11日
    浏览(37)
  • 【Java】学生管理系统项目演示

    目录 学生管理系统 学生管理系统代码思路分析 nextLine() 和 nextInt() 区别 需求:实现对学生的增删改查功能,学生(学号,姓名,年龄,地址)字段 定义学生 Student 实体类 成员属性 (学号,姓名,年龄,地址); 定义容器(ArrayList) 集合存入对象; 定义StudentManage 对 Stu

    2024年02月07日
    浏览(35)
  • Java版本的工程项目管理系统源代码之工程项目管理系统面临的挑战

    ​ ​工程项目管理系统是指从事工程项目管理的企业(以下简称工程项目管理企业)受业主委托,按照合同约定,代表业主对工程项目的组织实施进行全过程或若干阶段的管理和服务。 ​系统定义 工程项目管理企业不直接与该工程项目的总承包企业或勘察、设计、供货、施

    2024年02月03日
    浏览(46)
  • 23基于java教师科研项目管理系统

    目前许多人仍将传统的纸质工具作为信息管理的主要工具,而网络技术的应用只是起到辅助作用。在对网络工具的认知程度上,较为传统的office软件等仍是人们使用的主要工具,而相对全面且专业的信息管理软件仍没有得到大多数人的了解或认可。本选题则旨在通过标签分类

    2024年02月02日
    浏览(44)
  • 【功能超全】基于OpenCV车牌识别停车场管理系统软件开发【含python源码+PyqtUI界面+功能详解】-车牌识别python 深度学习实战项目

    摘要: 车牌识别系统(Vehicle License Plate Recognition,VLPR) 是指能够检测到受监控路面的车辆并自动提取车辆牌照信息(含汉字字符、英文字母、阿拉伯数字及号牌颜色)进行处理的技术。车牌识别是现代智能交通系统中的重要组成部分之一,应用十分广泛。本文详细介绍了 车牌

    2024年02月09日
    浏览(63)
  • Java版本工程管理系统源码&企业工程项目管理系统简介

          一、立项管理 1、招标立项申请 功能点:招标类项目立项申请入口,用户可以保存为草稿,提交。 2、非招标立项申请 功能点:非招标立项申请入口、用户可以保存为草稿、提交。 3、采购立项列表 功能点:对草稿进行编辑,驳回的立项编辑,在途流程查看。 二、项

    2024年02月15日
    浏览(56)
  • 基于java的项目管理平台系统设计与实现

    基于java的项目管理平台系统设计与实现 研究背景: 随着企业业务的不断扩张和复杂化,项目管理工作成为了企业运营过程中不可或缺的一部分。传统的项目管理方式通常依赖于手工管理和纸质文档,存在着信息传递不及时、项目管理效率低下、资源利用率不高等问题。为了

    2024年02月02日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包