在Linux环境下利用MTCNN进行人脸检测(基于ncnn架构)

news2025/2/27 20:49:57

概述

本文将详细介绍如何在Linux环境下部署MTCNN模型进行人脸检测,并使用NCNN框架进行推理。

1. CMake的安装与配置

下载CMake源码

前往CMake官网下载,找到适合您系统的最新版本tar.gz文件链接,或者直接通过wget下载:CMake官方下载页面https://cmake.org/download/

cd ~
wget https://github.com/Kitware/CMake/releases/download/v3.x.x/cmake-3.x.x.tar.gz

请将3.x.x替换为您想要安装的具体版本号。

解压并进入解压后的目录

tar -xzvf cmake-3.x.x.tar.gz
cd cmake-3.x.x

编译与安装

  1. 配置编译选项:使用bootstrap脚本进行配置:

    ./bootstrap
  2. 编译:使用所有可用的核心进行并行编译:

    make -j$(nproc)
  3. 安装:将CMake安装到系统中:

    sudo make install
  4. 刷新共享库缓存(如果需要):

    sudo ldconfig

安装完成后,再次运行以下命令验证安装是否成功:

cmake --version

配置环境变量(可选)

如果您选择自定义安装路径(例如/usr/local/bin以外的路径),可能需要手动配置环境变量以确保系统能够找到新安装的CMake。

编辑~/.bashrc~/.zshrc文件(取决于您使用的shell),添加以下行:

export PATH=/path/to/cmake/bin:$PATH

其中/path/to/cmake/bin是您指定的CMake安装路径下的bin目录。

保存文件后,运行以下命令使更改生效:

source ~/.bashrc  # 对于Bash用户
# 或者
source ~/.zshrc   # 对于Zsh用户

2. Protobuf的安装

更新系统软件包

首先,更新您的系统软件包列表,确保所有现有的包都是最新的:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建Protobuf所需的各种工具和库:

sudo apt-get install autoconf automake libtool curl make g++ unzip

下载Protobuf源码

您可以从GitHub上克隆官方Protobuf仓库,或者直接下载特定版本的压缩包。这里我们使用Git进行操作:

cd ~
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive

编译Protobuf

创建并进入构建目录

为了保持源代码目录的整洁,建议在一个新的目录中进行编译:

cd ~/protobuf
mkdir -p build && cd build

使用CMake配置项目

CMake是一个跨平台的构建系统生成器,支持多种IDE和构建工具。

  1. 配置CMake:

    cmake .. -DCMAKE_BUILD_TYPE=Release
  2. 编译Protobuf:

    make -j$(nproc)
  3. 安装Protobuf:

    sudo make install
    sudo ldconfig  # 刷新共享库缓存

验证安装

检查版本信息

验证Protobuf是否正确安装,并检查其版本号:

protoc --version

您应该看到类似如下的输出:

libprotoc 3.x.x

其中3.x.x是具体的版本号。

配置环境变量(可选)

如果希望在任何位置都能直接运行protoc命令,而无需指定完整路径,可以将Protobuf的bin目录添加到系统的PATH环境变量中。编辑~/.bashrc~/.zshrc文件,根据您的shell类型,添加以下行:

export PATH=$PATH:/usr/local/bin

保存文件后,运行以下命令使更改生效:

source ~/.bashrc  # 对于Bash用户
# 或者
source ~/.zshrc   # 对于Zsh用户

3. OpenCV库的安装与配置

更新系统软件包

首先,更新您的系统软件包列表:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建OpenCV所需的各种工具和库:

sudo apt-get install build-essential cmake git pkg-config libgtk-3-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python3-dev python3-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev
sudo apt-get install libdc1394-22-dev libopenblas-dev liblapack-dev gfortran
sudo apt-get install libprotobuf-dev protobuf-compiler

下载OpenCV源码

您可以通过Git克隆OpenCV的GitHub仓库来获取最新的稳定版本:

cd ~
git clone https://github.com/opencv/opencv.git
cd opencv
git checkout 4.x # 替换为所需的版本号

# 克隆contrib仓库(可选)
cd ~
git clone https://github.com/opencv/opencv_contrib.git
cd opencv_contrib
git checkout 4.x # 确保与主仓库版本一致

编译OpenCV

创建并进入构建目录

cd ~/opencv
mkdir -p build && cd build

