C#开发学习人工智能的第一步
前言
作为一个软件开发者,我们除了要学会复制,黏贴,还要学会调用API和优秀的开源类库。
也许,有人说C#做不了人工智能,如果你相信了,那只能说明你的思想还是狭隘的。
做不了人工智能的不是C#这种语言,而是你,我这种普通的程序员。
做人工智能需要一定的学历背景,一定的数学基础和公司专项的资源供给;而这种机缘小之又小,你我既然是普通的程序员,就必然与此无缘。
但在人工智能如日中天的当下,接触深度学习是必然会发生的事情,所以我们要做的就是,学会调用相关的类库。
现在,让我们迈出C#学习人工智能的第一步,通过调用Affdex来锁定图片中人物的面部,然后将其截取出来。
准备工作
首先,我们需要先访问官网下载Affdex的Sdk。
在官网找中找到下载Affdex的Sdk的地方也是个挺困难的事。。。所以下载链接如下:
进入网页后,向下拉动滚动条,找到到下图所示位置,点击Download进行下载。

下载完成后得到Sdk,如下图:

下面,我们双击进行安装,不过安装SDK有一些限制,需要预先安装NET Framework4.0和C++ 2015。如果电脑里已经安装了,就不必担心了;如果安装的是C++2015-2017这类型的,则需要卸载了,重新安装C++2015的版本,否则Affdex的SDK将安装失败。
安装完成后,我们去安装目录找到Affdex.dll,affdex-native.dll,tensorflow.dll三个文件,如下图:

我们先将它们复制出来,等待使用。
简单的介绍一下,这三个类库中,Affdex.dll是可以被C#项目直接引用的,而另外两个文件是Affdex.dll的依赖文件;也就是说,affdex-native.dll,tensorflow.dll需要在生成时,输出到运行目录下。
有经验的朋友想必已经发现了,这里有个类库名叫tensorflow.dll,tensorflow是什么啊?稍微百度一下大家就会了解了,它是专门来做深度学习的。
也就是说Affdex是支持深度学习的。
----------------------------------------------------------------------------------------------------
现在我们来学习Affdex的使用。
首先我们新建一个WPF项目,然后引用Affdex.dll。
然后将项目的运行平台设置为64位,因为,这样处理图片的速度能快一点,如下图:

