首页 > 编程语言 >C#集成ViewFaceCore人脸检测识别库

C#集成ViewFaceCore人脸检测识别库

时间:2023-10-07 12:44:28浏览次数:46  
标签:C# face ViewFaceCore 人脸 Location https var new

前言#

人脸检测与识别现在已经很成熟了,C# 上有 ViewFaceCore 这个很方便的库,但这种涉及到 native 调用的库,一般会有一些坑,本文记录一下开发和部署的过程。

本文的项目是 AIHub ,关于本项目的开发过程,可以参考之前的文章:项目完成小结:使用Blazor和gRPC开发大模型客户端

而且经过最近一个月的工作,我把这个项目重构了一遍,界面换成了 Ant Design ,增加了很多功能,更多的我会在后续的博客文章中分享。

先看效果#

多目标检测,我一下就想到了以前读书时很火的「少女时代」

ViewFaceCore简介#

这是一个基于 SeetaFace6 的 .NET 人脸识别解决方案

SeetaFace6是中科视拓最新开源的商业正式版本。突破了之前社区版和企业版版本不同步发布的情况,这次开源的v6版本正式与商用版本同步。

主要做了对 SeetaFace6 的 .Net 封装。

支持以下功能:

  • 年龄预测
  • 眼睛状态检测
  • 性别预测
  • 人脸检测
  • 口罩检测 / 戴口罩关键定定位,5个关键点
  • 人脸关键定定位 (5点 / 68点)
  • 人脸识别 (5点 / 68点)
  • 活体检测
  • 姿态检测
  • 质量检测

在 C# 中使用非常简单,不过因为是调用了C++的库,所以部署的时候会有点小坑,本文记录了这些小坑和解决方案。

添加依赖#

先添加以下依赖

<PackageReference Include="ViewFaceCore" Version="0.3.8" />
<PackageReference Include="ViewFaceCore.all_models" Version="6.0.7" />
<PackageReference Include="ViewFaceCore.Extension.ImageSharp" Version="0.3.7" />
<PackageReference Include="ViewFaceCore.runtime.ubuntu.20.04.x64" Version="6.0.7" />
<PackageReference Include="ViewFaceCore.runtime.win.x64" Version="6.0.7" />

ViewFaceCore.runtime. 开头的运行时,需要根据开发和部署的环境来安装,我这里安装了 Windows 版和 Linux 版本。

注意 Linux 版本还依赖 libgomp1 这个库,如果使用的时候报错,需要先安装。

人脸检测#

很简单,先创建个 FaceDetector 对象。

因为这个模型是调用了非托管资源,所以要记得使用 using 或者手动调用 Dispose 方法释放资源。

using FaceDetector _faceDetector = new();

然后传入图片对象就可以进行检测了,如果检测到人脸,会返回人脸框的四个坐标。

支持三种图片库:

  • SkiaSharp
  • ImageSharp
  • System.Drawing

第三个是微软官方的,据说要 Obsolete 了,所以我一般用 ImageSharp ,纯 C# 实现,跨平台也好用。

需要安装 ViewFaceCore.Extension.ImageSharp 依赖以支持 ImageSharp 图片。

简单例子#

先来一个最简单的例子,检测人脸,并把人脸框出来。

public async Task<byte[]> DrawFaceFrame(byte[] imageBuffer, string format = "jpg") {
  using var inputStream = new MemoryStream(imageBuffer);
  using var image = await Image.LoadAsync(inputStream);
  var faceInfos = await _faceDetector.DetectAsync(image);
  foreach (var face in faceInfos) {
    image.Mutate(x => {
      x.Draw(
        Color.HotPink, 2.5f,
        new RectangleF(face.Location.X, face.Location.Y, face.Location.Width, face.Location.Height)
      );
    });
  }

  using var outputStream = new MemoryStream();
  await image.SaveAsync(outputStream, image.DetectEncoder($"demo.{format}"));
  return outputStream.ToArray();
}

以上代码实现了传入 byte[] 类型的图片流,然后输出画了人脸框的图片,同样是 byte[] 类型。

非常滴简单,不过 ImageSharp 的文档太少了,还是得探索一下才知道咋画方框。

完整用法#

以前文「先看效果」为例,先定义一下数据结构

public class FaceItem {
  public FaceInfo FaceInfo { get; set; }
  public FaceMarkPoint[] FaceMarkPoints { get; set; }
  public float[]? FaceFeatures { get; set; }
  public byte[]? ImageBuffer { get; set; }
}

public class FaceDetectResult {
  public List<FaceItem> FaceItems { get; set; }
  public byte[] ImageBuffer { get; set; }
}

需要使用 ViewFaceCore 里的三个对象

// 人脸检测
private readonly FaceDetector _faceDetector = new();

// 人脸标记点位
private readonly FaceLandmarker _faceMark = new();

