本帖最后由 云天 于 2023-2-22 22:46 编辑
【项目背景】
让“聋人”看到声音! 世界上有各种各样的残障人士,这些人或不能说话,或听不见声音。聋人,是听力因先天遗传或后天人为因素而受损的残疾人,也叫听力障碍者,简称听障人。根据最近的全国人口普查统计,全中国大约有2700多万听障人,包括弱听、重听、老化聋等。因为各种不方便让其在这个世界上生活极为不方便。 比如有人敲门,水龙头忘关、孩子在卧室里啼哭,可是“听障人”他们听不见。 【项目设计】 利用人工智能,让模型学习各种声音,使用行空板采集声音,通过物联网将相应文字信息发送给 Arduino主板在显示屏上显示并利用灯光提醒,并且利用 micro:bit制作的手表进行文字、灯光加震动提醒,让“听障人”看见、感触到声音。 【项目改进】 上一版本使用波形图,本次学生项目作品使用“语谱图”,使训练出来的模型识别声音的种类和准确度更多更高。
【音频信号】
声音以音频信号的形式表示,音频信号具有频率、带宽、分贝等参数,音频信号一般可表示为振幅和时间的函数。这些声音有多种格式,因此计算机可以对其进行读取和分析。例如:mp3 格式、WMA (Windows Media Audio) 格式、wav (Waveform Audio File) 格式。
【语谱图】
语谱图是二战时期发明的一种语音频谱图,一般是通过处理接收的时域信号得到频谱图。
语谱图是频谱分析视图,如果针对语音数据的话,叫语谱图。语谱图的横坐标是时间,纵坐标是频率,坐标点值为语音数据能量。由于是采用二维平面表达三维信息,所以能量值的大小是通过颜色来表示的,颜色深,表示该点的语音能量越强。
语音的时域分析和频域分析是语音分析的两种重要方法,但是都存在着局限性。时域分析对语音信号的频率特性没有直观的了解,频域特性中又没有语音信号随时间的变化关系。而语谱图综合了时域和频域的优点,明显的显示出了语音频谱随时间的变化情况、语谱图的横轴为时间,纵轴为频率,任意给定频率成分在给定时刻的强弱用颜色深浅来表示。颜色深的,频谱值大,颜色浅的,频谱值小。语谱图上不同的黑白程度形成不同的纹路,称之为声纹,不同讲话者的声纹是不一样的,可用作声纹识别。
【录制音频】
使用pyaudio库这个可以进行录音,生成wav文件。PyAudio 提供了 PortAudio 的 Python 语言版本,这是一个跨平台的音频 I/O 库,使用 PyAudio 你可以在 Python 程序中播放和录制音频。为PoTaTudio提供Python绑定,跨平台音频I/O库。使用PyAudio,您可以轻松地使用Python在各种平台上播放和录制音频。
测试程序,使用pyaudio录制5秒声音文件“output.wav”:
- import pyaudio
- import wave
-
- CHUNK = 1024
- FORMAT = pyaudio.paInt16
- CHANNELS = 2
- RATE = 44100
- RECORD_SECONDS = 5
- WAVE_OUTPUT_FILENAME = "output.wav"
-
- p = pyaudio.PyAudio()
-
- stream = p.open(format=FORMAT,
- channels=CHANNELS,
- rate=RATE,
- input=True,
- frames_per_buffer=CHUNK)
-
- print("* recording")
-
- frames = []
-
- for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
- data = stream.read(CHUNK)
- frames.append(data)
-
- print("* done recording")
-
- stream.stop_stream()
- stream.close()
- p.terminate()
-
- wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
- wf.setnchannels(CHANNELS)
- wf.setsampwidth(p.get_sample_size(FORMAT))
- wf.setframerate(RATE)
- wf.writeframes(b''.join(frames))
- wf.close()
-
复制代码
【批量生成语谱图】
使用Librosa库批量生成各类声音的语谱图,如敲门声、水龙头流水声、婴儿啼哭声、警报声等。
Librosa是一个用于音频、音乐分析、处理的python工具包,一些常见的时频处理、特征提取、绘制声音图形等功能应有尽有,功能十分强大。
Librosa语音频谱图
- librosa.display.specshow(data, x_axis=None, y_axis=None, sr=22050, hop_length=512)
复制代码
参数:
data:要显示的矩阵
sr :采样率
hop_length :帧移
x_axis 、y_axis :x和y轴的范围
频率类型
‘linear’,‘fft’,‘hz’:频率范围由 FFT 窗口和采样率确定
‘log’:频谱以对数刻度显示
‘mel’:频率由mel标度决定
时间类型
time:标记以毫秒,秒,分钟或小时显示。值以秒为单位绘制。
s:标记显示为秒。
ms:标记以毫秒为单位显示。
所有频率类型均以Hz为单位绘制
- #加载模块:
-
- import pyaudio
- import numpy as np
- import matplotlib.pyplot as plt
- from tqdm import tqdm
- from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
- import librosa.display
- import pandas as pd
- import librosa
-
- def record_audio(record_second):
- global wave
- CHUNK = 1024
- FORMAT = pyaudio.paInt16
- CHANNELS = 1
- RATE = 44100
-
- p = pyaudio.PyAudio()
- stream = p.open(format=FORMAT,
- channels=CHANNELS,
- rate=RATE,
- input=True,
- frames_per_buffer=CHUNK)
- audio_data = []
- print("* recording")
- for i in tqdm(range(0, int(RATE / CHUNK * record_second))):
- data = stream.read(CHUNK)
- audio_data.append(data)
- audio_samples = librosa.core.samples_to_frames(
- np.frombuffer(b''.join(audio_data), dtype=np.int16),
- CHANNELS,
- hop_length=CHUNK
- )
- wave = audio_samples[5500:5500+int(1 * RATE)]/2**16
-
- print("* done recording")
- stream.stop_stream()
- stream.close()
- p.terminate()
-
- for i in range(50):
- #录制音频
- record_audio(record_second=2)
- #加载音频:
-
- window_size = 1024
- window = np.hanning(window_size)
- stft = librosa.core.spectrum.stft(wave, n_fft=window_size, hop_length=512, window=window)
- out = 2 * np.abs(stft) / np.sum(window)
- # For plotting headlessly
- fig = plt.figure(figsize=(2.24, 2.24))
- ax = fig.add_subplot(111)
- ax.axes.xaxis.set_visible(False)
- ax.axes.yaxis.set_visible(False)[indent]#生成语音频谱图
- p = librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), ax=ax, y_axis='log', x_axis='time')
- fig.savefig('save'+str(i)+'.jpg')
-
复制代码
注:需在行空板程序所在目录建立相应文件夹,如“door”,生成图像后,拷贝到电脑,使用ML训练模型。
【硬件制作过程】 一、行空板主控 按钮接行空板引脚21(用于关闭提醒),LED灯接引脚22(用于亮灯提醒)。
将震动马达接“掌控宝”的M2接口,并粘在表带上。当掌控板收到信息后,启动震动马达开始震动,提醒“听障人”查看屏幕提示信息。
【训练模型】
将图片上传到“英艻AI训练平台”进行模型训练。类型有[size=18.6667px]“background”[size=18.6667px]、“door”、“water”。
【电脑推理测试】
下载模型,放到程序相应目录下。 - #加载模块:
-
- import pyaudio
- #from unihiker import Audio
- import numpy as np
- import matplotlib.pyplot as plt
- from PIL import Image, ImageOps #Install pillow instead of PIL
- from tqdm import tqdm
- from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
- import librosa.display
- import pandas as pd
- import librosa
- from keras.models import load_model
-
- # 禁用科学符号
- np.set_printoptions(suppress=True)
- #内容.config(text="加载模型")
- # 加载模型
- model = load_model('keras_model.h5', compile=False)
- # 加载标签
- class_names = ['background','jianpan','desktop']
-
- #创建正确形状的数组以输入keras模型
- #可以放入阵列的图像的“长度”或数量为
- #由形状元组中的第一位置(在这种情况下为1)确定。
-
- data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
- #录制声音函数
- def record_audio(record_second):
- global wave
- CHUNK = 1024
- FORMAT = pyaudio.paInt16
- CHANNELS = 1
- RATE = 44100
-
- p = pyaudio.PyAudio()
- stream = p.open(format=FORMAT,
- channels=CHANNELS,
- rate=RATE,
- input=True,
- frames_per_buffer=CHUNK)
- audio_data = []
- print("* recording")
- for i in tqdm(range(0, int(RATE / CHUNK * record_second))):
- data = stream.read(CHUNK)
- audio_data.append(data)
- audio_samples = librosa.core.samples_to_frames(
- np.frombuffer(b''.join(audio_data), dtype=np.int16),
- CHANNELS,
- hop_length=CHUNK
- )
- wave = audio_samples[5500:5500+int(1 * RATE)]/2**16
- print("* done recording")
- stream.stop_stream()
- stream.close()
- p.terminate()
-
-
-
- while(True):
-
- [/indent][indent] #录制音频
- record_audio(record_second=2)
- #加载音频保存语谱图
- window_size = 1024
- window = np.hanning(window_size)
- stft = librosa.core.spectrum.stft(wave, n_fft=window_size, hop_length=512, window=window)
- out = 2 * np.abs(stft) / np.sum(window)
- #绘制
- fig = plt.figure(figsize=(2.24, 2.24))
- ax = fig.add_subplot(111)[/indent][indent] #不显示横纵坐标轴
- ax.axes.xaxis.set_visible(False)
- ax.axes.yaxis.set_visible(False)[/indent] #生成语音频谱图保存
- p = librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), ax=ax, y_axis='log', x_axis='time')
- fig.savefig('wave.jpg')
- #打开语音频谱图
- image = Image.open('wave.jpg').convert('RGB')
- #将图像转换为numpy数组
- image_array = np.asarray(image)
- # 图像规格化
- normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1
- #将图像加载到数组中
- data[0] = normalized_image_array
- # 运行推断
- prediction = model.predict(data)
-
- index = np.argmax(prediction)
- #获取标签
- class_name = class_names[index]
- #获取置信度
- confidence_score = prediction[0][index]
- print('Class:', class_name)
- print('Confidence score:', confidence_score)
-
复制代码
【行空板推理】
下载模型,放到行空板程序相应目录。 利用行空板板载麦克风采集声音,利用matplotlib变换成图片,使用keras加载训练好的模型“keras_model.h5”,进行预测出声音类型。点亮LED灯并通过物联网发送相关信息指令。 -
-
- #加载模块:
-
- from unihiker import GUI
- u_gui=GUI()
- 显示=u_gui.draw_text(text="智能提示器",x=0,y=100,font_size=35, color="#0000FF")
- 内容=u_gui.draw_text(text="加载库",x=20,y=180,font_size=35, color="#0000FF")
- from pinpong.extension.unihiker import *
- from pinpong.board import Board,Pin,NeoPixel
-
- import pyaudio
- #from unihiker import Audio
- import numpy as np
- import matplotlib.pyplot as plt
- from PIL import Image, ImageOps #Install pillow instead of PIL
- from tqdm import tqdm
- from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
- import librosa.display
- import pandas as pd
- import librosa
- from keras.models import load_model
-
- import siot
-
- Board().begin()
- p_p22_out=Pin(Pin.P22, Pin.OUT)
- p_p21_in=Pin(Pin.P21, Pin.IN)
- np1 = NeoPixel(p_p22_out,1)
- np1[0] = (0,0,0)
-
- # 禁用科学符号
- np.set_printoptions(suppress=True)
- #内容.config(text="加载模型")
- # 加载模型
- model = load_model('keras_model.h5', compile=False)
- # 加载标签
- class_names = ['background','door','water']
-
- #创建正确形状的数组以输入keras模型
- #可以放入阵列的图像的“长度”或数量为
- #由形状元组中的第一位置(在这种情况下为1)确定。
-
- data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
-
- 内容.config(text="连物联网")
-
- siot.init(client_id="",server="iot.dfrobot.com.cn",port=1883,user="X8jykxFnR",password="u8jskbFngz")
-
- siot.connect()
-
- siot.loop()
-
-
- def record_audio(record_second):
- global wave
- CHUNK = 1024
- FORMAT = pyaudio.paInt16
- CHANNELS = 1
- RATE = 44100
-
- p = pyaudio.PyAudio()
- stream = p.open(format=FORMAT,
- channels=CHANNELS,
- rate=RATE,
- input=True,
- frames_per_buffer=CHUNK)
- audio_data = []
- print("* recording")
- for i in tqdm(range(0, int(RATE / CHUNK * record_second))):
- data = stream.read(CHUNK)
- audio_data.append(data)
- audio_samples = librosa.core.samples_to_frames(
- np.frombuffer(b''.join(audio_data), dtype=np.int16),
- CHANNELS,
- hop_length=CHUNK
- )
- wave = audio_samples[5500:5500+int(1 * RATE)]/2**16
- print("* done recording")
- stream.stop_stream()
- stream.close()
- p.terminate()
-
-
-
- while(True):
- if (p_p21_in.read_digital()==True):
- siot.publish(topic="1DXAmWJ4g", data="S")
- np1[0] = (0,0,0)
- else:
-
- #录制音频
- 内容.config(text="录音中……")
- record_audio(record_second=2)
- 内容.config(text="识别中……")
- #加载音频:
- window_size = 1024
- window = np.hanning(window_size)
- stft = librosa.core.spectrum.stft(wave, n_fft=window_size, hop_length=512, window=window)
- out = 2 * np.abs(stft) / np.sum(window)
- # 绘制
- fig = plt.figure(figsize=(2.24, 2.24))
- ax = fig.add_subplot(111)
- ax.axes.xaxis.set_visible(False)
- ax.axes.yaxis.set_visible(False)
- p = librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), ax=ax, y_axis='log', x_axis='time')
- fig.savefig('wave.jpg')
-
- image = Image.open('wave.jpg').convert('RGB')
-
- #将图像转换为numpy数组
- image_array = np.asarray(image)
-
- # 规格化图像
- normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1
- # 将图像加载到数组中
- data[0] = normalized_image_array
- # 运行推断
- prediction = model.predict(data)
- index = np.argmax(prediction)
- class_name = class_names[index]
-
- confidence_score = prediction[0][index]
-
- if class_name=='background':
- siot.publish(topic="1DXAmWJ4g", data="B")
- np1[0] = (0,0,0)
- 内容.config(text='背景音')
- elif class_name=='door':
- siot.publish(topic="1DXAmWJ4g", data="D")
- np1[0] = (0,255,0)
- 内容.config(text='敲门')
- elif class_name=='water':
- siot.publish(topic="1DXAmWJ4g", data="W")
- np1[0] = (0,0,255)
- 内容.config(text='流水')
- print('Class:', class_name)
-
- print('Confidence score:', confidence_score)
-
复制代码
【掌控板手表程序】 通过物联网接收行空板传来的指令,屏幕显示相应信息,板载LED灯循环闪烁,并驱动马达震动。
|