在Affdex中我们可以发现四个探头—VideoDetector,PhotoDetector,FrameDetector,CameraDetector。
在这里我们要处理的是图片,所以我们选择PhotoDetector,下面我们创建一个PhotoWindow.Xaml页面来使用PhotoDetector处理图片。
代码实现
首先,我们定义一个PhotoDetector的属性,用于处理图片。
然后我们在构造函数中对他进行实例化,代码如下:
private Affdex.PhotoDetector Detector { get; set; }
public PhotoWindow()
{
InitializeComponent();
uint maxNumFaces = 1;//最多识别图片中几张脸
Detector = new Affdex.PhotoDetector(maxNumFaces, Affdex.FaceDetectorMode.SMALL_FACES);
Detector.setImageListener(this);
Detector.setProcessStatusListener(this);
Detector.start();
}
在上述代码中可以看到,除了初始化PhotoDetector,我们还做了一个图片监听设置setImageListener,那么图片监听是干什么的呢?
很简单,图片被PhotoDetector处理完,我们需要知道图片处理结果呀,而这个图片监听正是是用来返回图片处理结果的。
可以看到图片监听设置的入参是this,也就是说,需要把图片的处理结果返回给当前页面。
如果就这样写是会编译报错的,会提示setImageListener的入参错误。
我们查看setImageListener的入参,发现它的入参是一个ImageListener接口,即,setImageListener的入参是一个要实现了ImageListener接口的类。
到这里,我们就都明白了,现在我们让当前PhotoWindow.xaml窗体继承接口ImageListener,并实现接口ImageListener内的方法。
public partial class PhotoWindow : Window, Affdex.ImageListener
===========================================================================
public void onImageCapture(Affdex.Frame frame)
{
}
public void onImageResults(Dictionary<int, Face> faces, Affdex.Frame frame)
{
}
如上述代码所示,在我们实现的接口onImageResults里有两个参数:faces、frame。
其中faces是最重要的,这里包含Affdex分析图片的结果。
----------------------------------------------------------------------------------------------------
现在,Affdex的配置代码已经写完了,我们可以把图片读取出来调用Affdex处理了。
public PhotoWindow()
{
InitializeComponent();
uint maxNumFaces = 1;//最多识别图片中几张脸
Detector = new Affdex.PhotoDetector(maxNumFaces, Affdex.FaceDetectorMode.SMALL_FACES);
Detector.setImageListener(this);
Detector.start(); byte[] bytes = FileHelper.FileToBytes(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "timg.jpg"));
BitmapSource bitmapSource = ImageHelper.BytesToBitmapImage(bytes);
var w = bitmapSource.Width;
var h = bitmapSource.Height;
var stride = bitmapSource.Format.BitsPerPixel * (int)w / 8; //计算Stride
byte[] byteList = new byte[(int)h * stride];
bitmapSource.CopyPixels(byteList, stride, 0);
Affdex.Frame frame = new Affdex.Frame((int)w, (int)h, byteList, Affdex.Frame.COLOR_FORMAT.BGRA);
Detector.process(frame);
}
如上述代码所示,我们在启动了Detector后,读取了一个人物图片,然后把人物图片的像素数组解析出来,生成一个Frame;这个Frame是Affdex的类,用于保存图像数据信息。
最后,我们把生成的Frame对象,扔给Detecotor的Process方法处理。
Detecotor处理完成后,会触发onImageResults方法。
在onImageResults方法里,入参faces包含了处理结果。
现在我们使用faces里的内容,来定位图片中人物面部的位置。
public void onImageResults(Dictionary<int, Face> faces, Affdex.Frame frame)
{
Face face = null;
if (faces != null && faces.Values != null && faces.Values.Count() > 0)
{
face = faces.Values.First();//因为我们的Detector只识别了一个脸,所以这里最多只有一个数据
}
int top = (int)face.FeaturePoints.Min(r => r.X);
int left = (int)face.FeaturePoints.Min(r => r.Y);
int bottom = (int)face.FeaturePoints.Max(r => r.X);
int right = (int)face.FeaturePoints.Max(r => r.Y);
ImageHelper.cutPicture(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "timg.jpg"),
left, top, right , bottom - top);
}
如上述代码所示,我们在onImageResults里做了【最简单】人物面部坐标定位,并进行了剪切。
处理结果如下图所示:

结语
事实上,上面介绍的只是Affdex最基础调用,而且,这里并没有使用到深度学习的内容,只是简单的扫描和分析。
想要使用深度学习的内容还需要进一步学习该开源控件,不过,万事开头难,我们现在已经迈出了第一步。
----------------------------------------------------------------------------------------------------
到此C#开发学习人工智能的第一步就完成了。
代码已经传到Github上了,欢迎大家下载。
Github地址:https://github.com/kiba518/WpfAffdex
----------------------------------------------------------------------------------------------------
注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!
本文已独家授权给脚本之家(ID:jb51net)公众号发布!
https://www.cnblogs.com/kiba/p/11416919.html

