【Android +Tensroflow Lite】实现从基于机器学习语音中识别指令讲解及实战(超详细 附源码)
创始人
2024-02-21 12:33:47

需要源码和配置文件请点赞关注收藏后评论区留言~~~

一、基于机器学习的语音推断

Tensorflow基于分层和模块化的设计思想,整个框架以C语言的编程接口为界,分为前端和后端两大部分 Tensorflow框架结构如下图

 二、Tensorflow Lite简介

虽然Tensorflow是一款十分优秀的机器学习框架,但是它层次众多,不适合在单个设备上独立运行,为此Google推出了Tensorflow Lite,也就是Tensorflow的精简版,它可以在移动设备,嵌入式设备和物联网设备上运行Tensorflow模型

Tensorflow Lite包括下列两个主要组件

Tensorflow Lite解释器 允许在设备端的不同硬件上运行优化过的模型

Tensorflow Lite转换器 将Tensorflow模型转换为解释器使用的格式 同时通过优化提高应用性能

Tensorflow Lite允许在网络边缘的设备上执行机器学习任务,无须在设备与服务器之间来回发送数据 对开发者来说 在设备端执行机器学习任务有以下好处

缩短延迟 数组无须往返服务器

保护隐私 任何数据都不会离开设备

减少连接 不需要互联网连接

降低功耗 网络连接非常耗电 

三、从语音中识别指令实战 

首先给App工程手工添加Tensorflow Lite支持

implementation 'org.tensorflow:tensorflow-lite:2.5.0'

同时还要引入语音识别的配置文件 请点赞关注收藏后评论区留言私信博主

然后在活动代码中初始化Tensorflow Lite 分别读取标签配置 加载模型文件 

运行效果如下

语音识别支持App支持识别英文单词指令 识别到的指令会高亮显示在App界面 并且标出吻合度

需要对着手机大声朗读上述英文单词 就可观察到语音推断结果

所以此处连接真机测试效果更好 模拟机不好录制语音~~~

 

 

 四、代码

部分源码如下 需要全部代码请点赞关注收藏后评论区留言~~~

