• C#使用Emgu CV来进行图片人脸检测


    项目需求:某市级组织考试,在考试前需审核考生采集表中的考生照片是否合格,由于要审核的考生信息采集表有很多,原先进行的是手动人工审核,比较费时费力,审核的要求也很简单,并不判断考生是否是图片本人(身份验证有另外一套程序来进行),只是看考生采集表中考生头像是否是人脸(是否存在辨识不清楚,不是人脸)。因此提出需求,看是否能用程序来检测考生信息采集表中的照片,只需找出来疑似不是人脸的考生所在文档位置(pdf文档)即可,存疑的考生再由人工进行审核。

    PDF文档中有很多页,每一页都是如图中的结构。

     

    经过百度摸索,采用了C#+WPF+Spire.PDF+Emgu CV+MvvmLight来进行人脸判断的技术选型。

    Emgu CV(https://sourceforge.net/projects/emgucv/files/emgucv/)是.NET平台下对OpenCV图像处理库的封装,也就是.NET版的
    OpenCV的全称是:Open Source Computer Vision Library。OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。Emgu CV官方带的有训练过的人脸识别模板,可以直接使用。

    Spire.PDF可以来读取PDF文档,同时可以读取到PDF文档中的图片。

    MvvmLight是WPF可以使用的一种MVVM模式的实现框架。

    项目技术选型确定以后,下面就是代码的编写。

    项目引用Emgu CV、Spire.PDF、MvvmLight

    从官网下载Emgu CV后,我们把它项目中的haarcascade_eye.xml、haarcascade_frontalface_alt.xml两个训练过的人脸识别模板放到bin/debug下,供Emgu CV使用时调用。

    引用MvvmLight后,会自动在项目中创建ViewModel目录,我们在此目录中新建一个Pdf2FaceInfoModel.cs类,用来做为检测结果的通知类。

    using System.ComponentModel;
     
    namespace Pdf2Face.ViewModel
    {
        public class Pdf2FaceInfoModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
     
            private string pdfName { get; set; }
            /// <summary>
            /// Pdf文件名
            /// </summary>
            public string PdfName
            {
                get => pdfName;
                set
                {
                    pdfName = value;
                    PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("PdfName"));
                }
            }
     
            private int pdfImgCount { get; set; } = 0;
            /// <summary>
            /// Pdf中图片数量
            /// </summary>
            public int PdfImgCount
            {
                get => pdfImgCount;
                set
                {
                    pdfImgCount = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PdfImgCount"));
                }
            }
     
            private int pdfFaceCount { get; set; } = 0;
            /// <summary>
            /// Pdf中人脸数量
            /// </summary>
            public int PdfFaceCount
            {
                get => pdfFaceCount;
                set
                {
                    pdfFaceCount = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PdfFaceCount"));
                }
            }
     
            private string pdfFaceSuccess { get; set; } ="否";
            /// <summary>
            /// 数量相对是否存疑 0 正常 1存疑
            /// </summary>
            public string PdfFaceSuccess
            {
                get => pdfFaceSuccess;
                set
                {
                    pdfFaceSuccess = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PdfFaceSuccess"));
                }
            }
        }
    }
    

    主程序只有一个界面,界面两个按钮,一个用来选择要检测pdf所在文件夹,一个用来开始检测。

    主程序代码:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    using Emgu.CV;
    using Emgu.CV.Structure;
    using Microsoft.WindowsAPICodePack.Dialogs;
    using Pdf2Face.ViewModel;
    using Spire.Pdf;
     
    namespace Pdf2Face
    {
        /// <summary>
        /// 人脸检测功能的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            private string _pdfDirPath;
            private readonly string _pdfFaceSaveDir;
            private readonly ObservableCollection<Pdf2FaceInfoModel> facelist = new ObservableCollection<Pdf2FaceInfoModel>();
     
            public MainWindow()
            {
                InitializeComponent();
                Thread.Sleep(10000);
                dataGrid.ItemsSource = facelist;
                _pdfFaceSaveDir = $"{AppDomain.CurrentDomain.BaseDirectory}face";
                if (!Directory.Exists(_pdfFaceSaveDir))
                {
                    Directory.CreateDirectory(_pdfFaceSaveDir);
                }
            }
            /// <summary>
            /// 选择Pdf所在目录
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void BtnChooseDir_OnClick(object sender, RoutedEventArgs e)
            {
                using (var folderBrowser = new CommonOpenFileDialog())
                {
                    folderBrowser.IsFolderPicker = true;
                    folderBrowser.Multiselect = false;
                    folderBrowser.Title = "选择考生照片所在文件夹";
                    if (folderBrowser.ShowDialog(GetWindow(this)) != CommonFileDialogResult.Ok) return;
                    _pdfDirPath = folderBrowser.FileName;
                    txtBlockPath.Text = _pdfDirPath;
                }
            }
            /// <summary>
            /// 人脸识别检测
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void BtnCheck_OnClick(object sender, RoutedEventArgs e)
            {
                if (string.IsNullOrEmpty(_pdfDirPath) || !Directory.Exists(_pdfDirPath))
                {
                    MessageBox.Show("目录无法访问。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
     
                var pdfs = FileSearch(_pdfDirPath, "*.pdf", SearchOption.AllDirectories,
                    x => x.Length > 6);
                if (pdfs.Length == 0)
                {
                    MessageBox.Show("指定的目录中没有发现PDF文件。", "错误", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
     
                txtBlockInfo.Text = $"Pdf文件数量{pdfs.Length}";
                var doc = new PdfDocument();
     
                Dispatcher?.InvokeAsync(async () =>
                {
                    await Task.Run(() =>
                    {
                        foreach (var pdf in pdfs)
                        {
                            doc.LoadFromFile(pdf);
                            var pagenum = 1;
     
                            foreach (PdfPageBase page in doc.Pages)
                            {
                                var newPdfFaceSaveDir = $"{_pdfFaceSaveDir}\{pdf.Substring(pdf.LastIndexOf('\') + 1)}";
                                if (page.ExtractImages() != null)
                                {
                                    if (!Directory.Exists(newPdfFaceSaveDir))
                                    {
                                        Directory.CreateDirectory(newPdfFaceSaveDir);
                                    }
                                    var images = new List<Image>();
                                    var model = new Pdf2FaceInfoModel
                                    {
                                        PdfName = $"{pdf.Substring(pdf.LastIndexOf('\') + 1)}_第{pagenum}页"
                                    };
                                    Dispatcher?.Invoke(() =>
                                    {
                                        facelist.Add(model);
     
                                    });
                                    var c = 0;
                                    foreach (var image in page.ExtractImages())
                                    {
                                        images.Add(image);
                                        var filename = $"{newPdfFaceSaveDir}\{pagenum}_{c}.png";
                                        image.Save(filename, ImageFormat.Png);
     
                                        #region 人脸判断
                                        //检测是否是人脸
                                        //如果支持用显卡,则用显卡运算
                                        CvInvoke.UseOpenCL = CvInvoke.HaveOpenCLCompatibleGpuDevice;
                                        //构建级联分类器,利用已经训练好的数据,识别人脸
                                        var face = new CascadeClassifier("haarcascade_frontalface_alt.xml");
                                        var eyes = new CascadeClassifier("haarcascade_eye.xml");
                                        //加载要识别的图片
                                        var img = new Image<Bgr, byte>(filename);
                                        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 System.Drawing.Size(50, 50));                                    
     
                                        if (facesDetected.Length > 0)
                                        {
                                            model.PdfFaceCount += facesDetected.Length;
                                            model.PdfFaceSuccess = facesDetected.Length > 1 ? "是" : "否";
                                            //删除图片,留下的都是无法正确识别的
                                            try
                                            {
                                                File.Delete(filename);
                                            }
                                            catch (Exception exception)
                                            {
                                                //
                                            }
                                        }
     
                                        img.Dispose();
                                        img2.Dispose();
                                        face.Dispose();
                                        #endregion
     
                                        c += 1;
                                    }
                                    model.PdfImgCount = images.Count;
                                }
                                pagenum += 1;
                            }
                            doc.Close();
                        }
     
                    });
                });
            }
     
            private string[] FileSearch(string directoryPath, string searchFilter, SearchOption option, Func<string, bool> func)
            {
                if (!Directory.Exists(directoryPath)) return null;
                var s = Directory.GetFiles(directoryPath, searchFilter, option).Where(func).ToArray();
                return s;
            }
     
            private void MainWindow_OnClosing(object sender, CancelEventArgs e)
            {
                Application.Current.Shutdown(0);
            }
        }
    }
    

    程序运行效果:

  • 相关阅读:
    [置顶] 怎么对待重复的代码
    AIX和Linux中wtmp的不同处理方式
    Visio 下载,及密钥
    全局变量和局部变量
    UNIX网络编程--IPV4 IPV6 ICMPV4 ICMPV6
    Android XML文档解析(一)——SAX解析
    rnqoj-30- [stupid]愚蠢的矿工-树形DP
    linux 文件内容的复制
    主流视音频平台参数
    FTP原理
  • 原文地址:https://www.cnblogs.com/wdw984/p/11758163.html
Copyright © 2020-2023  润新知