// 人脸识别
private readonly FaceRecognizer _faceRecognizer = new();

关键代码

PS:代码写得很粗糙,性能一般般,只是凑合能用

/// <summary>
/// 人脸检测
/// </summary>
/// <param name="extractFeatures">是否提取人脸特征</param>
/// <param name="cropEveryFace">是否裁剪每个人脸小图</param>
/// <returns></returns>
public async Task<FaceDetectResult> Detect(
  byte[] imageBuffer, string format = "jpg",
  bool extractFeatures = false, bool cropEveryFace = false
) {
  var font = GetFont("segoeui.ttf");

  using var inputStream = new MemoryStream(imageBuffer);
  using var image = await Image.LoadAsync<Rgba32>(inputStream);
  using var resultImage = image.Clone();

  var result = new FaceDetectResult { FaceItems = new List<FaceItem>() };
  var faceInfos = await _faceDetector.DetectAsync(image);

  foreach (var face in faceInfos) {
    var faceItem = new FaceItem {
      FaceInfo = face,
      FaceMarkPoints = await _faceMark.MarkAsync(image, face)
    };

    // 提取人脸特征
    if (extractFeatures) {
      faceItem.FaceFeatures = await _faceRecognizer.ExtractAsync(image, faceItem.FaceMarkPoints);    
    }

    // 裁剪人脸小图
    if (cropEveryFace) {
      using var faceImage = image.Clone();
      var cropRect = GetCropRect(face, 5);
      try {
        faceImage.Mutate(x => x.Crop(cropRect));
      }
      catch (ArgumentException ex) {
        faceImage.Mutate(x => x.Crop(GetCropRect(face, 0)));
      }

      using (var faceImageStream = new MemoryStream()) {
        await faceImage.SaveAsync(faceImageStream, faceImage.DetectEncoder($"demo.{format}"));
        faceItem.ImageBuffer = faceImageStream.ToArray();
      }
    }

    result.FaceItems.Add(faceItem);

    // 画人脸框
    resultImage.Mutate(x => {
      x.Draw(
        Color.HotPink, 2.5f,
        new RectangleF(face.Location.X, face.Location.Y, face.Location.Width, face.Location.Height)
      );
      x.DrawText(
        $"face:{face.Score}", font, Color.HotPink,
        new PointF(face.Location.X, face.Location.Y - 20)
      );
    });
  }

  using var outputStream = new MemoryStream();
  await resultImage.SaveAsync(outputStream, resultImage.DetectEncoder($"demo.{format}"));
  result.ImageBuffer = outputStream.ToArray();

  return result;
}

字体和生成矩形的代码

ImageSharp 的文档非常缺乏,每一步都需要经过大量的搜索……

private Font GetFont(string fontFileName) {
  var path = !string.IsNullOrWhiteSpace(_pathPrefix) ? Path.Combine(_pathPrefix, fontFileName) : fontFileName;
  FontCollection collection = new();
  FontFamily family = collection.Add(path);
  return family.CreateFont(20, FontStyle.Bold);
}

private static Rectangle GetCropRect(FaceInfo faceInfo, int cropOffset) {
  return new Rectangle(faceInfo.Location.X - cropOffset, faceInfo.Location.Y - cropOffset,
                       faceInfo.Location.Width + cropOffset * 2, faceInfo.Location.Height + cropOffset * 2);
}

人脸识别#

人脸识别的思路:

  • 检测到人脸
  • 确定人脸关键点位置 (5点/68点)
  • 根据关键点提取特征
  • 在向量数据库中搜索该特征对应的人

最后一步使用了向量数据库,其实不用也行,人脸特征提取出来是 float[] 类型,理论上保存在任何地方都行,然后识别的时候把人脸特征拿出来与保存的特征库做遍历对比。

FaceRecognizer 对象提供了 Compare 功能,可以计算两个人脸特征的相似度。

这个特征其实是个向量,所以理论上是可以自己用其他算法来计算相似度,比如

  • 基于距离的欧氏距离、曼哈顿距离、
  • 夹角余弦
  • 皮尔逊相关系数

在上面人脸检测的「完整用法」中,已经把检测人脸、关键点位置、特征提取这部分搞定了。

接下来需要做的

  • 人脸信息录入,需要传统关系型数据库搭配向量数据库,或者是 PostgreSql 这类支持向量存储的数据库也行
  • 人脸信息比对,使用向量数据库的向量搜索功能,或者自行实现向量搜索算法

因为篇幅限制,本文就不展开人脸识别这一块内容了,接下来有时间单独写一篇文章。

部署#

接下来是填坑。

使用 docker 部署应用

本项目使用 .Net Core 7.0 所以对应使用 mcr.microsoft.com/dotnet/aspnet:7.0 基础镜像

这个镜像是基于 Debian11 系统制作

默认没有 libgomp1 这个库,需要自行添加

apt 软件源#

