专业的编程技术博客社区

网站首页 > 博客文章 正文

OpenVINO开发必备知识点——人脸检测实战

baijin 2024-08-30 11:46:09 博客文章 2 ℃ 0 评论

OpenVINO是Intel推出的计算机视觉深度学习推理加速库,支持Intel CPU, 核心显卡,VPU和FPGA。前三篇文章分别介绍了OpenVINO的环境搭建、重要文件、目录、环境变量, 以及人脸特征提取SDK的开发。


人脸识别的完整流程主要包括人脸检测(人脸位置定位),人脸特征提取和人脸特征检索三个阶段。只提供人脸特征提取并不能完成完整的人脸识别。人脸识别的第一步是从图片中找出人脸的位置,然后将人脸图片抠出来作为人脸特征提取的输入,从而人脸特征提取器提出人脸特征,用于后续的人脸特征检索,因此本文就来实战一下人脸检测。本文所有操作的系统环境为Ubuntu16.04。


人脸检测的C++工程已经推到了Github: adamydwang/face-openvino。 该工程取名为face-openvino是希望能够包括整个人脸识别的完整sdk, 因此该工程集成了Github: adamydwang/insightface-openvino


CMake工程目录结构

本文延续insightface-openvino工程的做法,依然采用CMakeLists来构建C++工程,目录如下:


  • bin: 用于保存编译生成的人脸特征提取和人脸检测demo可执行程序。
  • build: 用于保存编译过程中产生的临时文件。
  • demo: 存放人脸特征提取和人脸检测demo程序。
  • image: 存放了一张测试图片。
  • include: 用于保存C++头文件。
  • lib: 用于保存编译生成的人脸相关的sdk,包括人脸特征提取和人脸检测。
  • model: 用于保存openvino模型文件。
  • src: 用于保存C++源文件。

完整的CMakeLists.txt文件内容如下
本工程的CMakeLists几乎是最简版,只包括了必须的内容,可用于初学者参考学习CMakeLists的编写。

cmake_minimum_required(VERSION 2.7)
project(insightface)
add_definitions(-std=c++11)
find_package(InferenceEngine REQUIRED)
find_package(OpenCV REQUIRED)
include_directories(${PROJECT_SOURCE_DIR}/include
                  ${InferenceEngine_INCLUDE_DIRS}
                  ${OpenCV_INCLUDE_DIRS})
set(LIBS ${InferenceEngine_LIBRARIES} ${OpenCV_LIBRARIES})
set(SRCS  ${PROJECT_SOURCE_DIR}/src/insightface.cpp
        ${PROJECT_SOURCE_DIR}/src/facedetector.cpp
        ${PROJECT_SOURCE_DIR}/src/baseface.cpp)
add_library(face STATIC ${SRCS})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_executable(feature_extract ${PROJECT_SOURCE_DIR}/demo/feature_extract.cpp)
target_link_libraries(feature_extract  face ${LIBS})
add_executable(face_detect ${PROJECT_SOURCE_DIR}/demo/face_detect.cpp)
target_link_libraries(face_detect face ${LIBS})
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

BaseFace类实现

本工程是在insightface-openvino工程的基础上开发的。当撰写FaceDetector类的时候发现, FaceDetectorInsightFace的方法90%的操作是相同的,因此果断从insightface-openvino中提取出BaseFace类,人脸检测和人脸特征提取都会继承自该类。
因为BaseFace类是从insightface-openvino中提出来的,因此BaseFace与上一篇文章中InsightFace类的实现重合度极高。


BaseFace类定义:

class BaseFace {
public:
//model: openvino 模型xml文件全路径
//dev: 设备类型,包括CPU, GPU, VPU等
//width, height: 模型接受的输入图片宽和高
BaseFace(const std::string& model, const std::string& dev, int width=112, int height=112);
~BaseFace();
int init();
int process(cv::Mat& image, std::vector<float>& res);
void preprocess(cv::Mat& image);
InferenceEngine::InferRequest m_ireq;
std::string                   m_model;
std::string                   m_device;
std::string                   m_input_name;
std::string                   m_output_name;
int                           m_width;
int                           m_height;
};

BaseFace类重要方法实现(有删减,完整版请参考Github: adamydwang/face-openvino):