使用CMake配置项目

运行CMake以配置构建选项。这里我们指定一些常用的选项,例如启用Python支持、设置安装路径等。如果您不需要这些功能或使用了不同的路径,请相应地调整命令。

cmake -D CMAKE_BUILD_TYPE=Release \
      -D CMAKE_INSTALL_PREFIX=/usr/local \
      -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
      -D BUILD_opencv_python3=ON \
      -D PYTHON3_EXECUTABLE=$(which python3) \
      -D PYTHON3_INCLUDE_DIR=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \
      -D PYTHON3_PACKAGES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") ..

编译

make -j$(nproc)

这可能需要一些时间,具体取决于您的硬件性能。

安装

完成编译后,使用以下命令安装OpenCV到系统中:

sudo make install
sudo ldconfig

验证安装

查找头文件和库文件

安装完成后,OpenCV的头文件通常位于/usr/local/include/opencv4/,而库文件则位于/usr/local/lib/

您可以检查这些位置是否包含必要的文件:

ls /usr/local/include/opencv4/
ls /usr/local/lib/

4. ncnn库在Linux环境下的编译

更新系统软件包

首先,更新您的系统软件包列表,确保所有现有的包都是最新的:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建NCNN所需的各种工具和库:

sudo apt-get install build-essential cmake git libprotobuf-dev protobuf-compiler
sudo apt-get install libvulkan-dev vulkan-utils  # 如果需要Vulkan支持

libprotobuf-devprotobuf-compiler 是用于处理模型文件(如 .param.bin 文件)的必要依赖项。

下载NCNN源码

您可以通过Git克隆NCNN的GitHub仓库来获取最新的稳定版本:

cd ~
git clone https://github.com/Tencent/ncnn.git
cd ncnn

如果您想要特定的版本,可以切换到对应的分支或标签:

git checkout <branch_or_tag_name>

例如,切换到最新稳定版:

git checkout master

编译NCNN

创建并进入构建目录

为了保持源代码目录的整洁,建议在一个新的目录中进行编译:

mkdir -p build && cd build

使用CMake配置项目

运行CMake以配置构建选项。这里我们指定一些常用的选项,例如启用Vulkan支持、设置安装路径等。如果您不需要这些功能或使用了不同的路径,请相应地调整命令。