首先准备 sources.list 文件,用于修改 apt 仓库为国内源

# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free

# deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
# # deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free

deb https://security.debian.org/debian-security bullseye-security main contrib non-free
# deb-src https://security.debian.org/debian-security bullseye-security main contrib non-free

dockerfile#

base 构建阶段,安装 libgomp1 这个库

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY ./sources.list /etc/apt/sources.list
RUN apt update && apt install libgomp1
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM base AS final
WORKDIR /app
COPY . .
ENTRYPOINT ["./AIHub.Blazor"]

docker-compose.yml#

version: '3.6'

services:
  web:
    image: ${DOCKER_REGISTRY-}web
    container_name: aihub
    restart: always
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:80
    build:
      context: .
    volumes:
      - .:/app
    networks:
      - default
      - swag

networks:
  swag:
    name: swag
    external: true
  default:
    name: aihub

启动!#

一切准备就绪。

C#,启动!

docker compose up --build -d

参考资料#

 

【出处】:https://www.cnblogs.com/deali/p/17716884.html

标签:C#,face,ViewFaceCore,人脸,Location,https,var,new
From: https://www.cnblogs.com/mq0036/p/17746010.html

相关文章

  • 为什么用chdir而不用system
    system("cd..")和chdir(dir)实际上执行了不同的操作,虽然目标可能是相似的,但它们的作用和实现方式不同。system("cd.."):system函数是一个C标准库函数,用于执行系统命令。在system("cd..")中,它会启动一个新的shell进程,并在该shell进程中执行cd..命令,该命令用于切......
  • 20款VS Code实用插件推荐
    思维导航前言VSCode相关链接地址VSCode拓展插件安装适用于VSCode的中文(简体)语言包VSCodeC#开发工具包VisualStudio代码的图标TabNine代码补全工具CopilotAI辅助编程工具自动添加关闭标签VsCode最好的代码注释扩展插查找并修复JavaScript代码中的问题Vs......
  • NetCore学习笔记:单元测试和集成测试
    前言#我在使用AspNetCore的这段时间内,看了很多开源项目和博客,发现各种.Net体系的新技术很多人都有关注和使用,但却很少有人关注测试。测试是软件生命周期中的一个非常重要的阶段,对于保证软件的可靠性具有极其重要的意义。在应用程序的开发过程中,为了确保它的功能与预期一致,......
  • SpringCloud:Feign实现微服务之间相互请求
    文章目录......
  • AIGC革新,将文字或者LOGO融入AI视频基于PIKA-labs(Python3.10)
    很多平台都会禁止用户使用带有网址或者二维码的头像以及文章配图,这样可以有效的防止用户的一些“导流”行为。当然,头像、文章或者视频现在都是AI来审,毕竟现在人工的成本实在太高,但是如果我们把文字元素直接融入图像或者视频之中,如此一来,AI也会很难识别出一些“导流”的元素。本次......
  • 钡铼BL124EC:优化工业通信,畅通EtherCAT到Ethernet/IP
    钡铼BL124EC是一种用于工业网络通信的网关设备,专门用于将EtherCAT协议转换成Ethernet/IP协议。它充当一个桥梁,连接了使用不同协议的设备,使它们能够无缝地进行通信和互操作。具体来说,BL124EC通过支持EtherCAT(以太CAT)总线协议的设备与支持Ethernet/IP(以太网/IP)协议的设备之间建立连......
  • access
    access函数是C语言标准库中的一个函数,用于检查文件是否可以访问。函数原型intaccess(constchar*path,intmode);path:要检查的文件或目录的路径。mode:指定操作的方式,可以是以下值之一:F_OK:用于检查文件是否存在。R_OK:用于检查文件是否可读。W_OK:用于检查文件是否......
  • [已解决] Compilation error ptxas fatal : Value ‘sm_30‘ is not defined for opti
    在用cmake编译cuda程序时,总是报Compilationerrorptxasfatal:Value‘sm_30’isnotdefinedforoption‘gpu-name’问题,也是折腾了好久,感谢这位小哥的解决方案,亲试无误,万分感谢~转载:https://blog.csdn.net/Fucking_Code0916/article/details/132429186安装tiny-cudann出......
  • 构建自己的docker镜像
    目录前奏基于当前OS创建自己的镜像让Alpine不再是Alpine防走丢前奏rambo@debian:~$cat/etc/issueDebianGNU/Linux12\n\l基于当前OS创建自己的镜像rambo@debian:~$mkdirmyimagerambo@debian:~$sudocp-a/usr/lib/usr/lib32//usr/lib64//usr/bin/myimage......
  • java中如何对特大文件做断点续传RandomAccessFile
    Java中可以使用 RandomAccessFile 类来实现特大文件的断点续传功能。importjava.io.File;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.net.URL;importjava.net.HttpURLConnection;publicclassResumeDownloadExample{publicstaticvoi......