涉及的环境
这些库的安装在网上应该很容易就可以找到,这里不多做赘述。
- dlib人脸检测库
- opencv 4.0 静态编译库
- jsoncpp 库 (这个是非必要的环境)
涉及的核心实现
c/c++端的实现
头文件
#pragma once
#include "stdafx.h"
//初始化方法 netPath=128人脸数据网络的特征转换文件地址 path68 = 68人脸特征检测文件地址
extern "C" __declspec(dllexport) int Init_descriptors();
//侦测并框选人脸
extern "C" __declspec(dllexport) const char* FaceRects(unsigned char *ImageBuffer,
int imageWedth, int imageHeight);
源文件
#include "stdafx.h"
#include "FaceRect.h"
#include <dlib/dnn.h> //构建神经网络必须
#include <dlib/image_processing/frontal_face_detector.h> //两个解析器
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_transforms.h> //cv_img 与array2d
#include <dlib/opencv.h> //opencv和dlib数据转换
#include <opencv2\opencv.hpp> //opencv基础库
#include <io.h> //文件读取使用
#include <json/json.h> //用于将反馈值转化为json格式
#pragma comment(lib,"jsoncpp.lib")
using namespace dlib;
using namespace std;
#pragma comment(lib,"ade.lib")
#pragma comment(lib,"ilmimf.lib")
#pragma comment(lib,"ippicvmt.lib")
#pragma comment(lib,"ippiw.lib")
#pragma comment(lib,"ittnotify.lib")
#pragma comment(lib,"libjasper.lib")
#pragma comment(lib,"libjpeg-turbo.lib")
#pragma comment(lib,"libpng.lib")
#pragma comment(lib,"libprotobuf.lib")
#pragma comment(lib,"libtiff.lib")
#pragma comment(lib,"libwebp.lib")
#pragma comment(lib,"opencv_calib3d400.lib")
#pragma comment(lib,"opencv_core400.lib")
#pragma comment(lib,"opencv_dnn400.lib")
#pragma comment(lib,"opencv_features2d400.lib")
#pragma comment(lib,"opencv_flann400.lib")
#pragma comment(lib,"opencv_gapi400.lib")
#pragma comment(lib,"opencv_highgui400.lib")
#pragma comment(lib,"opencv_imgcodecs400.lib")
#pragma comment(lib,"opencv_imgproc400.lib")
#pragma comment(lib,"opencv_ml400.lib")
#pragma comment(lib,"opencv_objdetect400.lib")
#pragma comment(lib,"opencv_photo400.lib")
#pragma comment(lib,"opencv_stitching400.lib")
#pragma comment(lib,"opencv_video400.lib")
#pragma comment(lib,"opencv_videoio400.lib")
#pragma comment(lib,"quirc.lib")
#pragma comment(lib,"zlib.lib")
//用于人脸截取的处理器
frontal_face_detector detector;
int Init_descriptors()
{
try {
//人脸截取器
detector = get_frontal_face_detector();
}
catch (int) {
return 0;
}
return 1;
}
const char* FaceRects(unsigned char *ImageBuffer, int imageWedth, int imageHeight)
{
//定义根节点
Json::Value jsonRoot;
cv::Mat compMat;
try {
//接受图像
compMat = cv::Mat(imageHeight, imageWedth, CV_8UC3, ImageBuffer);
//色彩问题,注意反色
cv::cvtColor(compMat, compMat, cv::COLOR_BGR2RGB);
cv::imshow("接收到的图像", compMat);
}
catch (exception e) {
const char* is_what = e.what();
return is_what;
}
if (compMat.rows <=0 || compMat.cols <=0)
{
return "rows or cols is 0";
}
//转为dlib图片
array2d<rgb_pixel> img;
dlib::assign_image(img, dlib::cv_image<dlib::bgr_pixel>(compMat));
//解析其中所有人脸
std::vector<rectangle> faces = detector(img);
std::vector<matrix<rgb_pixel>> faces_R;
int facesNum = faces.size();
if (facesNum == 0)
{
return NULL;//无任何图像数据,直接反馈null
}
for (int i = 0; i < facesNum; i++)
{
Json::Value item;
item["top"] = faces[i].top();
item["bottom"] = faces[i].bottom();
item["left"] = faces[i].left();
item["right"] = faces[i].right();
jsonRoot.append(item);
item = NULL;
}
//转为json格式
std::string result = jsonRoot.toStyledString();
jsonRoot = NULL;
compMat.release();
ImageBuffer = NULL;
return result.data();
}
C#端的实现
DataAdapter
namespace WF_FaceRect.Core
{
public class DataAdapter
{
/// <summary>
/// 寻找所有的人脸区域
/// </summary>
/// <param name="img">传入opencv的mat对象</param>
/// <returns>json 解析字符串</returns>
public static List<RectModel> FaceRects(Mat img)
{
//扫描宽度
int s_width;
//扫描高度
int s_height;
//图像实际宽度
int imgWidth;
//图像实际高度
int imgHeight;
imgWidth = img.Width;
imgHeight = img.Height;
//将图像数据lock在内存
Bitmap bmp = img.ToBitmap();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
//------------判断实际应该显示的大小--------------
s_width = bmpData.Stride;
s_height = bmp.Height;
imgHeight = bmpData.Height;
if (s_width > imgWidth * 3)
{
imgWidth++;
}
//------------------------------------------------
int bytes = s_width * s_height;
byte[] rgbValues = new byte[bytes];
Marshal.Copy(ptr, rgbValues, 0, bytes);
IntPtr repI = DllLinker.FaceRects(rgbValues, imgWidth, imgHeight);
if (repI == IntPtr.Zero)
{
return null;
}
string strContent = Marshal.PtrToStringAnsi(repI);
List<RectModel> result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<RectModel>>(strContent,
new Newtonsoft.Json.JsonSerializerSettings { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore });
//相关释放
bmp.UnlockBits(bmpData);
//Marshal.FreeHGlobal(repI);
return result;
}
/// <summary>
/// 基础文件和环境加载
/// </summary>
/// <returns>是否加载成功</returns>
public static Boolean InitBaseEvm()
{
int result = DllLinker.Init_descriptors();
if (result == 1)
{
return true;
}
else
{
return false;
}
}
}
}
DllLinker
namespace WF_FaceRect.Core
{
//注,请勿直接调用本类方法,入口在 DataAdapter.cs
public class DllLinker
{
[DllImport("Asserts/Dll_FaceRect.dll", EntryPoint = "Init_descriptors", CallingConvention = CallingConvention.Cdecl)]
public static extern int Init_descriptors();
[DllImport("Asserts/Dll_FaceRect.dll", CharSet = CharSet.Ansi, EntryPoint = "FaceRects", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr FaceRects(byte[] ImageBuffer, int imageWedth, int imageHeight);
}
}
main
namespace WF_FaceRect
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
//初始化状态
bool isInited = false;
//摄像机持续运行关键字
bool statCarm = false;
//摄像机播放线程
Thread videoThread;
//测试图片
string testPicturePath = @"Asserts\test.jpg";
// First of all ,在你使用检测功能之前请务必优先进行初始化
private void InitBtn_Click(object sender, EventArgs e)
{
if (!isInited)
{
bool initRes = DataAdapter.InitBaseEvm();
if (initRes)
{
MessageBox.Show("加载成功!");
isInited = true;
}
else
{
MessageBox.Show("加载失败!");
isInited = false;
}
}
else
{
MessageBox.Show("请勿重复初始化","警告",MessageBoxButtons.OK,MessageBoxIcon.Warning);
}
}
//启动对一张图片的检验
private void startBtn_Click(object sender, EventArgs e)
{
if (!isInited)
{
MessageBox.Show("请优先对系统进行初始化", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
//===============================单个图像===============================
//Mat myImage = new Mat(testPicturePath, ImreadModes.Color);
如果图像太小可能影响识别准确度,你可以使用resize()方法适当放大
//List<RectModel> rects = DataAdapter.FaceRects(myImage);
//foreach(RectModel item in rects)
//{
// myImage.Rectangle(
// new OpenCvSharp.Point(item.Left, item.Top), //左上
// new OpenCvSharp.Point(item.Right, item.Bottom), //右下
// Scalar.Red, //线色
// 2 //线粗
// );
//}
展现
//pictureBox.Image = myImage.ToBitmap();
//===============================摄像头=====================================
videoThread = new Thread(videoShownFunc);
statCarm = true;
videoThread.Start();
}
}
/// <summary>
/// 视频播放实际执行方法
/// </summary>
private void videoShownFunc()
{
//加载视频
//VideoCapture capture = new VideoCapture(@"C:\Users\Administrator\Desktop\movice\source9.mp4"); //测试
VideoCapture capture = new VideoCapture(0);
//capture.Set(CaptureProperty.FrameWidth, 1280);
//capture.Set(CaptureProperty.FrameHeight, 680);
if (capture.IsOpened())
{
Console.WriteLine("摄像头已经打开");
}
else
{
MessageBox.Show("警告,摄像头未开启!");
return;
}
//图像人脸识别(注意,这里是true死循环,跳出循视频请Abort)
while (statCarm)
{
//读取一帧
//Mat myImage = new Mat(cs_pic,ImreadModes.Color);//测试
Mat myImage = new Mat();
capture.Read(myImage);
//Cv2.Flip(myImage, myImage, FlipMode.XY);
if (myImage.Empty()) //判断当前帧是否捕捉成功 这步很重要
{
Cv2.WaitKey(60);
myImage.Release();
continue;
}
//人脸图像检测
List<RectModel> rects = DataAdapter.FaceRects(myImage);
if(rects != null && rects.Count > 0)
{
// 绘制人脸框选区
foreach (RectModel item in rects)
{
myImage.Rectangle(
new OpenCvSharp.Point(item.Left, item.Top), //左上
new OpenCvSharp.Point(item.Right, item.Bottom), //右下
Scalar.Red, //线色
2 //线粗
);
}
}
//Cv2.ImShow("摄像头图像", myImage); //测试
Image imgshow = myImage.ToBitmap();
pictureBox.Image = imgshow;
Cv2.WaitKey(30);
//myImage.Release();//手动释放
//imgshow.Dispose();
GC.Collect(); //这里要使用gc,手动释放会造成GDI异常
}
}
}
声明与源码
我TM要下班了要下班了,再不赶紧回去饭菜都凉了!
代码就随便贴这么多吧,毕竟懂的都懂, CTRL+C就完了。