cmake .. \
    -DCMAKE_BUILD_TYPE=Release \
    -DNCNN_VULKAN=ON \  # 如果你希望使用Vulkan加速,请启用此选项
    -DNCNN_PROTOBUF_USE_SYSTEM=ON \
    -DProtobuf_DIR=/usr/lib/x86_64-linux-gnu/pkgconfig \  # 手动指定 Protobuf 的路径
    -DProtobuf_INCLUDE_DIR=/usr/include \
    -DProtobuf_LIBRARY=/usr/lib/x86_64-linux-gnu/libprotobuf.so \
    -DProtobuf_PROTOC_EXECUTABLE=/usr/bin/protoc

    编译

    使用所有可用的核心进行并行编译:

    make -j$(nproc)

    这可能需要一些时间,具体取决于您的硬件性能。

    安装

    完成编译后,使用以下命令安装NCNN到系统中:

    sudo make install
    sudo ldconfig  # 刷新共享库缓存

    默认情况下,头文件会被安装到 /usr/local/include/ncnn,库文件会被安装到 /usr/local/lib

    验证安装

    查找头文件和库文件

    安装完成后,NCNN的头文件通常位于 /usr/local/include/ncnn,而库文件则位于 /usr/local/lib

    您可以检查这些位置是否包含必要的文件:

    ls /usr/local/include/ncnn/
    ls /usr/local/lib/

    5. MTCNN源码

    ncnn框架实现的mtcnn主要包含两个核心代码文件mtcnn.h,mtcnn.cpp

    mtcnn.h代码如下:

    #pragma once
    
    #ifndef __MTCNN_NCNN_H__
    #define __MTCNN_NCNN_H__
    #include <ncnn/net.h>
    #include <string>
    #include <vector>
    #include <time.h>
    #include <algorithm>
    #include <map>
    #include <iostream>
    
    using namespace std;
    struct Bbox
    {
        float score;
        int x1;
        int y1;
        int x2;
        int y2;
        bool exist;
        float area;
        float ppoint[10];
        float regreCoord[4];
    };
    
    
    class MTCNN {
    
    public:
        MTCNN(const string& model_path);
        MTCNN(const std::vector<std::string> param_files, const std::vector<std::string> bin_files);
        ~MTCNN();
    
        void configure_ncnn(ncnn::Net& net, int num_threads);
        void SetMinFace(int minSize);
        void detect(ncnn::Mat& img_, std::vector<Bbox>& finalBbox);
    private:
        void generateBbox(ncnn::Mat score, ncnn::Mat location, vector<Bbox>& boundingBox_, float scale);
        void nms(vector<Bbox>& boundingBox_, const float overlap_threshold, string modelname = "Union");
        void refine(vector<Bbox>& vecBbox, const int& height, const int& width, bool square);
    
        void PNet();
        void RNet();
        void ONet();
    
        ncnn::Net Pnet, Rnet, Onet;
        ncnn::Mat img;
    
        const float nms_threshold[3] = { 0.5f, 0.7f, 0.7f };
        const float mean_vals[3] = { 127.5, 127.5, 127.5 };
        const float norm_vals[3] = { 0.0078125, 0.0078125, 0.0078125 };
        const int MIN_DET_SIZE = 12;
        std::vector<Bbox> firstBbox_, secondBbox_, thirdBbox_;
        int img_w, img_h;
    
    private://部分可调参数
        const float threshold[3] = { 0.8f, 0.8f, 0.6f };
        int minsize = 20;
        const float pre_facetor = 0.709f;
    
    };
    
    
    #endif //__MTCNN_NCNN_H__
    

    mtcnn.cpp代码如下:

    #include "mtcnn.h"
    
    bool cmpScore(Bbox lsh, Bbox rsh) {
    	if (lsh.score < rsh.score)
    		return true;
    	else
    		return false;
    }
    
    bool cmpArea(Bbox lsh, Bbox rsh) {
    	if (lsh.area < rsh.area)
    		return false;
    	else
    		return true;
    }
    
    
    MTCNN::MTCNN(const std::string& model_path) {
        std::vector<std::string> param_files = {
            model_path + "/det1.param",
            model_path + "/det2.param",
            model_path + "/det3.param"
        };
    
        std::vector<std::string> bin_files = {
            model_path + "/det1.bin",
            model_path + "/det2.bin",
            model_path + "/det3.bin"
        };
    
        // 配置多线程
        
        int num_threads = 4; // 设置线程数
        configure_ncnn(Pnet, num_threads);
        configure_ncnn(Rnet, num_threads);
        configure_ncnn(Onet, num_threads);
        
    
        // 加载模型
        Pnet.load_param(param_files[0].data());
        Pnet.load_model(bin_files[0].data());
        Rnet.load_param(param_files[1].data());
        Rnet.load_model(bin_files[1].data());
        Onet.load_param(param_files[2].data());
        Onet.load_model(bin_files[2].data());
    }
    
    
    MTCNN::~MTCNN(){
        Pnet.clear();
        Rnet.clear();
        Onet.clear();
    }
    
    
    void MTCNN::configure_ncnn(ncnn::Net& net, int num_threads) {
        ncnn::Option opt;
        opt.num_threads = num_threads; // 设置线程数
        opt.use_vulkan_compute = false; // 如果不使用 Vulkan,设置为 false
        net.opt = opt;
    }
    
    void MTCNN::SetMinFace(int minSize){
    	minsize = minSize;
    }
    
    void MTCNN::generateBbox(ncnn::Mat score, ncnn::Mat location, std::vector<Bbox>& boundingBox_, float scale){
        const int stride = 2;
        const int cellsize = 12;
        //score p
        float *p = score.channel(1);//score.data + score.cstep;
        //float *plocal = location.data;
        Bbox bbox;
        float inv_scale = 1.0f/scale;
        for(int row=0;row<score.h;row++){
            for(int col=0;col<score.w;col++){
                if(*p>threshold[0]){
                    bbox.score = *p;
                    bbox.x1 = round((stride*col+1)*inv_scale);
                    bbox.y1 = round((stride*row+1)*inv_scale);
                    bbox.x2 = round((stride*col+1+cellsize)*inv_scale);
                    bbox.y2 = round((stride*row+1+cellsize)*inv_scale);
                    bbox.area = (bbox.x2 - bbox.x1) * (bbox.y2 - bbox.y1);
                    const int index = row * score.w + col;
                    for(int channel=0;channel<4;channel++){
                        bbox.regreCoord[channel]=location.channel(channel)[index];
                    }
                    boundingBox_.push_back(bbox);
                }
                p++;
                //plocal++;
            }
        }
    }
    
    void MTCNN::nms(std::vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname){
        if(boundingBox_.empty()){
            return;
        }
        sort(boundingBox_.begin(), boundingBox_.end(), cmpScore);
        float IOU = 0;
        float maxX = 0;
        float maxY = 0;
        float minX = 0;
        float minY = 0;
        std::vector<int> vPick;
        int nPick = 0;
        std::multimap<float, int> vScores;
        const int num_boxes = boundingBox_.size();
    	vPick.resize(num_boxes);
    	for (int i = 0; i < num_boxes; ++i){
    		vScores.insert(std::pair<float, int>(boundingBox_[i].score, i));
    	}
        while(vScores.size() > 0){
            int last = vScores.rbegin()->second;
            vPick[nPick] = last;
            nPick += 1;
            for (std::multimap<float, int>::iterator it = vScores.begin(); it != vScores.end();){
                int it_idx = it->second;
                maxX = (std::max)(boundingBox_.at(it_idx).x1, boundingBox_.at(last).x1);
                maxY = (std::max)(boundingBox_.at(it_idx).y1, boundingBox_.at(last).y1);
                minX = (std::min)(boundingBox_.at(it_idx).x2, boundingBox_.at(last).x2);
                minY = (std::min)(boundingBox_.at(it_idx).y2, boundingBox_.at(last).y2);
                //maxX1 and maxY1 reuse 
                maxX = ((minX-maxX+1)>0)? (minX-maxX+1) : 0;
                maxY = ((minY-maxY+1)>0)? (minY-maxY+1) : 0;
                //IOU reuse for the area of two bbox
                IOU = maxX * maxY;
                if(!modelname.compare("Union"))
                    IOU = IOU/(boundingBox_.at(it_idx).area + boundingBox_.at(last).area - IOU);
                else if(!modelname.compare("Min")){
                    IOU = IOU/((boundingBox_.at(it_idx).area < boundingBox_.at(last).area)? boundingBox_.at(it_idx).area : boundingBox_.at(last).area);
                }
                if(IOU > overlap_threshold){
                    it = vScores.erase(it);
                }else{
                    it++;
                }
            }
        }
        
        vPick.resize(nPick);
        std::vector<Bbox> tmp_;
        tmp_.resize(nPick);
        for(int i = 0; i < nPick; i++){
            tmp_[i] = boundingBox_[vPick[i]];
        }
        boundingBox_ = tmp_;
    }
    
    void MTCNN::refine(vector<Bbox> &vecBbox, const int &height, const int &width, bool square){
        if(vecBbox.empty()){
            cout<<"Bbox is empty!!"<<endl;
            return;
        }
        float bbw=0, bbh=0, maxSide=0;
        float h = 0, w = 0;
        float x1=0, y1=0, x2=0, y2=0;
        for(vector<Bbox>::iterator it=vecBbox.begin(); it!=vecBbox.end();it++){
            bbw = (*it).x2 - (*it).x1 + 1;
            bbh = (*it).y2 - (*it).y1 + 1;
            x1 = (*it).x1 + (*it).regreCoord[0]*bbw;
            y1 = (*it).y1 + (*it).regreCoord[1]*bbh;
            x2 = (*it).x2 + (*it).regreCoord[2]*bbw;
            y2 = (*it).y2 + (*it).regreCoord[3]*bbh;
    
            
            
            if(square){
                w = x2 - x1 + 1;
                h = y2 - y1 + 1;
                maxSide = (h>w)?h:w;
                x1 = x1 + w*0.5 - maxSide*0.5;
                y1 = y1 + h*0.5 - maxSide*0.5;
                (*it).x2 = round(x1 + maxSide - 1);
                (*it).y2 = round(y1 + maxSide - 1);
                (*it).x1 = round(x1);
                (*it).y1 = round(y1);
            }
    
            //boundary check
            if((*it).x1<0)(*it).x1=0;
            if((*it).y1<0)(*it).y1=0;
            if((*it).x2>width)(*it).x2 = width - 1;
            if((*it).y2>height)(*it).y2 = height - 1;
    
            it->area = (it->x2 - it->x1)*(it->y2 - it->y1);
        }
    }
    
    void MTCNN::PNet(){
        firstBbox_.clear();
        float minl = img_w < img_h? img_w: img_h;
        float m = (float)MIN_DET_SIZE/minsize;
        minl *= m;
        float factor = pre_facetor;
        vector<float> scales_;
        while(minl>MIN_DET_SIZE){
            scales_.push_back(m);
            minl *= factor;
            m = m*factor;
        }
        for (size_t i = 0; i < scales_.size(); i++) {
            int hs = (int)ceil(img_h*scales_[i]);
            int ws = (int)ceil(img_w*scales_[i]);
            ncnn::Mat in;
    
            resize_bilinear(img, in, ws, hs);
            ncnn::Extractor ex = Pnet.create_extractor();
            //ex.set_num_threads(2);
            ex.set_light_mode(true);
            ex.input("data", in);
            ncnn::Mat score_, location_;
            ex.extract("prob1", score_);
            ex.extract("conv4-2", location_);
            std::vector<Bbox> boundingBox_;
            generateBbox(score_, location_, boundingBox_, scales_[i]);
            nms(boundingBox_, nms_threshold[0]);
            firstBbox_.insert(firstBbox_.end(), boundingBox_.begin(), boundingBox_.end());
            boundingBox_.clear();
        }
    }
    void MTCNN::RNet(){
        secondBbox_.clear();
        int count = 0;
        for(vector<Bbox>::iterator it=firstBbox_.begin(); it!=firstBbox_.end();it++){
            ncnn::Mat tempIm;
            copy_cut_border(img, tempIm, (*it).y1, img_h-(*it).y2, (*it).x1, img_w-(*it).x2);
            ncnn::Mat in;
            resize_bilinear(tempIm, in, 24, 24);
            ncnn::Extractor ex = Rnet.create_extractor();
    		//ex.set_num_threads(2);
            ex.set_light_mode(true);
            ex.input("data", in);
            ncnn::Mat score, bbox;
            ex.extract("prob1", score);
            ex.extract("conv5-2", bbox);
    		if ((float)score[1] > threshold[1]) {
    			for (int channel = 0; channel<4; channel++) {
    				it->regreCoord[channel] = (float)bbox[channel];//*(bbox.data+channel*bbox.cstep);
    			}
    			it->area = (it->x2 - it->x1)*(it->y2 - it->y1);
    			it->score = score.channel(1)[0];//*(score.data+score.cstep);
    			secondBbox_.push_back(*it);
    		}
        }
    }
    void MTCNN::ONet(){
        thirdBbox_.clear();
        for(vector<Bbox>::iterator it=secondBbox_.begin(); it!=secondBbox_.end();it++){
            ncnn::Mat tempIm;
            copy_cut_border(img, tempIm, (*it).y1, img_h-(*it).y2, (*it).x1, img_w-(*it).x2);
            ncnn::Mat in;
            resize_bilinear(tempIm, in, 48, 48);
            ncnn::Extractor ex = Onet.create_extractor();
    		//ex.set_num_threads(2);
            ex.set_light_mode(true);
            ex.input("data", in);
            ncnn::Mat score, bbox, keyPoint;
            ex.extract("prob1", score);
            ex.extract("conv6-2", bbox);
            ex.extract("conv6-3", keyPoint);
    		if ((float)score[1] > threshold[2]) {
    			for (int channel = 0; channel < 4; channel++) {
    				it->regreCoord[channel] = (float)bbox[channel];
    			}
    			it->area = (it->x2 - it->x1) * (it->y2 - it->y1);
    			it->score = score.channel(1)[0];
    			for (int num = 0; num<5; num++) {
    				(it->ppoint)[num] = it->x1 + (it->x2 - it->x1) * keyPoint[num];
    				(it->ppoint)[num + 5] = it->y1 + (it->y2 - it->y1) * keyPoint[num + 5];
    			}
    
    			thirdBbox_.push_back(*it);
    		}
        }
    }
    
    void MTCNN::detect(ncnn::Mat& img_, std::vector<Bbox>& finalBbox_){
        img = img_;
        img_w = img.w;
        img_h = img.h;
        img.substract_mean_normalize(mean_vals, norm_vals);
    
        PNet();
        //the first stage's nms
        if(firstBbox_.size() < 1) return;
        nms(firstBbox_, nms_threshold[0]);
        refine(firstBbox_, img_h, img_w, true);
    
        //second stage
        RNet();
        if(secondBbox_.size() < 1) return;
        nms(secondBbox_, nms_threshold[1]);
        refine(secondBbox_, img_h, img_w, true);
    
        //third stage 
        ONet();
        if(thirdBbox_.size() < 1) return;
        refine(thirdBbox_, img_h, img_w, true);
        nms(thirdBbox_, nms_threshold[2], "Min");
    
        finalBbox_ = thirdBbox_;
    }
    

    6. Linux下进行推理

    在Linux下配置完Opencv和ncnn的环境后编写简单的main.cpp进行模型的推理,代码如下:

    #include "mtcnn.h"
    #include <opencv2/opencv.hpp>
    #include <chrono>
    
    using namespace cv;
    
    
    int main()
    {
        std::string model_path = "./models"; //根据模型权重所在位置修改路径
        MTCNN mm(model_path);
        mm.SetMinFace(20);
    
        cv::VideoCapture video("./video/video.mp4"); //根据测试视频所在位置修改路径
    
        if (!video.isOpened()) {
            std::cerr << "failed to load video" << std::endl;
            return -1;
        }
    
        std::vector<Bbox> finalBbox;
        cv::Mat frame;
        // 记录开始时间
        auto start = std::chrono::high_resolution_clock::now();
    
        do {
            finalBbox.clear();
            video >> frame;
            if (!frame.data) {
                std::cerr << "Capture video failed" << std::endl;
                break;
            }
    
            ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(frame.data, ncnn::Mat::PIXEL_BGR2RGB, frame.cols, frame.rows);
            mm.detect(ncnn_img, finalBbox);
    
            for (vector<Bbox>::iterator it = finalBbox.begin(); it != finalBbox.end(); it++) {
                if ((*it).exist) {
                    cv::rectangle(frame, cv::Point((*it).x1, (*it).y1), cv::Point((*it).x2, (*it).y2), cv::Scalar(0, 0, 255), 2, 8, 0);
                }
            }
    
        } while (1);
    
        // 释放资源
        video.release();
    
        // 记录结束时间
        auto end = std::chrono::high_resolution_clock::now();
    
        // 计算持续时间
        std::chrono::duration<double> duration = end - start;
    
        // 输出结果(秒)
        std::cout << "Time taken: " << duration.count() << " seconds" << std::endl;
    
        return 0;
    }
    

    测试项目的目录结构如下:

    mtcnn/
    ├── Makefile
    ├── video
    │   └── video.mp4
    ├── include/
    │   └── mtcnn.h
    ├── src/
    │   ├── mtcnn.cpp
    │   └── main.cpp
    └── models
        ├── det1.bin
        ├── det1.param
        ├── det2.bin
        ├── det2.param
        ├── det3.bin
        └── det3.param
    

    ncnn架构的mtcnn模型权重下载链接如下:

    ncnn架构的mtcnn模型权重下载https://download.csdn.net/download/m0_57010556/90433089Makefile的内容如下:

    # 编译器
    CXX = g++
    
    # 编译选项
    CXXFLAGS = -Wall -I./include -O2 -fopenmp `pkg-config --cflags opencv4`
    
    # 目标可执行文件名
    TARGET = face_detection
    
    # 源文件目录
    SRCDIR = src
    
    # 头文件目录
    INCDIR = include
    
    # 链接库路径
    OPENCV_LIBS = `pkg-config --libs opencv4`
    OPENCV_CFLAGS = `pkg-config --cflags opencv4`
    NCNN_CFLAGS = -I/home/ncnn/build/install/include
    NCNN_LIBS = -L/home/ncnn/build/install/lib -lncnn
    
    # 找到所有源文件
    SOURCES := $(wildcard $(SRCDIR)/*.cpp)
    
    # 生成目标文件列表
    OBJECTS := $(patsubst $(SRCDIR)/%.cpp, %.o, $(SOURCES))
    
    # 默认目标
    all: $(TARGET)
    
    # 链接目标文件生成可执行文件
    $(TARGET): $(OBJECTS)
    	$(CXX) $(CXXFLAGS) $^ -o $@ $(OPENCV_LIBS) $(NCNN_LIBS) -lpthread -ldl -lgomp
    
    # 规则:从源文件生成目标文件
    %.o: $(SRCDIR)/%.cpp
    	$(CXX) $(CXXFLAGS) $(OPENCV_CFLAGS) $(NCNN_CFLAGS) -c $< -o $@
    
    # 清理生成的文件
    clean:
    	rm -f $(OBJECTS) $(TARGET)
    
    .PHONY: all clean

    编译和运行

    在项目目录下运行以下命令来编译和运行你的程序:

    编译

    make

    这将编译 src/mtcnn.cppsrc/main.cpp 并生成可执行文件 face_detection

    运行

    ./face_detection

    你应该会看到输出:

    Capture video failed
    Time taken: 22.336 seconds

    此时说明在linux下模型推理成功

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307040.html

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

    相关文章

    AI数字人系统源码部署解决方案!!!

    一、开场白 如今&#xff0c;科技的步伐越来越快&#xff0c;数字人已经从想象中走进了我们的现实生活。它们在娱乐、教育、医疗等多个领域大放异彩。了解数字人的代码开发技术&#xff0c;能让我们更好地理解其工作原理&#xff0c;为那些想在这一领域大展拳脚或者用数字人技…

    W803|联盛德|WM IoT SDK2.X测试|(1)开箱:开发板及说明

    前几天关注的联盛德微电子新推出了WM IoT SDK2.X&#xff0c;正式发布后&#xff0c;邀请用户参加“免费试用&#xff0c;赢千元大礼”活动&#xff0c;填写信息&#xff0c;等待统一发送&#xff0c;很快收到了板子。 活动地址&#xff1a;联盛德微电子WM IoT SDK2.X正式发布…

    2.✨java练习1(熟悉“类”)

    1. A B - AcWing题库 问题描述 输入两个整数&#xff0c;求这两个整数的和是多少。 输入格式 输入两个整数A,B&#xff0c;用空格隔开 输出格式 输出一个整数&#xff0c;表示这两个数的和 数据范围 0≤A,B≤1e8 C #include <iostream> // 包含标准输入输出库 using n…

    基本网络安全的实现

    基本网络安全的实现 一 &#xff1a;AAA AAA 是Authentication&#xff0c;Authorization and Accounting&#xff08;认证、授权和计费&#xff09;的简 称&#xff0c;它提供了一个用来对认证、授权和计费这三种安全功能进行配置的一致性框架&#xff0c; 它是对网络安全…

    快手前端通用静态托管服务KFX演进历程:从崎岖土路到平坦高速

    快手静态部署托管服务&#xff08;KFX&#xff09;历经四年发展&#xff0c;经历了三个阶段&#xff0c;一步步从勉强能行车的“崎岖土路”到现在多车道并行的“平坦高速”&#xff0c;这一转变极大地提升了资源利用率和效率&#xff0c;满足业务的实际需要。本文将带你了解其背…

    Locale+Jackson导致Controller接口StackOverflowError异常解决

    问题 由于参与的项目有出海需求&#xff0c;即需要给外国人使用&#xff0c;即&#xff1a;需要支持i18n&#xff08;Internationalization的缩写&#xff0c;共20个字母&#xff0c;除去首尾两个字母&#xff0c;中间有18个&#xff0c;故简称i18n&#xff09;。 本来是好的…

    安卓工控平板电脑在环境监测设备中的运用

    安卓工控平板电脑在环境监测设备中的运用主要体现在以下几个方面&#xff1a; 一、耐用性与可靠性 安卓工控平板电脑通常具有坚固耐用的外壳设计&#xff0c;如铝合金面板和镀锌钢板箱体结构&#xff0c;能够抵抗高温、低温、湿度、震动等恶劣的工作环境。这种耐用性和可靠性…

    【洛谷排序算法】P1012拼数-详细讲解

    洛谷 P1012 拼数这道题本身并非单纯考察某种经典排序算法&#xff08;如冒泡排序、选择排序、插入排序、快速排序、归并排序等&#xff09;的实现&#xff0c;而是在排序的基础上&#xff0c;自定义了排序的比较规则&#xff0c;属于自定义排序类型的题目。不过它借助了标准库中…

    文心一言AI创意画

    介绍 文心一言是百度推出的新一代知识增强大语言模型&#xff0c;属于文心大模型家族的新成员。‌它能够与人对话互动、回答问题、协助创作&#xff0c;高效便捷地帮助人们获取信息、知识和灵感。‌ 特点 文心一言基于数万亿数据和数千亿知识进行融合学习&#xff0c;采用预训…

    java项目之基于ssm的图书馆书库管理系统(源码+文档)

    风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的图书馆书库管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 该系统可以实现图书信息管理…

    使用OpenCV实现帧间变化检测:基于轮廓的动态区域标注

    在计算机视觉中&#xff0c;帧间差异检测&#xff08;frame differencing&#xff09;是一种常用的技术&#xff0c;用于检测视频流中的动态变化区域。这种方法尤其适用于监控、运动分析、目标追踪等场景。在这篇博客中&#xff0c;我们将通过分析一个基于OpenCV的简单帧间差异…

    deepseek从入门到精通-第一篇.本地化部署

    前言 自从22年年底开始&#xff0c;人工智能开始从实验室一下子走入了普通人的视野中&#xff0c;chatgpt像一颗石子投入水中&#xff0c;溅起了一波又一波的涟漪。我们都通过各种方式试用大预言模型和机器进行对话或者提问。随着大语言模型的出现&#xff0c;各个类型的大模型…

    2025年SCI一区智能优化算法:真菌生长优化算法(Fungal Growth Optimizer,FGO),提供MATLAB代码

    一. 真菌生长优化算法&#xff08;FGO&#xff09; 真菌生长优化算法&#xff08;Fungal Growth Optimizer&#xff0c;FGO&#xff09;是一种新型的自然启发式元启发式算法&#xff0c;其灵感来源于自然界中真菌的生长行为。该算法通过模拟真菌的菌丝尖端生长、分支和孢子萌发…

    Ubutu部署WordPress

    前言 什么是word press WordPress是一种使用PHP语言开发的建站系统&#xff0c;用户可以在支持PHP和MySQL数据库的服务器上架设WordPress。它是一个开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;允许用户构建动态网站和博客。现在的WordPress已经强大到几乎可以…

    BIO、NIO、AIO解析

    一、基础概念 1、IO的含义 IO&#xff0c;Input/Output&#xff0c;即输入/输出。从计算机结构来看&#xff0c;IO描述了计算机系统和外部设备之间通讯的过程。从应用程序角度来看&#xff0c;一个进程的地址空间划分为 用户空间&#xff08;User space&#xff09; 和 内核空…

    【Python网络爬虫笔记】14-使用代理绕过访问限制

    【Python网络爬虫笔记】14-网络代理 目录什么是代理&#xff1f;为什么需要使用代理&#xff1f;代理的类型如何在Python中使用代理&#xff1f;使用requests库设置代理使用urllib库设置代理使用scrapy框架设置代理 典型案例&#xff1a;使用代理爬取豆瓣电影Top250步骤1&#…

    Linux中Shell运行原理和权限(下)(4)

    文章目录 前言一、Shell的运行原理二、Linux当中的权限问题Linux权限的概念如何将普通用户添加到信任列表 三、Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的相关设置方法如…

    OceanBase数据库实战:Windows Docker部署与DBeaver无缝对接

    一、前言 OceanBase 是一款高性能、高可扩展的分布式数据库&#xff0c;适用于大规模数据处理和企业级应用。 随着大数据和云计算的普及&#xff0c;OceanBase 在企业数字化转型中扮演着重要角色。学习 OceanBase 可以帮助开发者掌握先进的分布式数据库技术&#xff0c;提升数…

    技术速递|.NET 9 网络优化

    作者&#xff1a;Mňa&#xff0c;Natalia&#xff0c;Anton 排版&#xff1a;Alan Wang 秉承我们的传统&#xff0c;我们很高兴与您分享这篇博客文章&#xff0c;以介绍新的 .NET 版本中网络领域相关的最新动态和最有趣的变化。今年&#xff0c;我们带来了 HTTP 领域的改变、新…

    Tag标签的使用

    一个非常适合运用在vue项目中的组件&#xff1a;Tag标签。 目录 一、准备工作 1、安装element-plus库 2、配置element-plus库 二、Tag标签入门 1、打开element官网&#xff0c;搜索tag标签 2、体验Tag标签的基础用法 三、Tag标签进阶训练1 1、定义一个数组&#xff0c;…