嗯,这个代码中 ,当前特征值的画图好像没有 显示出来,请问是被覆盖了么,请给出完整修改代码
classdef SVMSpeechRecognitionGUI < matlab.apps.AppBase
% 属性
properties (Access = public)
UIFigure matlab.ui.Figure
RecordButton matlab.ui.control.Button
PlayButton matlab.ui.control.Button
RecognizeButton matlab.ui.control.Button
ResultLabel matlab.ui.control.Label
AudioAxes matlab.ui.control.UIAxes
FeatureAxes matlab.ui.control.UIAxes
Model % SVM模型
FeatureMean % 特征均值
FeatureStd % 特征标准差
AudioData % 录制的音频数据
SampleRate = 16000; % 采样率
IsRecording = false; % 录音状态
Recorder % 录音对象
Categories = {'0','1','2','3','4','5','6','极7','8','9','+','-','×','÷','='}; % 类别标签
FeatureLegend matlab.ui.control.Label % 特征图例标签
ClassFeatures % 各类别平均特征(用于可视化)
end
% 公共构造函数
methods (Access = public)
function app = SVMSpeechRecognitionGUI(model)
% 加载模型和标准化参数
try
modelData = load('svm_model.mat');
app.Model = modelData.model;
app.FeatureMean = modelData.featureMean;
app.FeatureStd = modelData.featureStd;
% 尝试加载类别平均特征(用于可视化)
if isfield(modelData, 'classFeatures')
app.ClassFeatures = modelData.classFeatures;
else
app.ClassFeatures = [];
end
catch ME
% 如果加载失败,使用传入的模型
if exist('model', 'var') && ~isempty(model)
app.Model = model;
% 尝试从文件加载标准化参数
if exist('svm_model.mat', 'file')
modelData = load('svm_model.mat');
app.FeatureMean = modelData.featureMean;
app.FeatureStd = modelData.featureStd;
if isfield(modelData, 'classFeatures')
app.ClassFeatures = modelData.classFeatures;
end
else
% 默认标准化参数
app.FeatureMean = zeros(1, 40);
app.FeatureStd = ones(1, 40);
app.ClassFeatures = [];
warning('标准化参数未找到,使用默认值');
end
else
uialert([], sprintf('加载模型失败: %s', ME.message), '错误');
delete(app);
return;
end
end
% 创建UI
createComponents(app);
% 注册关闭事件
app.UIFigure.CloseRequestFcn = @(~,~)closeRequest(app);
% 初始化图形
initializePlots(app);
% 显示UI
app.UIFigure.Visible = 'on';
end
end
% 私有方法
methods (Access = private)
% 窗口关闭请求函数
function closeRequest(app)
% 停止录音并释放资源
if app.IsRecording && ~isempty(app.Recorder) && isrecording(app.Recorder)
stop(app.Recorder);
end
% 删除UI
delete(app.UIFigure);
end
% 初始化图形
function initializePlots(app)
% 初始化音频波形图
plot(app.AudioAxes, 0, 0);
title(app.AudioAxes, '等待录音...');
xlabel(app.AudioAxes, '时间(s)');
ylabel(app.AudioAxes, '幅度');
grid(app.AudioAxes, 'on');
% 初始化特征图
bar(app.FeatureAxes, zeros(1, 40));
title(app.FeatureAxes, '特征向量');
xlabel(app.FeatureAxes, '特征索引');
ylabel(app.FeatureAxes, '特征值');
grid(app.FeatureAxes, 'on');
% 设置特征图属性
app.FeatureAxes.XLabel.FontSize = 12;
app.FeatureAxes.YLabel.FontSize = 12;
app.FeatureAxes.FontSize = 10;
app.FeatureAxes.XLim = [0, 41];
% 创建特征图例
featureTypes = {'均值', '标准差', '能量', '熵', '过零率', '最大值', '最小值', '峰度', '偏度', '中值'};
legendText = sprintf('特征类型:\n');
for i = 1:length(featureTypes)
legendText = sprintf('%s%d. %s\n', legendText, i, featureTypes{i});
end
app.FeatureLegend = uilabel(app.UIFigure, ...
'Position', [700, 50, 150, 120], ...
'Text', legendText, ...
'FontSize', 9, ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'Visible', 'off'); % 初始不可见
end
% 创建UI组件
function createComponents(app)
% 创建主窗口(增加高度以容纳更多内容)
app.UIFigure = uifigure('Name', 'SVM语音识别系统', ...
'Position', [100 100 900 600]);
% 创建录音按钮
app.RecordButton = uibutton(app.UIFigure, 'push', ...
'Text', '开始录音', ...
'Position', [50 550 100 30], ...
'ButtonPushedFcn', @(src,event)recordButtonPushed(app));
% 创建播放按钮
app.PlayButton = uibutton(app.UIFigure, 'push', ...
'Text', '播放录音', ...
'Position', [170 550 100 30], ...
'ButtonPushedFcn', @(src,event)playButtonPushed(app));
% 创建识别按钮
app.RecognizeButton = uibutton(app.UIFigure, 'push', ...
'Text', '识别语音', ...
'Position', [290 550 100 30], ...
'ButtonPushedFcn', @(src,event)recognizeButtonPushed(app));
% 创建结果显示标签
app.ResultLabel = uilabel(app.UIFigure, ...
'Position', [410 550 300 30], ...
'Text', '结果: ', ...
'FontSize', 16, ...
'FontWeight', 'bold');
% 创建音频波形图
app.AudioAxes = uiaxes(app.UIFigure, 'Position', [50 350 800 180]);
title(app.AudioAxes, '音频波形');
xlabel(app.AudioAxes, '时间(s)');
ylabel(app.AudioAxes, '幅度');
grid(app.AudioAxes, 'on');
% 创建特征向量图(增加高度)
app.FeatureAxes = uiaxes(app.UIFigure, 'Position', [50 100 800 220]);
title(app.FeatureAxes, '特征向量');
xlabel(app.FeatureAxes, '特征索引');
ylabel(app.FeatureAxes, '特征值');
grid(app.FeatureAxes, 'on');
% 设置特征图属性
app.FeatureAxes.XLabel.FontSize = 12;
app.FeatureAxes.YLabel.FontSize = 12;
app.FeatureAxes.FontSize = 10;
app.FeatureAxes.XLim = [0, 41];
end
% 录音按钮回调(参考CNN方法改进)
function recordButtonPushed(app)
if ~app.IsRecording
% 开始录音
app.RecordButton.Text = '停止录音';
app.IsRecording = true;
app.AudioData = []; % 清空旧数据
% 创建录音对象
app.Recorder = audiorecorder(app.SampleRate, 16, 1);
record(app.Recorder);
% 更新状态
app.ResultLabel.Text = '录音中...';
app.ResultLabel.FontColor = [0 0 0]; % 黑色
% 隐藏特征图例
app.FeatureLegend.Visible = 'off';
% 实时更新波形(使用循环)
while app.IsRecording && isrecording(app.Recorder)
pause(0.1); % 更新间隔100ms
try
audioData = getaudiodata(app.Recorder);
if ~isempty(audioData)
t = (0:length(audioData)-1)/app.SampleRate;
plot(app.AudioAxes, t, audioData);
title(app.AudioAxes, sprintf('录音中 (时长: %.2fs)', t(end)));
drawnow;
end
catch
% 忽略绘图错误
end
end
% 录音结束后处理
app.RecordButton.Text = '开始录音';
app.IsRecording = false;
% 确保Recorder有效
if ~isempty(app.Recorder) && isvalid(app.Recorder)
stop(app.Recorder);
% 获取音频数据
app.AudioData = getaudiodata(app.Recorder);
else
app.AudioData = [];
end
% 端点检测
if ~isempty(app.AudioData)
app.AudioData = app.detectSpeechEndpoints(app.AudioData, app.SampleRate);
% 显示最终波形
if ~isempty(app.AudioData)
t = (0:length(app.AudioData)-1)/app.SampleRate;
plot(app.AudioAxes, t, app.AudioData);
title(app.AudioAxes, sprintf('录音波形 (时长: %.2fs)', t(end)));
% 更新状态
app.ResultLabel.Text = '录音完成!';
app.ResultLabel.FontColor = [0 0.5 0]; % 绿色
else
app.ResultLabel.Text = '录音失败或无声!';
app.ResultLabel.FontColor = [1 0 0]; % 红色
end
else
app.ResultLabel.Text = '录音失败!';
app.ResultLabel.FontColor = [1 0 0]; % 红色
end
else
% 停止录音
app.IsRecording = false;
end
end
% 端点检测函数(基于CNN方法改进)
function audioOut = detectSpeechEndpoints(~, audioIn, fs)
% 简单端点检测算法
frameSize = round(0.025 * fs); % 25ms帧
frameStep = round(0.01 * fs); % 10ms步长
% 确保音频长度大于帧长
if length(audioIn) < frameSize
audioOut = audioIn; % 返回原始音频
return;
end
% 计算短时能量
numFrames = floor((length(audioIn)-frameSize)/frameStep)+1;
energy = zeros(1, numFrames);
for i = 1:numFrames
startIdx = (i-1)*frameStep+1;
endIdx = startIdx + frameSize - 1;
frame = audioIn(startIdx:endIdx);
energy(i) = sum(frame.^2);
end
% 归一化能量
if max(energy) > 0
energy = energy / max(energy);
else
% 如果能量全为0,则返回原始音频
audioOut = audioIn;
return;
end
% 设置阈值
threshold = 0.05; % 能量阈值
% 检测语音起点
startIdx = find(energy > threshold, 1, 'first');
% 检测语音终点
endIdx = find(energy > threshold, 1, 'last');
if isempty(startIdx) || isempty(endIdx)
audioOut = []; % 无声
else
% 扩展边界确保包含完整语音
startSample = max(1, (startIdx-5)*frameStep); % 提前5帧
endSample = min(length(audioIn), (endIdx+5)*frameStep+frameSize); % 延后5帧
% 提取有效语音段
audioOut = audioIn(startSample:endSample);
end
end
% 播放按钮回调(参考CNN方法改进)
function playButtonPushed(app)
if ~isempty(app.AudioData)
sound(app.AudioData, app.SampleRate);
app.ResultLabel.Text = '播放中...';
app.ResultLabel.FontColor = [0 0.5 0]; % 绿色
% 计算播放时间
duration = length(app.AudioData)/app.SampleRate;
startTime = tic;
while toc(startTime) < duration
% 实时更新进度
elapsed = toc(startTime);
progress = min(elapsed/duration, 1);
% 更新波形显示
t = (0:length(app.AudioData)-1)/app.SampleRate;
plot(app.AudioAxes, t, app.AudioData);
hold(app.AudioAxes, 'on');
plot(app.AudioAxes, [elapsed, elapsed], ylim(app.AudioAxes), 'r-', 'LineWidth', 2);
hold(app.AudioAxes, 'off');
title(app.AudioAxes, sprintf('播放中 (进度: %.1f%%)', progress*100));
drawnow;
end
app.ResultLabel.Text = '播放完成!';
plot(app.AudioAxes, (0:length(app.AudioData)-1)/app.SampleRate, app.AudioData);
title(app.AudioAxes, sprintf('录音波形 (时长: %.2fs)', duration));
else
app.ResultLabel.Text = '无录音数据!';
app.ResultLabel.FontColor = [1 0 0]; % 红色
end
end
% 识别按钮回调(增强特征可视化)
function recognizeButtonPushed(app)
if isempty(app.AudioData)
app.ResultLabel.Text = '请先录音!';
app.ResultLabel.FontColor = [1 0 0]; % 红色
return;
end
try
% 特征提取
features = extractDWTFeatures(app, app.AudioData);
% 显示特征向量 - 改进可视化
cla(app.FeatureAxes); % 清除之前的图形
% 标准化特征用于预测
normalizedFeatures = (features - app.FeatureMean) ./ app.FeatureStd;
% 预测
predicted = predict(app.Model, normalizedFeatures);
% 显示结果
resultText = ['识别结果: ', char(app.Categories{predicted})];
app.ResultLabel.Text = resultText;
app.ResultLabel.FontColor = [0 0 1]; % 蓝色
% 绘制当前特征(蓝色)
bar(app.FeatureAxes, features, 'FaceColor', [0.2 0.4 0.8], 'BarWidth', 0.6);
hold(app.FeatureAxes, 'on');
% 如果可用,绘制该类别的平均特征(红色)
if ~isempty(app.ClassFeatures)
% 获取该类别对应的平均特征(注意:模型中的特征已经标准化,但这里我们显示原始量级)
classMean = app.ClassFeatures(predicted, :) .* app.FeatureStd + app.FeatureMean;
plot(app.FeatureAxes, 1:40, classMean, 'r-o', 'LineWidth', 2, 'MarkerSize', 5);
% 添加图例
legend(app.FeatureAxes, {'当前特征', '类别平均特征'}, 'Location', 'best');
else
legend(app.FeatureAxes, {'当前特征'}, 'Location', 'best');
end
hold(app.FeatureAxes, 'off');
% 设置坐标轴属性
title(app.FeatureAxes, sprintf('%s的特征向量', resultText));
xlabel(app.FeatureAxes, '特征索引');
ylabel(app.FeatureAxes, '特征值');
grid(app.FeatureAxes, 'on');
% 添加特征组标签(D1, D2, D3, D4)
yLim = ylim(app.FeatureAxes);
labelY = yLim(1) - 0.05 * diff(yLim);
for k = 1:4
text(app.FeatureAxes, (k-1)*10+5, labelY, ...
sprintf('D%d', k), ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'top', ...
'FontSize', 11, ...
'FontWeight', 'bold');
end
% 在特征图上添加特征类型标记
featureTypes = {'Mean','Std','Energy','Entropy','ZCR','Max','Min','Kurt','Skew','Med'};
for group = 0:3
for ftype = 1:10
idx = group*10 + ftype;
text(app.FeatureAxes, idx, labelY + 0.01*diff(yLim), ...
featureTypes{ftype}, ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'bottom', ...
'FontSize', 8, ...
'Rotation', 90);
end
end
% 显示特征图例
app.FeatureLegend.Visible = 'on';
catch ME
app.ResultLabel.Text = '识别失败!';
app.ResultLabel.FontColor = [1 0 0]; % 红色
uialert(app.UIFigure, sprintf('错误: %s', ME.message), '识别错误');
end
end
% DWT特征提取函数(增强版,防止NaN)
function features = extractDWTFeatures(~, audio)
% 空音频处理
if isempty(audio) || all(audio == 0)
features = nan(1, 40);
return;
end
% 确保音频长度合适
targetLength = 16000; % 1秒@16kHz
if length(audio) > targetLength
audio = audio(1:targetLength);
elseif length(audio) < targetLength
audio = [audio; zeros(targetLength-length(audio), 1)];
end
% DWT特征提取
wavelet = 'db4';
level = 4;
[C, L] = wavedec(audio, level, wavelet);
% 提取特征
features = zeros(1, 40); % 40维特征向量
% 各层细节系数的统计特征
for k = 1:level
d = detcoef(C, L, k);
startIdx = (k-1)*10 + 1;
% 安全特征计算
features(startIdx) = mean(d); % 均值
% 标准差(防除0)
if numel(d) > 1
features(startIdx+1) = std(d);
else
features(startIdx+1) = 0;
end
features(startIdx+2) = sum(d.^2); % 能量
% 熵(防log(0))
if all(d == d(1)) || numel(d) < 2
features(startIdx+3) = 0;
else
[p, ~] = histcounts(d, 10, 'Normalization', 'probability');
p(p == 0) = []; % 移除零概率
features(startIdx+3) = -sum(p.*log2(p+eps));
end
% 过零率
features(startIdx+4) = sum(abs(diff(sign(d))));
% 最大值/最小值
features(startIdx+5) = max(d);
features(startIdx+6) = min(d);
% 峰度(需要≥4个点)
if numel(d) > 3
features(startIdx+7) = kurtosis(d);
else
features(startIdx+7) = 0;
end
% 偏度(需要≥3个点)
if numel(d) > 2
features(startIdx+8) = skewness(d);
else
features(startIdx+8) = 0;
end
features(startIdx+9) = median(d); % 中值
end
% 最终NaN检查(防止意外情况)
if any(isnan(features))
warning('特征包含NaN,使用零填充');
features(isnan(features)) = 0;
end
end
end
end