C# 使用OpenCV在一张图片里寻找人脸
先上个效果图
相关库的下载
例程中用到一个库叫做emgucv,是opencv的net封装 编译打包好的稳定版,在这:https://sourceforge.net/projects/emgucv/files/emgucv/ 如果要最新代码,在这里获取:https://github.com/emgucv/emgucv
做个opencv人脸识别的小伙伴们可能会遇到这样的一个问题,如何下载haarcascade_frontalface_default.xml和haarcascade_frontalface_alt2.xml呢? OpenCV有已经自带了人脸的Haar特征分类器,有了那些IT大牛帮我们创建的这个分类器,我们便可的实现人脸的检查功能了,你只需要将他们下载到opencv的目录下。 首先,进入网站 https://github.com/opencv/opencv/tree/master/data/haarcascades 点击这个文件。
haarcascade_frontalface_alt.xml 路径:
https://github.com/opencv/opencv/edit/master/data/haarcascades_cuda/haarcascade_frontalface_alt.xml
这里还有其他的模型:
https://github.com/opencv/opencv/tree/master/data/haarcascades
https://github.com/opencv/opencv/tree/master/data/haarcascades_cuda
建立工程
首先建立一个C#工程.nuget上安装引用
另外准备一张要识别的图片,放到编译输出目录. 接下来就是编辑代码了,后面所有代码都在main里
配置OpenCV使用显卡运算(如果支持的话)
使用显卡处理图像数据效率会很多,如果你的设备支持,最好打开,使用CvInvoke.HaveOpenCLCompatibleGpuDevice能返回是否支持. 配置CvInvoke.UseOpenCL能让OpenCV 启用或者停用 GPU运算
CvInvoke.UseOpenCL = CvInvoke.HaveOpenCLCompatibleGpuDevice;
构建级联分类器对象
emgu包里已经有训练好的数据了,文件名叫做"haarcascade_frontalface_alt.xml",就是上面添加的文件之一
var face = new CascadeClassifier("haarcascade_frontalface_alt.xml");
加载图像并作简单处理
在OpenCV中,大部分函数是处理灰度图的,包括这个识别物体,所以需要转成灰度图,然后再调整下亮度
//加载要识别的图片
var img = new Image<Bgr, byte>("0.png");
var img2 = new Image<Gray, byte>(img.ToBitmap());
//把图片从彩色转灰度
CvInvoke.CvtColor(img, img2, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
//亮度增强
CvInvoke.EqualizeHist(img2, img2);
检测人脸
进行目标区域检测: 如果进行级联级联分类器创建之后, 便可以对图片进行目标区域的检测。 public Rectangle[] DetectMultiScale(IInputArray image, double scaleFactor = 1.1, int minNeighbors = 3, Size minSize = null, Size maxSize = null);//通过多次扫描 不同尺度, 寻找图像中可能包含级联分类器训练的样本, 返回这些样本的区域。 参数解析: IInputArray image:被检测的图像。 double scaleFactor = 1.1:在随后的扫描中缩放比例。例如 double scaleFactor = 1.1, 意味着增加 10%的窗口。 int minNeighbors = 3:最小值(- 1)的邻居矩形组成一个对象。所有的组 比 min_neighbors=-1 数量较小的矩形会被略去。如果 min_neighbors 是 0, 这个函数没有任何分组并返回所有检测到的候选矩形。这对于用户自定义 分组过程是很有用的。默认值为 3; Size minSize = null:最小检测出来的窗口大小。 Size maxSize = null:最大检测出来的窗口大小
其实这一步反而最简单,返回的是rectangle[]格式,因为图中可能有多个人脸,所以返回的是数组.
//在这一步就已经识别出来了,返回的是人脸所在的位置和大小
var facesDetected = face.DetectMultiScale(img2, 1.1, 10, new Size(50, 50));
剪切并保存
因为是多个人脸所以需要循环剪切并保存,(→_→)这一块的代码量竟然反而比上面那堆多
//循环把人脸部分切出来并保存
int count = 0;
var b = img.ToBitmap();
foreach (var item in facesDetected)
{
count++;
var bmpOut = new Bitmap(item.Width, item.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(0, 0, item.Width, item.Height), new Rectangle(item.X, item.Y, item.Width, item.Height), GraphicsUnit.Pixel);
g.Dispose();
bmpOut.Save($"{count}.png", System.Drawing.Imaging.ImageFormat.Png);
bmpOut.Dispose();
}
释放资源退出
//释放资源退出
b.Dispose();
img.Dispose();
img2.Dispose();
face.Dispose();
全代码和测试图片:
static void Main(string[] args)
{
//如果支持用显卡,则用显卡运算
CvInvoke.UseOpenCL = CvInvoke.HaveOpenCLCompatibleGpuDevice;
//构建级联分类器,利用已经训练好的数据,识别人脸
var face = new CascadeClassifier("haarcascade_frontalface_alt.xml");
//加载要识别的图片
var img = new Image<Bgr, byte>("0.png");
var img2 = new Image<Gray, byte>(img.ToBitmap());
//把图片从彩色转灰度
CvInvoke.CvtColor(img, img2, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
//亮度增强
CvInvoke.EqualizeHist(img2, img2);
//在这一步就已经识别出来了,返回的是人脸所在的位置和大小
var facesDetected = face.DetectMultiScale(img2, 1.1, , new Size(, ));
//循环把人脸部分切出来并保存
int count = ;
var b = img.ToBitmap();
foreach (var item in facesDetected)
{
count++;
var bmpOut = new Bitmap(item.Width, item.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(, , item.Width, item.Height), new Rectangle(item.X, item.Y, item.Width, item.Height), GraphicsUnit.Pixel);
g.Dispose();
bmpOut.Save($"{count}.png", System.Drawing.Imaging.ImageFormat.Png);
bmpOut.Dispose();
}
//释放资源退出
b.Dispose();
img.Dispose();
img2.Dispose();
face.Dispose();
return;
}
运行效果
编译后运行可以看到目录多了两个图片文件:
打开看看:
耗时测算:
private void button1_Click(object sender, EventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
//如果支持用显卡,则用显卡运算
CvInvoke.UseOpenCL = CvInvoke.HaveOpenCLCompatibleGpuDevice;
//构建级联分类器,利用已经训练好的数据,识别人脸
var face = new CascadeClassifier("haarcascade_frontalface_alt.xml");
//加载要识别的图片
var img = new Image<Bgr, byte>("0.png");
var img2 = new Image<Gray, byte>(img.ToBitmap());
//把图片从彩色转灰度
CvInvoke.CvtColor(img, img2, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
//亮度增强
CvInvoke.EqualizeHist(img2, img2);
//在这一步就已经识别出来了,返回的是人脸所在的位置和大小
var facesDetected = face.DetectMultiScale(img2, 1.1, 10, new Size(50, 50));
//循环把人脸部分切出来并保存
int count = 0;
var b = img.ToBitmap();
foreach (var item in facesDetected)
{
count++;
var bmpOut = new Bitmap(item.Width, item.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(0, 0, item.Width, item.Height), new Rectangle(item.X, item.Y, item.Width, item.Height), GraphicsUnit.Pixel);
g.Dispose();
bmpOut.Save($"{count}.png", System.Drawing.Imaging.ImageFormat.Png);
bmpOut.Dispose();
}
//释放资源退出
b.Dispose();
img.Dispose();
img2.Dispose();
face.Dispose();
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds.ToString());
}
标注人脸:
using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApp18
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
//如果支持用显卡,则用显卡运算
CvInvoke.UseOpenCL = CvInvoke.HaveOpenCLCompatibleGpuDevice;
//构建级联分类器,利用已经训练好的数据,识别人脸
var face = new CascadeClassifier("haarcascade_frontalface_alt.xml");
//加载要识别的图片
var img = new Image<Bgr, byte>("0.png");
var img2 = new Image<Gray, byte>(img.ToBitmap());
//把图片从彩色转灰度
CvInvoke.CvtColor(img, img2, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
//亮度增强
CvInvoke.EqualizeHist(img2, img2);
//在这一步就已经识别出来了,返回的是人脸所在的位置和大小
var facesDetected = face.DetectMultiScale(img2, 1.1, 10, new Size(50, 50));
//循环把人脸部分切出来并保存
int count = 0;
var b = img.ToBitmap();
foreach (var item in facesDetected)
{
count++;
var bmpOut = new Bitmap(item.Width, item.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(0, 0, item.Width, item.Height), new Rectangle(item.X, item.Y, item.Width, item.Height), GraphicsUnit.Pixel);
g.Dispose();
bmpOut.Save($"{count}.png", System.Drawing.Imaging.ImageFormat.Png);
bmpOut.Dispose();
}
//释放资源退出
b.Dispose();
img.Dispose();
img2.Dispose();
face.Dispose();
sw.Stop();
// MessageBox.Show(sw.ElapsedMilliseconds.ToString());
label1.Text = "取出人脸耗时:" + sw.ElapsedMilliseconds.ToString() + "ms";
}
private void button2_Click(object sender, EventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
CascadeClassifier face_detect = new CascadeClassifier(@"haarcascade_frontalface_alt.xml");//创建一个人脸检测级联分类器。
Mat face_image = new Mat("0.png", Emgu.CV.CvEnum.LoadImageType.AnyColor);//打开指定目录下的图片。
Rectangle[] rects = face_detect.DetectMultiScale(face_image);//对人脸进行检测, 放回数据在 rects 数组中。
foreach (Rectangle rect in rects)//遍历每个矩形区域。
{
CvInvoke.Rectangle(face_image, rect, new MCvScalar(0, 0, 255), 2);//绘制检测出的人脸的区域。
}
imageBox1.Image = face_image;//显示图片
sw.Stop();
label2.Text = "标注人脸耗时:" + sw.ElapsedMilliseconds.ToString()+"ms";
}
}
}
注:人脸稍微小点测试效果就不好了
参考链接:
https://www.cnblogs.com/DragonStart/p/7751993.html
- 【高级编程】linux进程间通信总结
- JS 吸顶导航,告别“回到顶部”
- 用AlphaGo来做股票交易会怎样?机器学习预测股票靠谱么?
- 开发 | 深度神经网络可视化工具集锦
- CSS3三维变形,其实很简单!
- 使用 pandas处理股票数据并作分析
- 用R语言做时间序列分析(附数据集和源码)
- 【android开发】Android GUI系统学习1:Gralloc
- 【kaggle实战】从KNN,LR,SVM,RF到深度学习
- 开发 | 训练一个AI给颜值打分,公平公正!
- 【android开发】Android HAL模块实现
- 让剁手党洞察物体细节,“放大镜”当之无愧
- CSS3过渡,不再为JS动画而犯愁
- 【编程基础】c语言中获取整数和浮点数的符号位
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Flutter 学习笔记 16 - Hero 动画
- sdk冲突记录
- 个人账号密码管理体系(密码篇)
- 熬夜7天,我总结了JavaScript与ES的25个重要知识点!
- 【分享】MPSoC SWDT在Standalone下的应用
- 【分享】 PetaLinux工程出现大量Taskhash mismatch错误的原因
- Cypress系列(46)- then() 命令详解
- MultiDex原理
- 跨进程文件锁 - FileChannel
- 聊一聊二分查找法
- 【Ceph】集群升级之好好看ceph.conf
- Carthage使用
- java线程池(一):java线程池基本使用及Executors
- java线程池(二):聊聊newFixedThreadPool(1)和newSingleThreadExecutor()的区别
- SAP RFC函数RFC_READ_TABLE使用与优化