C#开发学习人工智能的第一步的更多相关文章
- [EntLib]微软企业库5.0 学习之路——第一步、基本入门
话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白). 直到去年在 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----文件系统操作与磁盘管理
介绍 本节的文件系统操作的内容十分简单,只会包含几个命令的几个参数的讲解,但掌握这些也将对你在学习后续其他内容的过程中有极大帮助. 因为本课程的定位为入门基础,尽快上手,故没有打算涉及太多理论内容,前 ...
- 学习Nodejs的第一步
最近看了几本关于Node.js的书,本来个人技术分享网站http://yuanbo88.com/是打算用Node.js作为服务器端语言来处理后台的,后来又改成了PHP(也是自己研究,毕竟网上DEMO多 ...
- ExtJS学习之路第一步:对比jQuery,认识ExtJS
最近纷杂的事情比较多了,奔波ing!所以,Node.js 和Canvas动画系列都停止了,等稳定了再重拾书本继续学习!因为某种原因最近在看ExtJS,分享下学习的心得,希望对同道中人有所帮助. 第一用 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----目录结构及文件基本操作
Linux 目录结构及文件基本操作 介绍 1.Linux 的文件组织目录结构. 2.相对路径和绝对路径. 3.对文件的移动.复制.重命名.编辑等操作. 一.Linux 目录结构 在讲 Linux 目录 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----Linux 下软件安装
介绍 介绍 Ubuntu 下软件安装的几种方式,及 apt,dpkg 工具的使用. 一.Linux 上的软件安装 通常 Linux 上的软件安装主要有三种方式: 在线安装 从磁盘安装deb软件包 从二 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----正则表达式基础
介绍 虽然我们这一节的标题是正则表达式,但实际这一节只是介绍grep,sed,awk这三个命令,而正则表达式作为这三个命令的一种使用方式(命令输出中可以包含正则表达式).正则表达式本身的内容很多,要把 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----环境变量与文件查找
环境变量与文件查找 本节介绍环境变量的作用与用法,及几种搜索文件的方法.学会这些技巧高效地使用 Linux. 一.环境变量 1.变量 要解释环境变量,得先明白变量是什么,准确的说应该是 Shell 变 ...
- (大数据工程师学习路径)第一步 Linux 基础入门----用户及文件权限管理
用户及文件权限管理 实验介绍 1.Linux 中创建.删除用户,及用户组等操作. 2.Linux 中的文件权限设置. 一.Linux 用户管理 Linux 是一个可以实现多用户登陆的操作系统,比如“李 ...
随机推荐
- Go语言圣经习题练习_1.6并发获取多个URL
练习 1.10: 找一个数据量比较大的网站,用本小节中的程序调研网站的缓存策略,对每个URL执行两遍请求,查看两次时间是否有较大的差别,并且每次获取到的响应内容是否一致,修改本节中的程序,将响应结果输 ...
- Flutter学习笔记(12)--列表组件
如需转载,请注明出处:Flutter学习笔记(12)--列表组件 在日常的产品项目需求中,经常会有列表展示类的需求,在Android中常用的做法是收集数据源,然后创建列表适配器Adapter,将数据源 ...
- mybatis01-1测试
首先需要数据源信息和日志文件 然后一个SQLMapConfig.xml配置文件连接数据库并且映射后一个xml文件,另一个xml文件写入SQL语句, 最后text测试文件读取第一个配置文件,放到SQLS ...
- 最近很火的MySQL:抛开复杂的架构设计,MySQL优化思想基本都在这
优化一览图 优化 笔者将优化分为了两大类:软优化和硬优化.软优化一般是操作数据库即可:而硬优化则是操作服务器硬件及参数设置. 1.软优化 1)查询语句优化 首先我们可以用EXPLAIN或DESCRIB ...
- 【Android Studio】E/memtrack: Couldn't load memtrack module (No such file or directory)【待解决】
Android Studio 又遇到了问题--如下: 06-21 07:27:57.855 3232-3232/? E/memtrack: Couldn't load memtrack module ...
- jumpserver1.4.1 安装过程
# 修改字符集 localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 echo 'LANG="zh_CN. ...
- 【pycharm】pycharm远程连接服务器的Python解释器,远程编写代码!!!
今天讲讲如何用pycharm连接远程服务器,使用远程服务器的Python解释器,比如说是你公司的服务器,在家里就可以编写或修改项目的代码! 第一步,先找到服务器上的ip地址 Linux查看IP命令:i ...
- 使用request获取访问者的真实IP
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...
- spark shuffle写操作之SortShuffleWriter
提出问题 1. spark shuffle的预聚合操作是如何做的,其中底层的数据结构是什么?在数据写入到内存中有预聚合,在读溢出文件合并到最终的文件时是否也有预聚合操作? 2. shuffle数据的排 ...
- 在Java大环境下.NET程序员如何夺得一线生机
先来看一组数据,从某招聘网站直接检索3-4w的岗位,会看到Java与.NET社会需求量的巨大差异,这里就不再对比高薪的岗位了,.NET的高薪岗位更是少的可怜: 笔者从业十余年,一直是在.NET圈子 ...