libctk shared library的设计及编码实践记录

news2024/11/26 8:19:27

一、引言

1.1 <libctk>的由来

1.2 <libctk>的设计理论依据

1.3 <libctk>的设计理念

二、<libctk>的依赖库

三、<libctk>的目录说明

四、<libctk>的功能模块及使用实例说明

4.1 日志模块
4.2 mysql client模块
4.3 ftp client模块
4.4 cv人脸检测与识别模块

五、下一步计划

5.1 后续版本规划

5.2 编写SDK

5.3 开源计划

一、引言
1.1 <libctk>的由来

       设计初衷是作为<Smart-park-FaceDR-SVC>项目的辅助项目,个中缘由可查阅:智慧园区项目人脸检测与识别子项目之-总体设计

       实际上,<libctk>发展到现在,已经完全独立于<Smart-park-FaceDR-SVC>项目,已经完完全全是linux下的一个shared library。
https://mp.csdn.net/mp_blog/creation/editor/139818825
1.2 <libctk>的设计理论依据:

      笔者为了为本文作铺垫,专门在之前编写了:linux下的动态链接库的编码实现。

      本文就不再描述<shared library>理论上的说明,一句话"干就完事!"(码农最喜欢的就是实操)。


1.3 <libctk>的设计理念

(1) 不重复造轮子

       利用现有的开源库来避免重复造轮子,提高开发效率和代码质量。同时,通过学习这些开源库的源代码,提升自己的编程技能和代码水平。

(2) 优雅、简洁、易用

       优雅:共享库的设计应该优雅简洁,遵循UNIX哲学,即"简单就是美"。避免过分复杂的设计,保持功能的简洁性和易懂性。

       简洁:共享库应该提供清晰简洁的接口,尽量少暴露内部实现细节,使用户可以方便地调用库的功能,而不需要了解其具体实现。

       易用:共享库应该易于安装、配置和使用,提供简单的API和文档,使用户能够快速上手并应用库的功能。

        总的来说,Linux下共享库的设计应该遵循优雅、简洁、易用的原则,以提高代码的可维护性和可扩展性,同时提供良好的用户体验,让用户能够方便地使用和扩展库的功能。

二、<libctk>的依赖库

库名称说明
libconfig++libconfig++是一个C++版本的libconfig配置文件解析库。在Linux系统上,可以使用libconfig++库来方便地读取和修改配置文件,从而实现程序的灵活配置和参数设置。
spdlogspdlog是一个快速、可扩展的C++日志记录库,提供了多种日志记录方式和格式化选项。它支持多线程并发日志记录,可以轻松地集成到各种应用程序中。
MySQL  Connector/C++MySQL官方提供的C++库,用于在Linux系统上连接和操作MySQL数据库。需要在官网上下载对应OS版本的安装包。
libcurl-devlibcurl-dev  是用于 Linux 系统的 libcurl 的开发包,用于编译和链接程序需要使用  libcurl  库的程序。通过安装  libcurl-dev,开发人员可以在其代码中使用  libcurl  的功能,如  HTTP  请求、FTP  传输等等。
opencv4.9是一个开源的计算机视觉库,提供了很多计算机视觉和图像处理的功能,如图像处理、目标识别、特征检测等。OpenCV库可以在Linux下使用,并且提供了很多用于C++、Python和Java等编程语言的接口。

特别说明:以上库,笔者全部选择下载源码编译安装(从版本、个性化方面考虑)。当然,你也可选择apt-get方式安装。至于编译安装方法,你可以自行摆渡,也可以在笔者的博文中查找相关文章。

三、<libctk>的目录及CMakeList.txt文件说明

<libctk>的clion-IDE界面:

大概的目录结构是这样的:

libctk
│-- cmake
├-- conf 
│-- include
|-----ctk
|-------cvFace.h
|-------cvUtils.h
|-------error.h
|-- onnx
├── src
│   ├── cvFace.cpp
│   ├── cvUtils.cpp
│   ├── error.cpp
│   └── ...
│-- build
└── CMakeList.txt

目录说明:

目录说明
cmake.cmake文件的存放路径。如:FindOpencv.cmake、utils.cmake
conf配置文件的存放路径。如:log.conf,mysql.conf,faceDR.conf
include/ctk头文件的存放路径。
onnx存放<物体检测>的训练模型文件(ONNX文件)
srcc++源代码文件的存放路径。
CMakeList.txt不用说明吧。

CMakeList.txt:

