YOLOv5 + OpenPPL 开发 CatPiano,猫咪也可以弹钢琴啦!
一、整体设计方案
要想实现猫头钢琴,要解决三个问题:
怎么拍摄猫主子的视频 怎么检测猫头的位置 怎么弹琴与怎么显示
首先是视频的拍摄。我本来在双十一买了一个便宜的 USB 摄像头,但是因为发货慢到现在还没到货。所以只能拿手机录视频来先模拟摄像头采集图像。
猫头的检测是最麻烦的部分,我这里打算用深度学习来搞定。首先需要采集一些猫主子的图片,用标注工具标注下猫头。然后用 YOLOV5 模型训练一把,最后用商汤开源的 OpenPPL 来做模型推理部署。
至于如何弹琴,我打算用 mingus 来搞定。视频的处理与图形界面部分用 OpenCV 来做。
二、图像采集与标注
图像采集用手机就好,但是难点在于如何让猫主子乖乖配合。
这里我祭出了神器 —— 酸奶,溜着猫主子在屋子里转了好几圈,终于采到了一些合适的视频
采视频的时候手头的 windows 电脑没装 OpenCV,就找了个网站把视频转成图片了,当然用 OpenCV 的 VideoCapture 也能做到。
转成图片后,用了一个在 GitHub 上找的标注工具:https://github.com/tzutalin/labelImg
众所周知,猫是流体,因此标注猫身子并不利于检测,因此最终选择标注猫头 因为背景简单且任务单一,所以标注少量图片即可完成猫头检测 标注时需要注意标注的格式,务必要采用 YOLOV5 模型可以识别的模式进行标注
我们最终标注了 430 张图片,训练集、验证集、测试集的数量分别是:400、20、10
三、YOLOV5 模型训练
模型训练
首先从 GitHub 上 clone YOLOV5 的源码:
https://github.com/ultralytics/yolov5
训练过程没对代码做太多改动,直接用的 repo 中的训练脚本 train.py
python .\train.py --data .\data\cat.yaml --weights .\weights\yolov5s.pt --img 160 --epochs 3000
模型测试
模型导出
推理框架安装
cd ppl.nn
./build.sh -DHPCC_USE_X86_64=ON -DHPCC_USE_OPENMP=ON -DPPLNN_ENABLE_PYTHON_API=ON
./pplnn-build/install/lib
路径下会生成 pyppl
包,设置下 PYTHONPATH
之后就可以用了模型推理
engines = []
# create x86 engine
x86_options = pplnn.X86EngineOptions()
x86_engine = pplnn.X86EngineFactory.Create(x86_options)
engines.append(pplnn.Engine(x86_engine))
return engines
class ModelRunner(object):
def __init__(self, model_path):
self.__initialize(model_path)
def __initialize(self, model_path):
# register engines
engines = RegisterEngines()
if len(engines) == 0:
raise Exception('failed to register engines')
# create runtime builder
runtime_builder = pplnn.OnnxRuntimeBuilderFactory.CreateFromFile(model_path, engines)
if not runtime_builder:
raise Exception('failed to create runtime builder from file: %s' % (model_path))
# create runtime
self.runtime = runtime_builder.CreateRuntime()
if not self.runtime:
raise Exception('failed to create runtime')
def get_input_tensor_shape(self):
return self.runtime.GetInputTensor(0).GetShape().GetDims()
def forward(self, input):
if not self.runtime:
raise Exception('runtime not created')
# get input tensor info
tensor = self.runtime.GetInputTensor(0)
shape = tensor.GetShape()
np_data_type = g_pplnntype2numpytype[shape.GetDataType()]
dims = shape.GetDims()
# feed input data
input = np.ascontiguousarray(input) # use contiguousarray to avoid calc error
status = tensor.ConvertFromHost(input)
if status != pplcommon.RC_SUCCESS:
raise Exception('failed to set input data')
# start to inference
status = self.runtime.Run()
if status != pplcommon.RC_SUCCESS:
raise Exception('failed to run')
# wait for inference finished
status = self.runtime.Sync()
if status != pplcommon.RC_SUCCESS:
raise Exception('failed to sync')
# get output data
out_datas = {}
for i in range(self.runtime.GetOutputCount()):
# get output tensor info
tensor = self.runtime.GetOutputTensor(i)
tensor_name = tensor.GetName()
# fetch output data
tensor_data = tensor.ConvertToHost()
if not tensor_data:
raise Exception('failed to get output ' + tensor_name)
out_data = np.array(tensor_data, copy=False)
out_datas[tensor_name] = copy.deepcopy(out_data)
return out_datas
img = cv2.resize(img, (self.input_img_w, self.input_img_h)) # resize
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR -> RGB
img = img.transpose(2, 0, 1) # HWC -> CHW
img = img.astype(dtype = np.float32) # uint8 -> fp32
img /= 255 # normalize
img = np.expand_dims(img, axis=0) # add batch dimension
琴键声音
pip3 install fluidsynth
fluidsynth.init('/usr/share/sounds/sf2/FluidR3_GM.sf2', 'alsa') # for ubuntu
fluidsynth.play_Note(64, 0, 100) # 标准音 a1
键盘显示
PianoKey
:WHITE_KEY = 0,
WHITE_KEY_LEFT = 1,
WHITE_KEY_RIGHT = 2,
BLACK_KEY = 3
PianoKey
中有一个 play(self, position)
接口,一旦 position 落在了琴键的范围内,就认定琴键被按下,发出琴键对应的声音。🔗 https://github.com/openppl-public