package com.example.voice;import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import com.example.voice.adapter.WordRecyclerAdapter;
import com.example.voice.bean.WordInfo;
import com.example.voice.tensorflow.RecognizeCommands;import org.tensorflow.lite.Interpreter;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;public class VoiceInferenceActivity extends AppCompatActivity {private final static String TAG = "VoiceInferenceActivity";private TextView tv_cost; // 声明一个文本视图对象private WordRecyclerAdapter mAdapter; // 英语单词的循环适配器private String[] mWordArray = new String[]{"Yes", "No", "Up", "Down", "Left", "Right", "On", "Off", "Stop", "Go"};private List mWordList = new ArrayList<>(); // 单词信息列表@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_voice_inference);initView(); // 初始化视图initTensorflow(); // 初始化Tensorflow}// 初始化视图private void initView() {TextView tv_rate = findViewById(R.id.tv_rate);tv_rate.setText(SAMPLE_RATE + " Hz");tv_cost = findViewById(R.id.tv_cost);for (String word : mWordArray) {mWordList.add(new WordInfo(word, null));}RecyclerView rv_word = findViewById(R.id.rv_word);GridLayoutManager manager = new GridLayoutManager(this, 2);rv_word.setLayoutManager(manager);mAdapter = new WordRecyclerAdapter(this, mWordList);rv_word.setAdapter(mAdapter);}private static final int SAMPLE_RATE = 16000;private static final int SAMPLE_DURATION_MS = 1000;private static final int RECORDING_LENGTH = (int) (SAMPLE_RATE * SAMPLE_DURATION_MS / 1000);private static final long AVERAGE_WINDOW_DURATION_MS = 1000;private static final float DETECTION_THRESHOLD = 0.50f;private static final int SUPPRESSION_MS = 1500;private static final int MINIMUM_COUNT = 3;private static final long MINIMUM_TIME_BETWEEN_SAMPLES_MS = 30;private static final String LABEL_FILENAME = "conv_actions_labels.txt";private static final String MODEL_FILENAME = "conv_actions_frozen.tflite";// Working variables.private short[] recordBuffer = new short[RECORDING_LENGTH];private int recordOffset = 0;private boolean continueRecord = true;private Thread recordThread;private boolean continueRecognize = true;private Thread recognizeThread;private final ReentrantLock recordBufferLock = new ReentrantLock();private List labelList = new ArrayList<>(); // 指令标签列表private RecognizeCommands recognizeCommands = null; // 待识别的指令private Interpreter.Options tfLiteOptions = new Interpreter.Options(); // 解释器选项private Interpreter tfLite; // Tensorflow Lite的解释器private long costTime; // 每次语音识别的耗费时间// 初始化Tensorflowprivate void initTensorflow() {Log.d(TAG, "Reading labels from: " + LABEL_FILENAME);try (BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open(LABEL_FILENAME)))) {String line;while ((line = br.readLine()) != null) {labelList.add(line);}} catch (Exception e) {throw new RuntimeException("Problem reading label file!", e);}Log.d(TAG, "labelList.size()=" + labelList.size());// 设置一个对象来平滑识别结果,以提高准确率recognizeCommands = new RecognizeCommands(labelList,AVERAGE_WINDOW_DURATION_MS,DETECTION_THRESHOLD,SUPPRESSION_MS,MINIMUM_COUNT,MINIMUM_TIME_BETWEEN_SAMPLES_MS);try {MappedByteBuffer tfLiteModel = loadModelFile(getAssets(), MODEL_FILENAME);tfLite = new Interpreter(tfLiteModel, tfLiteOptions);} catch (Exception e) {throw new RuntimeException(e);}tfLite.resizeInput(0, new int[]{RECORDING_LENGTH, 1});tfLite.resizeInput(1, new int[]{1});startRecord(); // 开始录音startRecognize(); // 开始识别}private MappedByteBuffer loadModelFile(AssetManager assets, String modelFilename) throws Exception {Log.d(TAG, "modelFilename="+modelFilename);AssetFileDescriptor descriptor = assets.openFd(modelFilename);FileInputStream fis = new FileInputStream(descriptor.getFileDescriptor());FileChannel fileChannel = fis.getChannel();long startOffset = descriptor.getStartOffset();long declaredLength = descriptor.getDeclaredLength();return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);}// 开始录音public synchronized void startRecord() {if (recordThread != null) {return;}continueRecord = true;recordThread = new Thread(() -> record());recordThread.start();}// 停止录音public synchronized void stopRecord() {if (recordThread == null) {return;}continueRecord = false;recordThread = null;}// 录制音频private void record() {android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);// Estimate the buffer size we'll need for this device.int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) {bufferSize = SAMPLE_RATE * 2;}short[] audioBuffer = new short[bufferSize / 2];AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.DEFAULT,SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT,bufferSize);if (record.getState() != AudioRecord.STATE_INITIALIZED) {Log.e(TAG, "Audio Record can't initialize!");return;}record.startRecording();Log.d(TAG, "Start record");// Loop, gathering audio data and copying it to a round-robin buffer.while (continueRecord) {int numberRead = record.read(audioBuffer, 0, audioBuffer.length);int maxLength = recordBuffer.length;int newRecordOffset = recordOffset + numberRead;int secondCopyLength = Math.max(0, newRecordOffset - maxLength);int firstCopyLength = numberRead - secondCopyLength;// We store off all the data for the recognition thread to access. The ML// thread will copy out of this buffer into its own, while holding the// lock, so this should be thread safe.recordBufferLock.lock();try {System.arraycopy(audioBuffer, 0, recordBuffer, recordOffset, firstCopyLength);System.arraycopy(audioBuffer, firstCopyLength, recordBuffer, 0, secondCopyLength);recordOffset = newRecordOffset % maxLength;} finally {recordBufferLock.unlock();}}record.stop();record.release();}// 开始识别public synchronized void startRecognize() {if (recognizeThread != null) {return;}continueRecognize = true;recognizeThread = new Thread(() -> recognize());recognizeThread.start();}// 停止识别public synchronized void stopRecognize() {if (recognizeThread == null) {return;}continueRecognize = false;recognizeThread = null;}// 识别语音private void recognize() {Log.d(TAG, "Start recognition");short[] inputBuffer = new short[RECORDING_LENGTH];float[][] floatInputBuffer = new float[RECORDING_LENGTH][1];float[][] outputScores = new float[1][labelList.size()];int[] sampleRateList = new int[]{SAMPLE_RATE};// Loop, grabbing recorded data and running the recognition model on it.while (continueRecognize) {long startTime = System.currentTimeMillis();// The record thread places data in this round-robin buffer, so lock to// make sure there's no writing happening and then copy it to our own// local version.recordBufferLock.lock();try {int maxLength = recordBuffer.length;int firstCopyLength = maxLength - recordOffset;int secondCopyLength = recordOffset;System.arraycopy(recordBuffer, recordOffset, inputBuffer, 0, firstCopyLength);System.arraycopy(recordBuffer, 0, inputBuffer, firstCopyLength, secondCopyLength);} finally {recordBufferLock.unlock();}// We need to feed in float values between -1.0f and 1.0f, so divide the// signed 16-bit inputs.for (int i = 0; i < RECORDING_LENGTH; ++i) {floatInputBuffer[i][0] = inputBuffer[i] / 32767.0f;}Object[] inputArray = {floatInputBuffer, sampleRateList};Map outputMap = new HashMap<>();outputMap.put(0, outputScores);// Run the model.tfLite.runForMultipleInputsOutputs(inputArray, outputMap);// Use the smoother to figure out if we've had a real recognition event.final RecognizeCommands.RecognitionResult result =recognizeCommands.processLatestResults(outputScores[0], System.currentTimeMillis());costTime = System.currentTimeMillis() - startTime;runOnUiThread( () -> {tv_cost.setText(costTime + " ms");// If we do have a new command, highlight the right list entry.if (!result.foundCommand.startsWith("_") && result.isNewCommand) {int position = labelList.indexOf(result.foundCommand) - 2;WordInfo word = mWordList.get(position);word.percent = Math.round(result.score * 100) + "%";mWordList.set(position, word);mAdapter.notifyItemChanged(position);new Handler(Looper.myLooper()).postDelayed(() -> {word.percent = "";mWordList.set(position, word);mAdapter.notifyItemChanged(position);}, 1500);}});try {// We don't need to run too frequently, so snooze for a bit.Thread.sleep(MINIMUM_TIME_BETWEEN_SAMPLES_MS);} catch (InterruptedException e) {}}Log.d(TAG, "End recognition");}}

创作不易 觉得有帮助请点赞关注收藏~~~

相关内容

热门资讯

【看表情包学Linux】进程地...   🤣 爆笑教程 👉 《看表情包学Linux》👈 猛...
吉字五行及吉凶 吉字五行中代表... 五行解析在文化中,五行是非常重要的概念之一,在这里解析一下五行对于人们生活的影响。首先,金属代表的是...
月老姻缘灵签内容详解大全 月老... 月老灵签 姻缘签44签 求解签君尔目下之人。本是可心满意足之人。焉知后来之人。一个比一个更美好。就此...
六爻排盘蛇 六爻排盘预测绝招 ... 六爻排盘结果怎么看纳甲六爻在线排盘姓名:出生年:1981性别:男占事:起卦方式:手动摇卦公历时间:2...
吉凶由情绪决定 每日吉凶 每月... 情绪的力量情绪是我们生活中一个重要的组成部分。我们每天都会通过不同的方式感受到情绪的存在,而情绪的质...
吉凶悔吝的解释是什么 风水形势... 吉凶悔吝的解释是什么从古至今,人们对于吉凶悔吝都有着不同的看法。所谓吉,是指好的运气,让人们沾沾自喜...
如何看每日生肖运势 每日生肖运... 背景说明每个人都希望自己的运势越来越好,而对于人来说,生肖运势是一个参考价值很高的判断标准。按照传统...
最准观音灵签21签解签 观音灵... 观音灵签21签解签-遵医嘱,健康长寿观音精神签证是中国民间宗教信仰的重要形式,也是一种广泛流传的祈祷...
梦见红裤子被水冲走 梦见河里洗... 梦见红裤子被水冲走红裤子是一种比较鲜艳的颜色,在梦中出现可能代表着某种情绪或状态。而被水冲走则更加具...
十二星座对象配对 12星座最佳... 12星座配偶标准白羊座:温柔善良的人乐观单纯的白羊座在恋爱时喜欢另一半无条件的宠爱自己,另一半对自己...
带水又带土的名字女孩名字有哪些... 含水和土的字有哪些含水的字:淦、澜、浸、泼、滴、没、汪、沸、鸿、沔、浩、渣、溢、潺江、注、漭、淬、澧...
六爻失物卦 在线占卜失物 六爻... 六爻占卜 寻找失物公历起卦时间:2012年12月24日9时44分(按公历时间起卦)农历:仁辰年十一月...
天网今日生肖运势 每日特吉生肖... 天网今日生肖运势天网今日,十二生肖依旧是重要的关键词之一。根据传统文化和民间信仰,每个人都属于一个生...
六爻代表书籍 六爻预测好的书籍... 学六爻的书籍那些比较经典,最好适合初学者的。从古至今六爻类的书流传于世的非常少,六爻类最经典的几本书...
十二星座女生专属花卉 小葩画1... 狮子座的女生喜欢什么样的花1.狮子座的女生喜欢鲜艳、华丽、高贵的花。2.狮子座的女生通常有着自信、热...
客厅风水禁忌及化解 客厅推拉门... 客厅风水禁忌及解决方案客厅是家庭中最重要的空间之一,也是最容易受到风水影响的空间之一。在客厅里,我们...
吉凶参半牛兔在含义 牛兔相冲到... 吉凶参半牛兔在啥意思吉凶参半牛兔在是指属牛的和属兔的结婚以后生活吉凶各一半。丑牛与子鼠六合,因此最宜...
各生肖属相的车牌号码吉凶对照表... 十二生肖与车牌号的佳搭配 十二生肖车牌号吉凶对照表通常每个人的黄道十二宫都会影响车牌号码的运行模式,...
客厅西部尖角的风水 客厅有棱角... 客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?房子的方形风水是...
带昶字的女孩名字 带滢的女孩名... 长字命名的寓意及意义长字命名的寓意和意义是正直、坚强、努力、阳光、前途似景、忠诚。长是一个通用词。长...
六壬怎么算命 六壬掐指神算金口... 什么是六仁?刘仁是中国古代的算命方法,起源于汉代,是中国道教学派的经典之一。刘仁包括六个神:天乙、天...
四月二十九生肖运势 十二生肖鸡... 女1993农历四月二十九早上十点生辰八字是什么如何出生时间:公历 1993年 6月 18日 10点本...
带心字的游戏女孩名字大全集 游... 2020男孩怎么起名有内涵 带心字的男孩名字大全心繁体:心起名五行:金姓名学笔画:4画简体笔画:4画...
八字鬼谷子算命 鬼谷子精髓50... 什么是八字鬼谷子算命?八字鬼谷子算命,又称李静算命,是中国传统的民间算命方式。八字鬼谷子算命起源于六...
十二星座下个月的运势女生 十二... 白羊座下个月的运势女生白羊座女生本身就充满了无限活力和热情,下个月的运势也不会让你失望。职业上可能会...
号令天下手机吉凶预测 号令天下... 手机号怎么算吉凶?用最后四个手机号码除以80,然后减去整数部分(只留小数),再乘以80,就会得到一个...
命理十二生肖今年运势 明天运势... 命理十二生肖今年运势今年每年都有不同的转瞬即逝的岁月。对于不同的黄道十二宫来说,它每年都有自己独特的...
八字长生好吗 八字中帝旺到长生... 八字日坐长生一定富吗丁火曰元生于未月,余气通根,年支丙火也能助身,但于上两透旺食,生财耗身过甚,故命...
带日的名字女孩名字大全 起名带... 日字旁边的女孩名字大全日字旁边的女孩名字推荐1、诗晗、慧曦、Xi、仲晴2、小芸、小娟、会晴、若昕、敏...
号令天下固话号码测吉凶 查电话... 周易81测手机号码吉凶,号令天下手机号码测吉凶提起周易81测手机号码吉凶,大家都知道,有人问天下手机...