首先我们先来说说这个ONNX

ONNX是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架(如Pytorch, MXNet)可以采用相同格式存储模型数据并交互。 ONNX的规范及代码主要由微软,亚马逊 ,Facebook 和 IBM 等公司共同开发,以开放源代码的方式托管在Github上。目前官方支持加载ONNX模型并进行推理的深度学习框架有: Caffe2, PyTorch, MXNet,ML.NET,TensorRT 和 Microsoft CNTK,并且 TensorFlow 也非官方的支持ONNX。---维基百科

看了上面的引用 大家应该知道了 这个其实是个文件格式用来存储训练好的模型,所以我这篇帖子既然是做表情识别那肯定是需要有个能识别表情的模型。有了这个模型我们就可以根据图片上的人物,进行表情的识别判断了。

刚好微软对机器学习这块也挺上心的,所以我也趁着疫情比较闲,就来学习学习了。UWP的机器学习的api微软已经切成正式了,所以大家可以放心使用。

这就是uwp api文档 开头就是AI的

我其实是个小白 所以我就直接拿官方的一个demo的简化版来进行讲解了,官方的demo演示如下。

这个app就是通过摄像头读取每一帧 进行和模型匹配得出结果的

下面是机器学习的微软的github地址

Emoji8的git地址

我今天要说的就是这个demo的简化代码大致运行流程

下面是项目结构图

我把官方项目简化了 所以只留下了识别后的文本移除了一些依赖的库

核心代码在IntelligenceService类里的Current_SoftwareBitmapFrameCaptured方法里

 private async void Current_SoftwareBitmapFrameCaptured(object sender, SoftwareBitmapEventArgs e)
{
Debug.WriteLine("FrameCaptured");
Debug.WriteLine($"Frame evaluation started {DateTime.Now}" );
if (e.SoftwareBitmap != null)
{
BitmapPixelFormat bpf = e.SoftwareBitmap.BitmapPixelFormat; var uncroppedBitmap = SoftwareBitmap.Convert(e.SoftwareBitmap, BitmapPixelFormat.Nv12);
var faces = await _faceDetector.DetectFacesAsync(uncroppedBitmap);
if (faces.Count > 0)
{
//crop image to focus on face portion
var faceBox = faces[0].FaceBox;
VideoFrame inputFrame = VideoFrame.CreateWithSoftwareBitmap(e.SoftwareBitmap);
VideoFrame tmp = null;
tmp = new VideoFrame(e.SoftwareBitmap.BitmapPixelFormat, (int)(faceBox.Width + faceBox.Width % 2) - 2, (int)(faceBox.Height + faceBox.Height % 2) - 2);
await inputFrame.CopyToAsync(tmp, faceBox, null); //crop image to fit model input requirements
VideoFrame croppedInputImage = new VideoFrame(BitmapPixelFormat.Gray8, (int)_inputImageDescriptor.Shape[3], (int)_inputImageDescriptor.Shape[2]);
var srcBounds = GetCropBounds(
tmp.SoftwareBitmap.PixelWidth,
tmp.SoftwareBitmap.PixelHeight,
croppedInputImage.SoftwareBitmap.PixelWidth,
croppedInputImage.SoftwareBitmap.PixelHeight);
await tmp.CopyToAsync(croppedInputImage, srcBounds, null); ImageFeatureValue imageTensor = ImageFeatureValue.CreateFromVideoFrame(croppedInputImage); _binding = new LearningModelBinding(_session); TensorFloat outputTensor = TensorFloat.Create(_outputTensorDescriptor.Shape);
List<float> _outputVariableList = new List<float>(); // Bind inputs + outputs
_binding.Bind(_inputImageDescriptor.Name, imageTensor);
_binding.Bind(_outputTensorDescriptor.Name, outputTensor); // Evaluate results
var results = await _session.EvaluateAsync(_binding, new Guid().ToString()); Debug.WriteLine("ResultsEvaluated: " + results.ToString()); var outputTensorList = outputTensor.GetAsVectorView();
var resultsList = new List<float>(outputTensorList.Count);
for (int i = 0; i < outputTensorList.Count; i++)
{
resultsList.Add(outputTensorList[i]);
} var softMaxexOutputs = SoftMax(resultsList); double maxProb = 0;
int maxIndex = 0; // Comb through the evaluation results
for (int i = 0; i < Constants.POTENTIAL_EMOJI_NAME_LIST.Count(); i++)
{
// Record the dominant emotion probability & its location
if (softMaxexOutputs[i] > maxProb)
{
maxIndex = i;
maxProb = softMaxexOutputs[i];
}
} Debug.WriteLine($"Probability = {maxProb}, Threshold set to = {Constants.CLASSIFICATION_CERTAINTY_THRESHOLD}, Emotion = {Constants.POTENTIAL_EMOJI_NAME_LIST[maxIndex]}"); // For evaluations run on the MainPage, update the emoji carousel
if (maxProb >= Constants.CLASSIFICATION_CERTAINTY_THRESHOLD)
{
Debug.WriteLine("first page emoji should start to update");
IntelligenceServiceEmotionClassified?.Invoke(this, new ClassifiedEmojiEventArgs(CurrentEmojis._emojis.Emojis[maxIndex]));
} // Dispose of resources
if (e.SoftwareBitmap != null)
{
e.SoftwareBitmap.Dispose();
e.SoftwareBitmap = null;
}
}
}
IntelligenceServiceProcessingCompleted?.Invoke(this, null);
Debug.WriteLine($"Frame evaluation finished {DateTime.Now}");
} //WinML team function
private List<float> SoftMax(List<float> inputs)
{
List<float> inputsExp = new List<float>();
float inputsExpSum = 0;
for (int i = 0; i < inputs.Count; i++)
{
var input = inputs[i];
inputsExp.Add((float)Math.Exp(input));
inputsExpSum += inputsExp[i];
}
inputsExpSum = inputsExpSum == 0 ? 1 : inputsExpSum;
for (int i = 0; i < inputs.Count; i++)
{
inputsExp[i] /= inputsExpSum;
}
return inputsExp;
} public static BitmapBounds GetCropBounds(int srcWidth, int srcHeight, int targetWidth, int targetHeight)
{
var modelHeight = targetHeight;
var modelWidth = targetWidth;
BitmapBounds bounds = new BitmapBounds();
// we need to recalculate the crop bounds in order to correctly center-crop the input image
float flRequiredAspectRatio = (float)modelWidth / modelHeight; if (flRequiredAspectRatio * srcHeight > (float)srcWidth)
{
// clip on the y axis
bounds.Height = (uint)Math.Min((srcWidth / flRequiredAspectRatio + 0.5f), srcHeight);
bounds.Width = (uint)srcWidth;
bounds.X = 0;
bounds.Y = (uint)(srcHeight - bounds.Height) / 2;
}
else // clip on the x axis
{
bounds.Width = (uint)Math.Min((flRequiredAspectRatio * srcHeight + 0.5f), srcWidth);
bounds.Height = (uint)srcHeight;
bounds.X = (uint)(srcWidth - bounds.Width) / 2; ;
bounds.Y = 0;
}
return bounds;
}