//初始化方法,BaseFace类实例话必须被执行一次
int BaseFace::init() {
try {
  InferenceEngine::Core ie;
  //1. 读取网络模型到Host内存
  InferenceEngine::CNNNetwork network = ie.ReadNetwork(m_model);
  //2. 设置输入参数
  InferenceEngine::InputsDataMap input_info(network.getInputsInfo());
  auto input = *input_info.begin();
  m_input_name = input.first;
  input.second->setPrecision(InferenceEngine::Precision::U8);
  input.second->setLayout(InferenceEngine::Layout::NCHW);
  //3. 将模型载入目标设备
  InferenceEngine::ExecutableNetwork executable_network = ie.LoadNetwork(network, m_device);
  m_ireq = executable_network.CreateInferRequest();
  //4. 设置输出参数
  InferenceEngine::OutputsDataMap output_info(network.getOutputsInfo());
  m_output_name = output_info.begin()->first;
  output_info.begin()->second->setPrecision(InferenceEngine::Precision::FP32);
catch(...) { }
return 0; 
}
//CNN推理, 输入Mat格式BGR图片,输出模型原始推理结果
int InsightFace::process(cv::Mat& image, std::vector<float>& feature) {
//1.对输入图片预处理
preprocess(image);
//2.输入从Host拷贝至Device, 推理, 输出从Device拷贝至Host
m_ireq.Infer();
//3.从Host获取推理结果
InferenceEngine::Blob::Ptr output_blob = m_ireq.GetBlob(m_output_name);
const int dims = output_blob->size();
feature.resize(dims);
memcpy(feature.data(), output_blob->buffer(), dims * sizeof(float));
return 0;
}
//输入图片预处理
void InsightFace::preprocess(cv::Mat& image) {
cv::Mat resized;
//图片必须resize至模型所需要的大小
cv::resize(image, resized, cv::Size(m_width, m_height));
//将输入图片喂到可执行网络在Host对应的内存
InferenceEngine::Blob::Ptr blob = m_ireq.GetBlob(m_input_name);
unsigned char* ptr = (unsigned char*)blob->buffer();
for (int c = 0; c < 3; ++c) {
  for (int y = 0; y < m_height; ++y) {
    for (int x = 0; x < m_width; ++x) {
      *(ptr++) = resized.at<cv::Vec3b>(y, x)[c];
    }
  }
}
}

FaceDetector类实现

FaceDetector类继承自BaseFace类
FaceDetector类定义:

class FaceDetector : public BaseFace {
public:
//model: 人脸检测openvino模型xml文件全路径
//dev: 用于OpenVINO推理的设备类型,包括CPU, GPU, VPU等
//width, height: 人脸检测模型输入图片的宽和高
//threshold: 人脸框置信度阈值,只有推理出的人脸框的得分高于该阈值,才输出对应的人脸框位置
FaceDetector(const std::string& model, const std::string& dev, int width=300, int height=300, float threhold=0.8);
~FaceDetector();
int init();
int process(cv::Mat& image, std::vector<cv::Rect>& faces);
private:
float m_threshold;
};

FaceDetector类实现:

FaceDetector::FaceDetector(const std::string& model, const std::string& dev, int width, int height, float threshold)
: BaseFace(model, dev, width, height), m_threshold(threshold) {
}
FaceDetector::~FaceDetector() {}
int FaceDetector::init() {
return BaseFace::init();
}
int FaceDetector::process(cv::Mat& image, std::vector<cv::Rect>& faces) {
//保存图片原始的尺寸,用于后续还原人脸框大小
int width = image.cols;
int height = image.rows;
BaseFace::preprocess(image);
std::vector<float> raws;
if (BaseFace::process(image, raws) < 0) {
  return -1;
}
//output shape: [1,1,N,7]
//[image_id, predicted_label, confidence, x_min, y_min, x_max, y_max]
//refer to https://docs.openvinotoolkit.org/2019_R2/_intel_models_face_detection_retail_0005_description_face_detection_retail_0005.html
if (raws.size() % 7) {
  return -2;
}
for (int i = 0; i < raws.size(); i += 7) {
  //得分未超过阈值则跳过
  if (raws[i+2] < m_threshold) {
    continue;
  }
  int x = raws[i+3] * width;
  int y = raws[i+4] * height;
  int w = (raws[i+5] - raws[i+3]) * width;
  int h = (raws[i+6] - raws[i+4]) * height;
  faces.emplace_back(x, y, w, h);
}
return 0;
}

人脸检测Demo

demo程序非常简单,就是调用人脸检测sdk, 对本地图片进行人脸检测,并将人脸框画在图片上保存。

int main(int argc, char** argv) {
if (argc != 4) {
  std::cout << "Usage: ./" << argv[0] << " model_path device image" << std::endl;
  return -1;
}
std::string model_path = argv[1];
std::string device = argv[2];
std::string image_name = argv[3];
FaceDetector face(model_path, device);
if (face.init() < 0) {
  std::cout << "FaceDetector init fail" << std::endl;
  return -2;
}
cv::Mat image = cv::imread(image_name);
if (image.empty()) {
  std::cout << "Invalid image" << std::endl;
  return -3;
}
std::vector<cv::Rect> faces;
struct timeval start, end;
gettimeofday(&start, NULL);
face.process(image, faces);
gettimeofday(&end, NULL);
for (auto& rect : faces) {
  std::cout << rect.x << ", " << rect.y << "," << rect.width << "," << rect.height << std::endl;
  //画出人脸检测框
  cv::rectangle(image, rect, cv::Scalar(255,0,0));
}
//保存带人脸框的图片
cv::imwrite("output.jpg", image);
std::cout << "Elapsed Time: " << (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec) / 1000 << " ms" << std::endl;
return 0;
}

人脸检测结果:

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表