# ----------------------------------------------------------------------------
#  Root CMake file for libctk
#
#    From the off-tree build directory, invoke:
#      $ cmake <PATH_TO_CTK_ROOT>
#  Author: RemonLin
# ----------------------------------------------------------------------------
# Disable in-source builds to prevent source tree corruption.
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
    message(FATAL_ERROR "
FATAL: In-source builds are not allowed.
       You should create a separate directory for build files.
")
endif()

list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
# message(STATUS "cmake_module_path is: ${CMAKE_MODULE_PATH}")
include(DepVersions)

#cmake_minimum_required(VERSION "${MIN_VER_CMAKE}" FATAL_ERROR)
cmake_minimum_required(VERSION 3.21..3.29 FATAL_ERROR)

# ---------------------------------------------------------------------------------------
# Start ctk project
# ---------------------------------------------------------------------------------------
include(cmake/utils.cmake)
ctk_extract_version()
message(STATUS "Build libctk: ${CTK_VERSION}")

project(ctk VERSION ${CTK_VERSION} LANGUAGES CXX)
# ---------------------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------------------
# Find libconfig++
# find_package(libconfig ${LIBCONFIG_VERSION} REQUIRED HINTS ${LIBCONFIG_INSTALLATION_PATH})
# Find spdlog 1.13.0
include(Findspdlog)
find_package(spdlog ${SPDLOG_VERSION} REQUIRED HINTS ${SPDLOG_INSTALLATION_PATH})

# Find OpenCV
include(FindOpenCV)
find_package(OpenCV ${OPENCV_VERSION} REQUIRED HINTS ${OPENCV_INSTALLATION_PATH})

# Find CURL
include(FindCURL)
find_package(CURL REQUIRED)

# must go before the project()/enable_language() commands
ctk_update(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE)
if(NOT DEFINED CMAKE_BUILD_TYPE)
    message(STATUS "'Release' build type is used by default. Use CMAKE_BUILD_TYPE to specify build type (Release or Debug)")
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build")
endif()
if(DEFINED CMAKE_BUILD_TYPE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${CMAKE_CONFIGURATION_TYPES}")
endif()

include(CheckIncludeFile)
include(GNUInstallDirs)

# set CMAKE_INSTALL_PREFIX
get_filename_component(CTK_INSTALL_PREFIX ${CMAKE_SOURCE_DIR} DIRECTORY)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)  # https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.html
    if(NOT CMAKE_TOOLCHAIN_FILE)
        if(WIN32)
            set(CMAKE_INSTALL_PREFIX "${CTK_INSTALL_PREFIX}/install" CACHE PATH "Installation Directory" FORCE)
        else()
            set(CMAKE_INSTALL_PREFIX "${CTK_INSTALL_PREFIX}/install" CACHE PATH "Installation Directory" FORCE)
        endif()
    else()
        # any cross-compiling
        set(CMAKE_INSTALL_PREFIX "${CTK_INSTALL_PREFIX}/install" CACHE PATH "Installation Directory" FORCE)
    endif()
endif()

# set(CMAKE_INSTALL_PREFIX "${CTK_INSTALL_PREFIX}/install" CACHE PATH "Installation Directory" FORCE)
message(STATUS "install to:${CMAKE_INSTALL_PREFIX}")

# ---------------------------------------------------------------------------------------
# Compiler config
# ---------------------------------------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# make sure __cplusplus is defined when using msvc and enable parallel build
if(MSVC)
    string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP")
endif()

set(CMAKE_CXX_EXTENSIONS OFF)

if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW")
    set(CMAKE_CXX_EXTENSIONS ON)
endif()

# ---------------------------------------------------------------------------------------
# compile option set
# ---------------------------------------------------------------------------------------

option(CTK_BUILD_ALL "Build all artifacts" OFF)

# build shared option
option(CTK_BUILD_SHARED "Build shared library" ON)
option(BUILD_SHARED_LIBS "Global flag to cause  add_library() to create shared libraries if on." ${CTK_BUILD_SHARED})

# precompiled headers option
option(CTK_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF)

# build position independent code
option(CTK_BUILD_PIC "Build position independent code (-fPIC)" ${CTK_BUILD_SHARED})

# example options
option(CTK_BUILD_EXAMPLE "Build example" ON)

# testing options
option(CTK_BUILD_TESTS "Build tests" OFF)

# warning options
option(CTK_BUILD_WARNINGS "Enable compiler warnings" OFF)

# install options
option(CTK_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." OFF)
option(CTK_INSTALL "Generate the install target" ON)
# option(CTK_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any CTK exceptions" OFF)

if(CTK_BUILD_PIC)
    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()

# ---------------------------------------------------------------------------------------
# Shared/static library
# ---------------------------------------------------------------------------------------
aux_source_directory(src/ CTK_SRCS)

if(CTK_BUILD_SHARED OR BUILD_SHARED_LIBS)
    if(WIN32)
        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
        list(APPEND CTK_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
    endif()
    add_library(ctk SHARED ${CTK_SRCS}
        include/ctk/error.h
        src/error.cpp
        src/error.cpp)
    #    target_compile_definitions(CTK PUBLIC CTK_SHARED_LIB)
    if(MSVC)
        target_compile_options(ctk PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
            /wd4275>)
    endif()
else()
    add_library(ctk STATIC ${CTK_SRCS})
endif()

set(CTK_INCLUDES_LEVEL "")
if(CTK_SYSTEM_INCLUDES)
    set(CTK_INCLUDES_LEVEL "SYSTEM")
endif()

set(LIBS_INCLUDE_DIR "/usr/local/include")
target_include_directories(ctk ${CTK_INCLUDES_LEVEL} PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
    "$<BUILD_INTERFACE:${LIBS_INCLUDE_DIR}"
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)

target_link_libraries(ctk PUBLIC
    libconfig++.so; libspdlog.so; mysqlcppconn8; mysqlcppconn; ${CURL_LIBS}; ${OpenCV_LIBS}
)

set_target_properties(ctk PROPERTIES VERSION ${CTK_VERSION} SOVERSION
    ${CTK_VERSION_MAJOR}.${CTK_VERSION_MINOR})
set_target_properties(ctk PROPERTIES DEBUG_POSTFIX d)


# ---------------------------------------------------------------------------------------
# Build binaries
# ---------------------------------------------------------------------------------------
if(CTK_BUILD_EXAMPLE OR CTK_BUILD_ALL)
    message(STATUS "Generating example(s)")
    add_subdirectory(example)
    # ctk_enable_warnings(example)
endif()

if(CTK_BUILD_TESTS OR CTK_BUILD_ALL)
    message(STATUS "Generating tests")
    enable_testing()
    #add_subdirectory(tests)
endif()


# ---------------------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------------------
message(STATUS "Generating install")

# ---------------------------------------------------------------------------------------
# Include files
# ---------------------------------------------------------------------------------------
message(STATUS "ctk bin dir is:${CMAKE_INSTALL_BINDIR}")
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install(
    TARGETS ctk example
    EXPORT ctk
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
INSTALL(DIRECTORY conf/ DESTINATION conf)
INSTALL(DIRECTORY onnx/ DESTINATION onnx)

四、<libctk>的功能模块及使用实例说明
4.1 日志模块

调用处必须:#include "ctk/xlogger.h"

void xlogger_sample() {
    auto pLogger = ctk::log::createLogger();  // 全局调用一次即可,以后用spdlog::XXX(msg);

    pLogger->error("libctk1.1.0 logger sample is Runing!!!");  //在调用处可以使用指针
    spdlog::info("FaceDR service start!");
    spdlog::error("出错了!");
    spdlog::debug("出错了!");

}

logger的配置在../conf/log.conf文件

log_format = "[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] %v";
log_name = "../logs/example.log";   /* 日志文件名称含路径 */
log_async = true;                   
log_type = "rotating";              /* daily || rotating*/
log_level = "info";                    /* 日志等级info */
flush_level = "error";         /* 日志刷新等级*/ 
log_q_size = 8192;                 /* 异步日志有用. 线程池可容纳的日志记录数*/
log_thread_count = 1;              /* 异步日志有用. 后台有多少个处理日志的子线程*/
log_flush_every = 5;               /* 异步日志有用. 每5s刷新一次,将缓存的日志写入目标*/

daily_logger = {
    log_hour = 0;               /* 日志创建-时*/
    log_minute = 0;             /* 日志创建-分*/         
};

rotating_logger = {
    log_size = 3145728;              /* 日志文件大小 */
    max_files = 3;                    /* 最多保存日志文件数量*/
};


4.2 mysql client模块

       两种连接mysql server方式:

client方式:具有pool功能。

// 传入参数方式调用
void mysqlx_client_sample() {
    auto pClient1 = ctk::mysql::MyClient::create("192.168.1.207", 33060, "admin", "Admin207", 7);
    auto pClient2 = ctk::mysql::MyClient::create();   // 从mysql.conf读取参数
    //auto pLogger = ctk::log::createLogger();
    auto pSession = std::make_shared<mysqlx::Session>(pClient1->getSession());
    std::string plateNo;

    mysqlx::Schema db = pSession->getSchema("tvdat");
    mysqlx::Table table = db.getTable("vehicle_info");

    spdlog::info("mysqlx_client_sample runing!");

    for (size_t i =1; i <= 50; i++) {
        plateNo = "闽K" + generate_random_string(5);
        //spdlog::info(plateNo.c_str());
        table.insert().values(plateNo, "Remon Lin").execute();
        //table.select()
    }

}

//!从 ../conf/mysql.conf读取参数
void mysqlx_client_sample_cfg() {
    auto pClient = ctk::mysql::MyClient::create();
    //auto pLogger = ctk::log::createLogger();
    auto pSession = std::make_shared<mysqlx::Session>(pClient->getSession());
    std::string plateNo;

    mysqlx::Schema db = pSession->getSchema("park");
    mysqlx::Table table = db.getTable("host");

    spdlog::info("mysqlx_client_sample runing!");

    std::string hostID("1217");
    std::string ip("1217");
    std::string txt("217 Linux server");

    try {
        table.insert().values(hostID, ip, txt).execute();
    } catch (const mysqlx::Error& e) {
        throw ctk::CtkException("mysql", e.what());
    }
}

session方式:通过创建的session操作数据库。

// 传入参数方式调用
void mysqlx_session_sample() {
    cout << "mysqlx_session_sample is runing!" << endl;
    auto pSession = ctk::mysql::MySession::create("192.168.1.142", 33060, "admin", "123456");
    std::string plateNo;

    mysqlx::Schema db = pSession->getSchema("tvdat");
    mysqlx::Table table = db.getTable("vehicle_info");

    spdlog::info("mysqlx_session_sample runing!");
    for (size_t i =1; i <= 60; i++) {
        plateNo = "闽F" + generate_random_string(5);
        //spdlog::info(plateNo.c_str());
        table.insert().values(plateNo, "Remon Lin").execute();
    }
}

//!从 ../conf/mysql.conf读取参数
void mysqlx_session_sample_cfg() {
    cout << "mysqlx_session_sample_cfg is runing!" << endl;
    auto pSession = ctk::mysql::MySession::create();
    std::string plateNo;

    mysqlx::Schema db = pSession->getSchema("tvdat");
    mysqlx::Table table = db.getTable("vehicle_info");

    spdlog::info("mysqlx_session_sample runing!");
    for (size_t i =1; i <= 60; i++) {
        plateNo = "京P" + generate_random_string(5);
        //spdlog::info(plateNo.c_str());
        table.insert().values(plateNo, "Remon Lin").execute();
    }

}

mysql client的配置在../conf/mysql.conf文件

#----------------------------------------------------------------------------------------------------------------------
#maxSize:定义最大可能池化会话数的整数。如果在使用了最大会话数的情况下使用尝试从池中获取会话,它将等待可用会话,直到queueTimeout。默认值为25。
#queueTimeout:一个整数值,定义客户端等待获得可用会话的时间(以毫秒为单位)。默认情况下,它不会超时。
#maxIdleTime:一个整数值,用于定义可用会话在删除之前在池中等待的时间(以毫秒为单位)。默认情况下,它不会清理会话。
#----------------------------------------------------------------------------------------------------------------------

host = "192.168.1.207";
port = 33060;
user = "admin";
pwd = "Admin207";
db_name = "park";
pool_max_size = 10;
pool_queue_timeout = 1000;
pool_max_idle_time = 6000;


4.3 ftp client模块

       封装了ctk::ftp::Client这个类,实现了ftp的上传功能,其它如下载等ftp功能暂且不考虑,因为目前为止,我的项目还不需要这些功能。

       上传有二个重构的函数:

/**@brief Upload cv::Mat to a remote server and save it as a JPG file
 * @param image Uploaded CV:: Mat
 * @param remoteFile JPG save path for remote server, automatically created if it does not exist
 * @return CURLcode(or errno) and error-message
 */
 std::tuple<int, std::string> upload(const cv::Mat &image, const std::string &remoteFile);

 /**@brief Upload a local file to a remote server
  * @param localFile
  * @param remoteFile
  * @return CURLcode(or errno) and error-message
  */
 std::tuple<int, std::string> upload(const std::string &localFile, const std::string &remoteFile) const;

第1个upload函数是上传cv::Mat(内存中)到远程图像服务器并保存为JPG文件。

第2个upload函数是上传本地文件(任意格式的有效文件)到远程图像服务器并保存为同一格式的文件。

优点:

(1) 支持多线程上传:如在一个线程内上传100个JPG文件到远程服务器,可以创建若干个线程并行上传。

(2) 性能优越:上传100个1M左右的人脸图片(JPG格式)到远程图片服务器(局域网内)耗时1秒左右。这是根据<Smart-park-FaceDR-svc>项目试运行一年多得出的结论。

(3) 支持断线续传功能。

实例1------上传本地路径下的JPG文件到远程服务器:

//! 单个文件上传
std::tuple<int, string> ftp_uploadFile(const std::string &localFile, const string &remotePath) {
    std::string host = "192.168.1.142";
    std::string user = "lin";
    std::string pwd = "123456";

    //auto pLogger = ctk::log::createLogger();
    ctk::ftp::Client client(std::move(host), std::move(user), std::move(pwd));
    auto [code, msg] = client.init();

    if (code != 0) {
        std::cout << msg << std::endl;
        spdlog::error(msg);
    }

    return client.upload(localFile, remotePath);
}

//! 多线程调用ftp_uploadFile函数实现路径下的JPG文件并行上传
int ftp_upload_dir_mt() {
    std::string host = "192.168.1.142";
    std::string user = "lin";
    std::string pwd = "123456";

    ctk::ftp::Client client(std::move(host), std::move(user), std::move(pwd));
    auto pLogger = ctk::log::createLogger();

    const std::string localPath = "/home/plum/repository/cvData/face/snap";
    std::string remotePath = "/home/lin/ftp_test_data/snap";
    // 使用std::future和std::async来实现多线程上传
    std::vector<std::future<std::tuple<int, string>>> results;

    // std::cout << "curl_version is :" << curl_version() << std::endl;
    std::cout << "Start uploading:" << ctk::utils::now_millis() << std::endl;
    for (const auto &entry : fs::directory_iterator(localPath)) {
        if (entry.path().extension() == ".JPG") {
            std::string filename = entry.path().filename().string();
            std::string localFile = entry.path().string();
            std::string remoteFile = (remotePath + "/").append(filename);

            results.emplace_back(std::async(std::launch::async, ftp_uploadFile, localFile, remoteFile));
        }
    }

    int res = 0;
    // 等待所有上传任务完成
    for (auto &result: results) {
        auto [code1, msg1] = result.get();
        if (code1 != 0) {
            // 处理错误
            std::cerr << "Upload failed with error code: " << code1 << std::endl;
            std::cerr << "Error upload image, error msg:" << msg1 << std::endl;
            pLogger->info(msg1);
            res = 1;
        }
    }
    std::cout << "Stop uploading:" << ctk::utils::now_millis() << std::endl;

    client.cleanup();

    return res;
}

实例2----------单线程及多线程上传cv::Mat(内存中)到远程服务器并保存为JPG文件

//! 上传cv::Mat实例
std::tuple<int, string> ftp_uploadMat(const string &remotePath, const cv::Mat &image) {
    std::string host = "192.168.1.142";
    std::string user = "lin";
    std::string pwd = "123456";

    ctk::ftp::Client client(std::move(host), std::move(user), std::move(pwd));

    auto [code, msg] = client.init();

    if (code != 0) {
        std::cout << msg << std::endl;
    }

    return client.upload(image, remotePath);

    //return res;
}

//! 单线程调用ftp_uploadMat实例
int ftp_uploadMat_single() {
    std::string remotePath = "/home/lin/ftp_test_data/";

    cv::Mat image = cv::imread("/home/plum/repository/cvData/face/init-image/DSC04599.JPG");

    if (image.empty()) {
        std::cerr << "Error loading image" << std::endl;
        return 1;
    }

    string remoteFile = remotePath + ctk::utils::filename("DSC04599.JPG");
    auto [code, msg] = ftp_uploadMat(remoteFile, image.clone());

    if (code != CURLE_OK)
    {
        std::cerr << "Error upload image, errcode is:" << code << std::endl;
        std::cerr << "Error upload image, error msg:" << msg << std::endl;
    }

    return code;
}

//! 多线程调用ftp_uploadMat实例
int ftp_uploadMat_mt() {
    std::vector<std::string> files_to_upload = {
            "/home/plum/repository/cvData/face/init-image/DSC04593.JPG",
            "/home/plum/repository/cvData/face/init-image/DSC04599.JPG",
            "/home/plum/repository/cvData/face/init-image/DSC04765.JPG",
            "/home/plum/repository/cvData/face/init-image/DSC04768.JPG"
    };

    std::string remotePath = "/home/lin/ftp_test_data/";

    // 使用std::future和std::async来实现多线程上传
    std::vector<std::future<std::tuple<int, string>>> results;

    std::cout << "curl_version is :" << curl_version() << std::endl;
    std::cout << "Start uploading:" << ctk::utils::now_millis() << std::endl;
    for (const auto &pair : files_to_upload) {
        cv::Mat image = cv::imread(pair);

        if (image.empty()) {
            std::cerr << "Error loading image" << std::endl;
            continue;
        }

        string remoteFile = remotePath + ctk::utils::filename(pair);
        results.emplace_back(std::async(std::launch::async, ftp_uploadMat, remoteFile, image));
    }

    // 等待所有上传任务完成
    for (auto &result: results) {
        auto [code, msg] = result.get();
        if (code != CURLE_OK) {
            // 处理错误
            std::cerr << "Upload failed with error code: " << code << std::endl;
            std::cerr << "Error upload image, error msg:" << msg << std::endl;
        }
    }
    std::cout << "Stop uploading:" << ctk::utils::now_millis() << std::endl;

    return 0;
}


4.4 cv人脸检测与识别模块

       采用opencv4.9的DNN模块封装了class YunetAndSF,实现了人脸检测与识别的所有功能(函数)。

//
// Created by Remon on 2023/2/21.
//

#ifndef CTK_CVFACE_H
#define CTK_CVFACE_H

#include "opencv2/opencv.hpp"
#include "ctk/mysqlx.h"
#include <map>
#include <utility>
#include <vector>
#include <string>
#include <iostream>
#include <chrono>

namespace ctk::face {

    /**@brief DNN-based face detector and recognizer
     * model download link: https://github.com/opencv/opencv_zoo/tree/master/models/face_detection_yunet
     * model download link: https://github.com/opencv/opencv_zoo/tree/master/models/face_recognition_sface
     */
    class YunetAndSF {
    public:
        YunetAndSF() = default;

        /**@brief Get face detector pointer
         * @return The face detector pointer
         */
        virtual cv::Ptr<cv::FaceDetectorYN> getDetectorPtr() = 0;

        /**@brief Get face recognizer pointer
         * @return The face recognizer pointer
         */
        virtual cv::Ptr<cv::FaceRecognizerSF> getRecognizerPtr() = 0;

        /**@brief Path of image file to be processed.Usually only one face is used for face registration
         * @param filePath Input file
         * @param scale The scaling ratio of the input image
         * @return false-Cannot read image in input file.
         */
        virtual bool setInputImage(const std::string& filePath, float scale) = 0;

        /**@brief Image mat to be processed.Usually from the camera
         * @param inputImage Input image
         * @param scale The scaling ratio of the input image
         * @return false-Cannot read image in input file.
         */
        virtual bool setInputImage(const cv::Mat& inputImage, float scale) = 0;

        /**@brief Get the input image resize(320,320).
         * @return
         */
        virtual cv::Mat getInputImage() = 0;

        /**@brief Get the scaling ratio of the input image
         *
         * @return
         */
        virtual float getScale() = 0;

        /**@brief Detects faces in the input image.
         * detection results stored in a 2D cv::Mat of shape [num_faces, 15]
         */
        virtual void detect() = 0;

        /**@brief Get the faces.Contains one or multiple faces
         * @return The faces image.stored in a 2D cv::Mat of shape [num_faces, 15]
         */
        virtual cv::Mat getFaces() = 0;

        /**@brief Extracting feature values.Using this method for a single face
         */
        virtual void extract() = 0;

        /**@brief Get the feature values.Using this method for a single face
         * @return The feature values
         */
        virtual cv::Mat getFeature() = 0;

        /**@brief Extract features from all faces.Multiple faces using this method
         */
        virtual void extractAll() = 0;

        /**@brief Get the feature vector.For face recognition containing multiple faces
         * @return
         */
        virtual std::vector<cv::Mat> getFeatureVec() = 0;

        /**@brief Create an instance smart pointer for the YunetAndSF class based on input parameters
         *
         * @param fd_model
         * @param fr_model
         * @param size_w
         * @param size_h
         * @param score_threshold
         * @param nms_threshold
         * @param top_k
         * @param backend_id
         * @param target_id
         * @return
         */
        static cv::Ptr<YunetAndSF> create(const std::string& fd_model,
                                          std::string& fr_model,
                                          int size_w = 320,
                                          int size_h = 320,
                                          float score_threshold = 0.9f,
                                          float nms_threshold = 0.3f,
                                          int top_k = 5000,
                                          int backend_id = 0,
                                          int target_id = 0);

        /**@brief Create an instance smart pointer for the YunetAndSF class based on the configuration file
         * @return
         */
        static cv::Ptr<YunetAndSF> create();

    };

    class Error {
    public:
        inline static std::string BAD_FORMAT_IMAGE = "missing file, improper permissions, unsupported or invalid format";
    };


}


#endif //CTK_CVFACE_H

实例----读取含有一张人脸的图片,检测出人脸特征并输出(应用于人脸注册):

void cvFaceDR_image(const string& imageFile) {
    std::string face_image_root = "/home/plum/repository/cvData/face/snap";

    try {
        auto ynsf = ctk::face::YunetAndSF::create();

        //string input_image_path = "/home/plum/repository/faceds/hsdata/DSC04593.JPG";
        if (!ynsf->setInputImage(imageFile, 0.16))
        {
            // std::cerr << "Cannot read image: " << imageFile << std::endl;
            throw ctk::CtkException("cvFace", ctk::face::Error::BAD_FORMAT_IMAGE);
            //;return 1;
        }

        std::string now = std::move(ctk::utils::now_millis());
        std::cout << imageFile << " FaceDR start in:" << now << std::endl;
        ynsf->detect();

        cv::Mat inputImage = ynsf->getInputImage();
        cv::Mat faces = ynsf->getFaces();

        auto vInputImage = ctk::cvutils::visualize(inputImage, faces, now);
        std::string vImage = ctk::utils::filenamePre(imageFile).append("_face.JPG");
        string& vImagePath(face_image_root);
        vImagePath.append(vImage);

        std::cout << "Results are saved to " << vImagePath << std::endl;
        cv::imwrite(vImagePath, vInputImage);

        ynsf->extract();
        cv::Mat feature = ynsf->getFeature();
        std::string ftStr = ctk::cvutils::matToStr(feature);



    } catch (const std::runtime_error& error) {
        std::cerr << error.what() << std::endl;
    }
}

       其它如检测含有多张人脸的图片,连接网络摄像机实时检测与识别等例子就不在此文列举了。


五、下一步计划
5.1 后续版本规划

       可能会新增加以下模块:

模块https
httpsssl验证、下载
webservice轻量级


5.2 编写SDK

       编写《libctk-SDK》,方便项目组新成员使用。
5.3 考虑开源

       现在还有点压力,争取年末在github平台开源。

HI,我是Remon,CSDN上的别名:AncleLeen(被csdn强迫更名),一位30年的老码农了。

VX:RemonLin,敬请备注:cvDNN。VX扫一扫:

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

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

相关文章

【Python】已解决:(SqlServer报错)SQL错误(208):对象名‘string_split’无效

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;&#xff08;SqlServer报错&#xff09;SQL错误&#xff08;208&#xff09;&#xff1a;对象名‘string_split’无效 一、分析问题背景 在使用Python连接SqlSe…

移动UI:我的界面,竟然有这么设计方式,而且个个都简洁美观。

移动应用中的个人中心页面通常包含以下内容&#xff1a; 1. 用户头像和昵称&#xff1a;展示用户的头像和昵称&#xff0c;用于个人身份的展示和识别。 2. 个人资料&#xff1a;展示用户的个人信息&#xff0c;如姓名、性别、生日、联系方式等。用户可以在这里查看和编辑自己…

增量模型:软件开发中的渐进式进步

目录 前言1. 什么是增量模型1.1 增量模型的工作原理1.2 增量模型与其他开发模型的对比 2. 增量模型的优势2.1 早期交付与用户反馈2.2 降低风险2.3 提高开发效率2.4 改进的项目管理 3. 增量模型的应用场景3.1 需求不明确的项目3.2 大型复杂项目3.3 有明确阶段性目标的项目 4. 增…

如何在LabVIEW中使用FPGA模块

LabVIEW FPGA模块是NI公司推出的一款强大工具&#xff0c;它允许用户使用LabVIEW图形化编程环境来开发FPGA&#xff08;现场可编程门阵列&#xff09;应用程序。与传统的HDL&#xff08;硬件描述语言&#xff09;编程相比&#xff0c;LabVIEW FPGA模块大大简化了FPGA开发的过程…

高效利用iCloud指南:打造无缝连接的数字生活

iCloud是苹果公司推出的一项云存储和云计算服务&#xff0c;它为用户提供了一个安全、便捷的云端存储空间&#xff0c;帮助用户在各个苹果设备之间无缝同步数据。无论是照片、文档、备忘录&#xff0c;还是应用程序数据&#xff0c;iCloud都能让你的数字生活更加高效和有序。本…

ECharts 源码代码规范

代码规范 - Apache EChartsApache ECharts&#xff0c;一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。https://echarts.apache.org/zh/coding-standard.html 源文件 [强制] JavaScr…

9. Revit API UI: UIView、UIDocument、框选聚焦

9. Revit API UI: UIView、UIDocument、框选聚焦 UI命名空间下的API&#xff0c;到这里差不多就押送讲完了&#xff0c;同Application那篇所讲的几个类与接口&#xff0c;都是带UI的对应了一个不带UI的&#xff0c;如UIApplication和Application&#xff0c;作用呢&#xff0c…

理解MySQL核心技术:外键(Foreign Key)的设计与实现

在日常开发中&#xff0c;数据库是必不可少的部分&#xff0c;而MySQL作为最流行的关系型数据库之一&#xff0c;广泛应用于各类项目中。为了确保数据的完整性和一致性&#xff0c;外键&#xff08;Foreign Key&#xff09;无疑是一个重要的概念。在本篇文章中&#xff0c;我们…

Py之dashscope:dashscope的简介、安装和使用方法、案例应用之详细攻略

Py之dashscope&#xff1a;dashscope的简介、安装和使用方法、案例应用之详细攻略 目录 dashscope的简介 1、产品的主要特点和优势包括&#xff1a; dashscope的安装和使用方法 1、安装 2、使用方法 dashscope的案例应用 1、通义千问-Max&#xff1a;通义千问2.5系列 2…

apk右键一键签名方法

使用说明 1 修改reg文件最后一行&#xff0c;修改为自己的电脑路径 2 修改bat文件apksigner_path路径为自己的SDK路径&#xff0c;将签名文件命名为platform.keystore放在该文件夹内 3 运行reg文件添加注册表后&#xff0c;要签名的apk右键选择“cux”系统签名即可 一键cux系…

第4章,在 PyCharm 中创建、打开、关闭项目的操作

在 PyCharm 中创建、打开、关闭项目的操作 在PyCharm中创建、打开和关闭项目的操作步骤。以下是每个操作的步骤说明&#xff0c;以及在PyCharm界面中可能对应的区域&#xff1a; 1、创建新项目 1&#xff09;启动PyCharm&#xff1a; 打开PyCharm IDE。 2&#xff09;创建新…

从0开始C++(十):异常处理——throw、try-catch、标准异常体系与粗略捕获

目录 概念 抛出异常&#xff08;throw&#xff09; 捕获异常&#xff08;try - catch&#xff09; 标准异常体系 自定义异常 多重捕获 粗略捕获 概念 异常是程序在执行期间产生的问题&#xff0c;C异常是指在程序运行时发生的特殊情况&#xff0c;比如下所示的范围越界等…

Spring Boot中实现定时任务最常用的方法 @Scheduled 注解和 TaskScheduler 接口【包含详情代码】

Spring Boot中实现定时任务最常用的方法 Scheduled 注解和 TaskScheduler 接口【包含详情代码】 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中………

「51媒体」政企活动媒体宣发如何做?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 政企活动媒体宣发是一个系统性的过程&#xff0c;需要明确…

[数据集][目标检测]金属架螺栓螺丝有无检测数据集VOC+YOLO格式857张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;857 标注数量(xml文件个数)&#xff1a;857 标注数量(txt文件个数)&#xff1a;857 标注类别…

SAP ALV 负号提前

FUNCTION CONVERSION_EXIT_ZSIGN_OUTPUT. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" IMPORTING *" REFERENCE(INPUT) *" EXPORTING *" REFERENCE(OUTPUT) *"…

统信桌面操作系统上使用命令行添加软件图标到任务栏

原文链接&#xff1a;统信桌面操作系统上使用命令行添加软件图标到任务栏 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在统信桌面操作系统上使用命令行添加软件图标到任务栏的文章。通过命令行将常用软件的图标添加到任务栏&#xff0c;可以快速启动软件&#xf…

Links: Challenging Puzzle Game Template(益智游戏模板)

链接:挑战益智游戏 《Links》是一款独特且具有挑战性的益智游戏,即将发布。 每个级别都会向玩家展示不同的棋盘。目标是通过移动和旋转所有棋子来连接它们。每个棋子都有自己的特点和功能-你可以移动它们,旋转它们,或者两者兼而有之。连接所有棋子,以解决难度和挑战不断增…

吹爆这8款AI工具,好用到打耳光!

一、Snapseek-后台自动屏幕截图软件 Snapseek 是一款安卓端的后台自动屏幕截图软件&#xff0c;专为无缝后台存档你选择的应用程序而设计。一旦你指定了监控的应用程序&#xff0c;Snapseek 就会不断捕获图像&#xff0c;并利用 OCR&#xff08;光学字符识别&#xff09;技术进…

LabVIEW电涡流检测系统

开发了一种基于LabVIEW的软件与硬件结合的电涡流检测系统&#xff0c;通过同步采样技术和编码器的协同工作&#xff0c;显著提高了大型结构物的损伤检测精度和效率&#xff0c;具有良好的应用前景和实用价值。 项目背景 传统的手持式电涡流检测方法因其速度慢、灵敏度低、准确…