感兴趣的朋友可以把官方的代码和我的代码都克隆下来看一看,玩一玩。

我的简化版的代码 地址如下

简化版表情识别代码地址

特别感谢 Windows Community Toolkit Sample App提供的摄像头辅助类

商店搜索 Windows Community Toolkit Sample App就能下载

讲的不好的地方 希望大家给与批评

最新文章

  1. 【krpano】二维码自动生成插件(源码+介绍+预览)
  2. 在同一台电脑上同时安装Python2和Python3
  3. 使用read write 读写socket
  4. call()和apply()方法
  5. 第一百二十四节,JavaScriptCookie与存储
  6. QT 的使用及编写代码遇到的问题和解决方法
  7. Swift 路由机制设计
  8. xss攻击和csrf攻击的定义及区别
  9. js生成自定义随机数方法
  10. Spring 完美配置跨域请求
  11. java操作elasticsearch实现条件查询(match、multiMatch、term、terms、reange)
  12. 大数据入门到精通13--为后续和MySQL数据库准备
  13. Unity 精灵物体的创建 Sprite.create
  14. 微信小程序tabbar设置样式在哪里改
  15. 【译】第19节---数据注解-NotMapped
  16. 《剑指offer》第四十六题(把数字翻译成字符串)
  17. 帝国cms栏目别名怎样调用?栏目名称太短了
  18. UVA10298 Power Strings
  19. js焦点事件:onfocus、onblur、focus()、blur()、select()
  20. Find Peak Element(ARRAY - Devide-and-Conquer)

热门文章

  1. NanoProfiler-Step1翻译
  2. Uva1014:Remember the Word
  3. .NetCore自定义WebAPI返回Json的格式大小写的三种方式
  4. 现在啊还不太清楚 nodejs和coffeescript 的关系
  5. Centos 7中安装svn服务器,史上最详细
  6. py2 to py3
  7. Linux下安装nvidia显卡驱动
  8. win10搭建本地服务器(IIS)
  9. Docker深入浅出系列 | 容器初体验
  10. 4、Oracle 数据库 startup 报错:ORA-27102: out of memory