手部关键点检测5:C++实现手部关键点检测(手部姿势估计)含源码 可实时检测

news2025/1/9 2:13:51

手部关键点检测5:C++实现手部关键点检测(手部姿势估计)含源码 可实时检测

目录

手部关键点检测4:C++实现手部关键点检测(手部姿势估计)含源码 可实时检测

1.项目介绍

2.手部关键点检测(手部姿势估计)方法

(1)Top-Down(自上而下)方法

(2)Bottom-Up(自下而上)方法:

3.手部关键点检测模型

(1) 手部关键点检测模型的训练

(2) 将Pytorch模型转换ONNX模型

(3) 将ONNX模型转换为TNN模型

4.手部关键点检测C/C++部署

(1)项目结构

(2)配置开发环境(OpenCV+OpenCL+base-utils+TNN)

(3)部署TNN模型

(4)CMake配置

(5)main源码

(6)源码编译和运行

(7)Demo测试效果 

5.项目源码下载


1.项目介绍

本篇文章是项目《手部关键点检测(手部姿势估计)》系列文章之《C++实现手部关键点检测(手部姿势估计)含源码 可实时检测》;项目基于Pytorch深度学习框架,实现手部关键点检测(手部姿势估计)模型,其中手部检测采用YOLOv5模型,手部关键点检测是基于开源的HRNet进行改进,构建了整套手部关键点检测的训练和测试流程;为了方便后续模型工程化和Android平台部署,项目支持高精度HRNet检测模型,轻量化模型LiteHRNet和Mobilenet模型训练和测试,并提供Python/C++/Android多个版本;

本篇主要分享将Python训练后的手部检测和手部关键点检测模型部署到C/C++平台。我们将开发一个简易的、可实时运行的手部关键点检测的C/C++ Demo。下表格给出HRNet,以及轻量化模型LiteHRNet和Mobilenet的计算量和参数量,以及其检测精度

模型input-sizeparams(M)GFLOPsAP
HRNet-w32192×19228.48M5734.05M0.8570
LiteHRNet18192×1921.10M182.15M0.8023
Mobilenet-v2192×1922.63M529.25M0.7574

先展示一下C/C++版本的手部检测以及手部关键点检测(手部姿势估计)效果:

Android手部关键点检测(手部姿势估计)APP Demo体验:

https://download.csdn.net/download/guyuealian/88418582

【尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/133277748


更多项目《手部关键点检测(手部姿势估计)》系列文章请参考:

  • 手部关键点检测1:手部关键点(手部姿势估计)数据集(含下载链接)https://blog.csdn.net/guyuealian/article/details/133277630
  • 手部关键点检测2:YOLOv5实现手部检测(含训练代码和数据集)https://blog.csdn.net/guyuealian/article/details/133279222
  • 手部关键点检测3:Pytorch实现手部关键点检测(手部姿势估计)含训练代码和数据集https://blog.csdn.net/guyuealian/article/details/133277726
  • 手部关键点检测4:Android实现手部关键点检测(手部姿势估计)含源码 可实时检测https://blog.csdn.net/guyuealian/article/details/133931698
  • 手部关键点检测5:C++实现手部关键点检测(手部姿势估计)含源码 可实时检测https://blog.csdn.net/guyuealian/article/details/133277748

 


2.手部关键点检测(手部姿势估计)方法

手部关键点检测(手部姿势估计)的方法,目前主流的方法主要两种:一种是Top-Down(自上而下)方法,另外一种是Bottom-Up(自下而上)方法;

(1)Top-Down(自上而下)方法

将手部检测和手部关键点估计分离,在图像上首先进行手部目标检测,定位手部位置;然后crop每一个手部图像,再估计每个手部的关键点;这类方法往往比较慢,但姿态估计准确度较高。目前主流模型主要有CPN,Hourglass,CPM,Alpha Pose,HRNet等。

(2)Bottom-Up(自下而上)方法:

先估计图像中所有手部的关键点,然后在通过Grouping的方法组合成一个一个手部实例;因此这类方法在测试推断的时候往往更快速,准确度稍低。典型就是COCO2016年人体关键点检测冠军Open Pose。

通常来说,Top-Down具有更高的精度,而Bottom-Up具有更快的速度;就目前调研而言, Top-Down的方法研究较多,精度也比Bottom-Up(自下而上)方法高。

本项目基于开源的HRNet进行改进,关于HRNet项目请参考GitHub

HRNet: https://github.com/leoxiaobin/deep-high-resolution-net.pytorch


3.手部关键点检测模型

(1) 手部关键点检测模型的训练

本篇博文主要分享C++版本的模型部署,不包含Python版本的手部关键点检测以及相关训练代码,关于手部关键点检测的训练方法和数据集说明,请参考本人另一篇博文《手部关键点检测3:Pytorch实现手部关键点检测(手部姿势估计)含训练代码和数据集》手部关键点检测3:Pytorch实现手部关键点检测(手部姿势估计)含训练代码和数据集-CSDN博客

(2) 将Pytorch模型转换ONNX模型

目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行C/C++端上部署。部署流程可分为四步:训练模型->将模型转换ONNX模型->将ONNX模型转换为TNN模型->C/C++部署TNN模型。

训练好Pytorch模型后,我们需要先将模型转换为ONNX模型,以便后续模型部署。

  • 原始项目提供转换脚本,你只需要修改model_file为你模型路径即可
  •  convert_torch_to_onnx.py实现将Pytorch模型转换ONNX模型的脚本
python libs/convert_tools/convert_torch_to_onnx.py
"""
This code is used to convert the pytorch model into an onnx format model.
"""
import os
import torch.onnx
from pose.inference import PoseEstimation
from basetrainer.utils.converter import pytorch2onnx
 
 
def load_model(config_file, model_file, device="cuda:0"):
    pose = PoseEstimation(config_file, model_file, device=device)
    model = pose.model
    config = pose.config
    return model, config
 
 
def convert2onnx(config_file, model_file, device="cuda:0", onnx_type="kp"):
    """
    :param model_file:
    :param input_size:
    :param device:
    :param onnx_type:
    :return:
    """
    model, config = load_model(config_file, model_file, device=device)
    model = model.to(device)
    model.eval()
    model_name = os.path.basename(model_file)[:-len(".pth")]
    onnx_file = os.path.join(os.path.dirname(model_file), model_name + ".onnx")
    # dummy_input = torch.randn(1, 3, 240, 320).to("cuda")
    input_size = tuple(config.MODEL.IMAGE_SIZE)  # w,h
    input_shape = (1, 3, input_size[1], input_size[0])
    pytorch2onnx.convert2onnx(model,
                              input_shape=input_shape,
                              input_names=['input'],
                              output_names=['output'],
                              onnx_file=onnx_file,
                              opset_version=11)
 
 
if __name__ == "__main__":
    model_file = "../../work_space/hand/mobilenet_v2_21_192_192_custom_coco_20230928_065444_0934/model/best_model_153_0.7574.pth"
    config_file = "../../work_space/hand/mobilenet_v2_21_192_192_custom_coco_20230928_065444_0934/mobilenetv2_hand_192_192.yaml"
    convert2onnx(config_file, model_file)

(3) 将ONNX模型转换为TNN模型

目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行C/C++端上部署

TNN转换工具:

  • (1)将ONNX模型转换为TNN模型,请参考TNN官方说明:TNN/onnx2tnn.md at master · Tencent/TNN · GitHub
  • (2)一键转换,懒人必备:一键转换 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine   (可能存在版本问题,这个工具转换的TNN模型可能不兼容,建议还是自己build源码进行转换,2022年9约25日测试可用)

​​​​

4.手部关键点检测C/C++部署

项目IDE开发工具使用CLion,相关依赖库主要有OpenCV,base-utils以及TNN和OpenCL(可选),其中OpenCV必须安装,OpenCL用于模型加速,base-utils以及TNN已经配置好,无需安装;

项目仅在Ubuntu18.04进行测试,Windows系统下请自行配置好开发环境。

(1)项目结构

(2)配置开发环境(OpenCV+OpenCL+base-utils+TNN)

项目IDE开发工具使用CLion,相关依赖库主要有OpenCV,base-utils以及TNN和OpenCL(可选),其中OpenCV必须安装,OpenCL用于模型加速,base-utils以及TNN已经配置好,无需安装;

项目仅在Ubuntu18.04进行测试,Windows系统下请自行配置和编译

  • 安装OpenCV:图像处理

图像处理(如读取图片,图像裁剪等)都需要使用OpenCV库进行处理

安装教程:Ubuntu18.04安装opencv和opencv_contrib

OpenCV库使用opencv-4.3.0版本,opencv_contrib库暂时未使用,可不安装

  • 安装OpenCL:模型加速

 安装教程:Ubuntu16.04 安装OpenCV&OpenCL

OpenCL用于模型GPU加速,若不使用OpenCL进行模型推理加速,纯C++推理模型,速度会特别特别慢

  • base-utils:C++库

GitHub:https://github.com/PanJinquan/base-utils (无需安装,项目已经配置了)

base_utils是个人开发常用的C++库,集成了C/C++ OpenCV等常用的算法

  • TNN:模型推理

GitHub:https://github.com/Tencent/TNN (无需安装,项目已经配置了)

由腾讯优图实验室开源的高性能、轻量级神经网络推理框架,同时拥有跨平台、高性能、模型压缩、代码裁剪等众多突出优势。TNN框架在原有Rapidnet、ncnn框架的基础上进一步加强了移动端设备的支持以及性能优化,同时借鉴了业界主流开源框架高性能和良好拓展性的特性,拓展了对于后台X86, NV GPU的支持。手机端 TNN已经在手机QQ、微视、P图等众多应用中落地,服务端TNN作为腾讯云AI基础加速框架已为众多业务落地提供加速支持。

(3)部署TNN模型

项目实现了C/C++版本的车牌检测和车牌识别,车牌检测模型YOLOv5和车牌识别模型PlateNet,模型推理采用TNN部署框架(支持多线程CPU和GPU加速推理);图像处理采用OpenCV库,模型加速采用OpenCL,在普通设备即可达到实时处理。

如果你想在这个 Demo部署你自己训练的车牌检测模型YOLOv5和车牌识别模型PlateNet,你可将训练好的Pytorch模型转换ONNX ,再转换成TNN模型,然后把原始的模型替换成你自己的TNN模型即可。

(4)CMake配置

这是CMakeLists.txt,其中主要配置OpenCV+OpenCL+base-utils+TNN这四个库,Windows系统下请自行配置和编译

cmake_minimum_required(VERSION 3.5)
project(Detector)

add_compile_options(-fPIC) # fix Bug: can not be used when making a shared object
set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -pthread")
#set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
#set(CMAKE_CXX_FLAGS_DEBUG "-g")

if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    # -DCMAKE_BUILD_TYPE=Debug
    # -DCMAKE_BUILD_TYPE=Release
    message(STATUS "No build type selected, default to Release")
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (default Debug)" FORCE)
endif ()

# opencv set
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS} ./src/)
#MESSAGE(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")

# base_utils
set(BASE_ROOT 3rdparty/base-utils) # 设置base-utils所在的根目录
add_subdirectory(${BASE_ROOT}/base_utils/ base_build) # 添加子目录到build中
include_directories(${BASE_ROOT}/base_utils/include)
include_directories(${BASE_ROOT}/base_utils/src)
MESSAGE(STATUS "BASE_ROOT = ${BASE_ROOT}")


# TNN set
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake buil ds it for you.
# Gradle automatically packages shared libraries with your APK.
# build for platform
# set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
if (CMAKE_SYSTEM_NAME MATCHES "Android")
    set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_ARM_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
    set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE)  # Multi-Thread
    #set(TNN_HUAWEI_NPU_ENABLE OFF CACHE BOOL "" FORCE)
    add_definitions(-DTNN_OPENCL_ENABLE)           # for OpenCL GPU
    add_definitions(-DTNN_ARM_ENABLE)              # for Android CPU
    add_definitions(-DDEBUG_ANDROID_ON)            # for Android Log
    add_definitions(-DPLATFORM_ANDROID)
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
    set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_X86_ENABLE OFF CACHE BOOL "" FORCE)
    set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
    set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE)  # Multi-Thread
    add_definitions(-DTNN_OPENCL_ENABLE)           # for OpenCL GPU
    add_definitions(-DDEBUG_ON)                    # for WIN/Linux Log
    add_definitions(-DDEBUG_LOG_ON)                # for WIN/Linux Log
    add_definitions(-DDEBUG_IMSHOW_OFF)            # for OpenCV show
    add_definitions(-DPLATFORM_LINUX)
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
    set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_X86_ENABLE ON CACHE BOOL "" FORCE)
    set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
    set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE)  # Multi-Thread
    add_definitions(-DTNN_OPENCL_ENABLE)           # for OpenCL GPU
    add_definitions(-DDEBUG_ON)                    # for WIN/Linux Log
    add_definitions(-DDEBUG_LOG_ON)                # for WIN/Linux Log
    add_definitions(-DDEBUG_IMSHOW_OFF)            # for OpenCV show
    add_definitions(-DPLATFORM_WINDOWS)
endif ()
set(TNN_ROOT 3rdparty/TNN)
include_directories(${TNN_ROOT}/include)
include_directories(${TNN_ROOT}/third_party/opencl/include)
add_subdirectory(${TNN_ROOT}) # 添加外部项目文件夹
set(TNN -Wl,--whole-archive TNN -Wl,--no-whole-archive)# set TNN library
MESSAGE(STATUS "TNN_ROOT = ${TNN_ROOT}")

# Detector
include_directories(src)
set(SRC_LIST
        src/Interpreter.cpp
        src/pose_detector.cpp
        src/object_detection.cpp
        src/pose_filter.cpp
        src/yolov5.cpp
        )
add_library(dlcv SHARED ${SRC_LIST})
target_link_libraries(dlcv ${OpenCV_LIBS} base_utils)
MESSAGE(STATUS "DIR_SRCS = ${SRC_LIST}")
add_executable(Detector src/main.cpp)
target_link_libraries(Detector dlcv ${TNN} -lpthread)


(5)main源码

主程序中函数main实现提供了手部关键点检测的使用方法,支持图片,视频和摄像头测试

  •     test_image_file();   // 测试图片文件
  •     test_video_file();   // 测试视频文件
  •     test_camera();       //测试摄像头
//
// Created by 390737991@qq.com on 2020/6/3.
//

#include "pose_detector.h"
#include "object_detection.h"
#include "yolov5.h"
#include "Types.h"
#include <iostream>
#include <string>
#include <vector>
#include "file_utils.h"
#include "image_utils.h"

using namespace dl;
using namespace vision;
using namespace std;

const int num_thread = 1; // 开启CPU线程数目
DeviceType device = GPU;  // 选择运行设备CPU/GPU

// 目标检测SSD或者YOLOv5
const float scoreThresh = 0.5;
const float iouThresh = 0.3;
//const char *det_model_file = (char *) "../data/tnn/ssd/rfb1.0_person_320_320_sim.opt.tnnmodel";
//const char *det_proto_file = (char *) "../data/tnn/ssd/rfb1.0_person_320_320_sim.opt.tnnproto";
//ObjectDetectionParam model_param = PERSON_MODEL;//模型参数
//ObjectDetection *detector = new ObjectDetection(det_model_file, det_proto_file, model_param, num_thread, device);

const char *det_model_file = (char *) "../data/tnn/yolov5/yolov5s05_320.sim.tnnmodel";
const char *det_proto_file = (char *) "../data/tnn/yolov5/yolov5s05_320.sim.tnnproto";
YOLOv5Param dets_model_param = YOLOv5s05_320;//模型参数
YOLOv5 *detector = new YOLOv5(det_model_file,
                              det_proto_file,
                              dets_model_param,
                              num_thread,
                              device);
// 关键点检测
const float poseThresh = 0.3;
const char *pose_model_file = (char *) "../data/tnn/pose/litehrnet18_192_192.sim.tnnmodel";
const char *pose_proto_file = (char *) "../data/tnn/pose/litehrnet18_192_192.sim.tnnproto";
PoseParam pose_model_param = HAND_PARAM;//模型参数
PoseDetector *pose = new PoseDetector(pose_model_file, pose_proto_file, pose_model_param, num_thread, device);

void test_image_file() {
    //测试图片的目录
    string image_dir = "../data/test_image";
    std::vector<string> image_list = get_files_list(image_dir);
    for (string image_path:image_list) {
        cv::Mat bgr = cv::imread(image_path);
        if (bgr.empty()) continue;
        FrameInfo resultInfo;
        // 进行目标检测
        detector->detect(bgr, &resultInfo, scoreThresh, iouThresh);
        // 进行关键点检测
        pose->detect(bgr, &resultInfo, poseThresh);
        // 可视化代码
        pose->visualizeResult(bgr, resultInfo, pose_model_param.skeleton, false, 0);
    }

    delete detector;
    detector = nullptr;
    delete pose;
    pose = nullptr;
    printf("FINISHED.\n");
}


/***
 * 测试视频文件
 * @return
 */
int test_video_file() {
    //测试视频文件
    string video_file = "../data/video/video-test.mp4";
    cv::VideoCapture cap;
    bool ret = get_video_capture(video_file, cap);
    cv::Mat frame;
    while (ret) {
        cap >> frame;
        if (frame.empty()) break;
        FrameInfo resultInfo;
        // 进行目标检测
        detector->detect(frame, &resultInfo, scoreThresh, iouThresh);
        // 进行关键点检测
        pose->detect(frame, &resultInfo, poseThresh);
        // 可视化代码
        pose->visualizeResult(frame, resultInfo, pose_model_param.skeleton, false, 5);
    }
    cap.release();

    delete detector;
    detector = nullptr;
    delete pose;
    pose = nullptr;
    printf("FINISHED.\n");
    return 0;

}


/***
 * 测试摄像头
 * @return
 */
int test_camera() {
    int camera = 0; //摄像头ID号(请修改成自己摄像头ID号)
    cv::VideoCapture cap;
    bool ret = get_video_capture(camera, cap);
    cv::Mat frame;
    while (ret) {
        cap >> frame;
        if (frame.empty()) break;
        FrameInfo resultInfo;
        // 进行目标检测
        detector->detect(frame, &resultInfo, scoreThresh, iouThresh);
        // 进行关键点检测
        pose->detect(frame, &resultInfo, poseThresh);
        // 可视化代码
        pose->visualizeResult(frame, resultInfo, pose_model_param.skeleton, false, 5);
    }
    cap.release();
    delete detector;
    detector = nullptr;
    delete pose;
    pose = nullptr;
    printf("FINISHED.\n");
    return 0;

}

/***
 * 测试跟踪效果
 * @return
 */
int test_pose_track() {
    //测试视频文件
    string video_file = "../data/video/video-test.mp4";
    cv::VideoCapture cap;
    bool ret = get_video_capture(video_file, cap);
    cv::Mat frame;
    // 指定需要跟踪(滤波)的关键点,目前仅仅支持单目标的关键点跟踪,多目标不支持,会出现异常
    vector<int> filter_id = {0};
    // 初始化跟踪
    pose->initTrack(filter_id, 20, 0.35);
    while (ret) {
        cap >> frame;
        if (frame.empty()) break;
        FrameInfo resultInfo;
        // 进行目标检测
        detector->detect(frame, &resultInfo, scoreThresh, iouThresh);
        // 进行关键点检测和跟踪
        pose->track(frame, &resultInfo, poseThresh);
        // 可视化代码
        pose->visualizeResult(frame, resultInfo, pose_model_param.skeleton, false, 5);
    }
    cap.release();

    delete detector;
    detector = nullptr;
    delete pose;
    pose = nullptr;
    printf("FINISHED.\n");
    return 0;

}


int main() {
    test_image_file();   // 测试图片文件
    test_video_file();   // 测试视频文件
    test_camera();       //测试摄像头
    return 0;
}

(6)源码编译和运行

编译脚本,或者直接:bash build.sh

#!/usr/bin/env bash
if [ ! -d "build/" ];then
  mkdir "build"
else
  echo "exist build"
fi
cd build
cmake ..
make -j4
sleep 1
./Detector

  • 如果你要测试CPU运行的性能,请修改src/main.cpp

DeviceType device = CPU;

  • 如果你要测试GPU运行的性能,请修改src/main.cpp (需配置好OpenCL) 

DeviceType device = GPU;

PS:纯CPU C++推理模式比较耗时,需要几秒的时间,而开启OpenCL加速后,GPU模式耗时仅需十几毫秒,性能极大的提高。

(7)Demo测试效果 

 C++版本与Python版本的结果几乎是一致,下面是手部关键点检测效果展示:

 


5.项目源码下载

C/C++实现手部关键点检测项目源码下载地址:C++实现手部关键点检测(手部姿势估计)源码下载 

整套项目源码内容包含:

  1. C/C++源码支持YOLOv5手部检测
  2. C/C++源码提供高精度版本HRNet手部关键点检测
  3. C/C++源码提供轻量化模型LiteHRNet和Mobilenet-v2手部关键点检测
  4. C/C++源码支持CPU和GPU,开启GPU(OpenCL)可以实时检测和识别(纯CPU推理速度很慢,模型加速需要配置好OpenCL,GPU推理约15ms左右)
  5. C/C++源码Demo支持图片,视频,摄像头测试
  6. 项目配置好了base-utils和TNN,而OpenCV和OpenCL需要自行编译安装

 Android手部关键点检测APP Demo体验:

https://download.csdn.net/download/guyuealian/88418582

如果你需要手部关键点检测的训练代码,请参考:手部关键点检测3:Pytorch实现手部关键点检测(手部姿势估计)含训练代码和数据集

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

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

相关文章

mac vscode 使用 clangd

C 的智能提示 IntelliSense 非常不准&#xff0c;我们可以使用 clangd clangd 缺点就是配置繁琐&#xff0c;优点就是跳转和提示代码精准 开启 clangd 之后会提示你关闭 IntelliSense 1、安装插件 clangd 搜索第一个下载多的就是 2、配置 clangd 可执行程序路径 clangd 插…

【虹科干货】谈谈Redis Enterprise的实时搜索

我们都知道&#xff0c;用户在使用应用程序时候&#xff0c;对于速度有着越来越高的要求&#xff0c;真可谓是“一秒也等不及”。而开发团队又该怎样来满足这种对于实时性的期望呢&#xff1f; 文章速览&#xff1a; Redis Enterprise实时搜索的应用场景利用索引为开发人员带…

特殊类设计[下] --- 单例模式

文章目录 5.只能创建一个对象的类5.1设计模式[2.5 万字详解&#xff1a;23 种设计模式](https://zhuanlan.zhihu.com/p/433152245)5.2单例模式1.饿汉模式1.懒汉模式 6.饿汉模式7.懒汉模式7.1饿汉模式优缺点:7.2懒汉模式1.线程安全问题2.单例对象的析构问题 8.整体代码9.C11后可…

C++项目——云备份-⑤-数据管理模块的设计与实现

文章目录 专栏导读1.要管理的数据有哪些2.如何管理数据3.数据信息结构体设计与实现4.数据管理类设计5.数据管理类实现6.数据管理模块整理 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师…

打破尺寸记录!荷兰QuTech研发16量子点阵列新技术

承载16个量子点交叉条阵列的量子芯片&#xff0c;可无缝集成到棋盘图案&#xff08;图片来源&#xff1a;网络&#xff09; 由荷兰代尔夫特理工大学(TU Delft)和荷兰应用科学研究组织(TNO)组建的荷兰量子计算研究中心QuTech的研究人员开发了一种用相对较少的控制线来控制大量量…

【QT】Qt控件不显示图标

问题描述 本人在跟着B站视频学习QT时&#xff0c;遇到了一件十分悲惨的事情&#xff0c;一模一样的步骤&#xff0c;我的图标却不能显示。 于是我上网查询一下解决方案&#xff0c;第一种&#xff0c;亲测没用&#xff1b;第二种亲测可以。 解决方法一 1、构建 -> 清理项目…

实战CubeMX配置CAN通讯教程,避免踩坑,cubeMX 回环模式可以但正常模式无法通信

文章目录 实战CubeMX配置CAN通讯教程&#xff0c;避免踩坑&#xff0c;cubeMX 回环模式可以但正常模式无法通信1. 先配置两个LED等的普通IO口&#xff0c;作为通信指示信号2.配置时钟单元3.配置工程文件4.配置代码生成的参数5.配置CAN通信的波特率&#xff0c;注意如果配置成50…

成都瀚网科技有限公司:抖音小店收益计算大揭秘,一招提升你的利润!

你是否曾对抖音小店的收益计算方式感到困惑&#xff1f;想要了解如何提高抖音小店的收益吗&#xff1f;本文将为你揭开抖音小店收益计算的神秘面纱&#xff0c;并分享一些实用的提升利润的方法。 一、抖音小店收益计算方式 抖音小店的收益主要来自于商品销售收入、佣金收入以及…

0146 网络层

目录 4 网络层 4.1 网络层的功能 4.2 路由算法与路由协议 4.3 IPv4 4.4 IPv6 4 网络层 4.1 网络层的功能 4.2 路由算法与路由协议 4.3 IPv4 4.4 IPv6 部分习题 1.网络层的主要目的是&#xff08;&#xff09; A.在邻接结点间进行数据报传输 B.在邻接结点间进行数…

郑州职工注意!郑州市职工数字人才技能竞赛正式启动

10月26日&#xff0c;由郑州市劳动竞赛委员会办公室、郑州市总工会、郑州市大数据管理局、郑州市人力资源和社会保障局、郑州市科学技术局主办&#xff0c;郑东新区总工会、中科大数据研究院联合承办的郑州市职工数字人才技能竞赛在郑东新区顺利举行启动仪式。 河南省总工会副主…

『进阶之路』- 揭开ThreadLocal神秘面纱

阅读本文主要可以解决以下困惑&#xff1a; 什么是ThreadLocal&#xff0c;隔离线程的本地变量ThreadLocal的数据结构是怎么样的&#xff0c;为什么能实现线程隔离ThreadLocal的get和set方法ThreadLocal如何实现的线程安全&#xff1f;结合同步锁机制&#xff0c;空间换取时间…

2023年腾讯云双11服务器活动及价格表

双十一购物狂欢节即将到来&#xff0c;腾讯云作为国内领先的云计算服务提供商&#xff0c;推出了一系列优惠活动&#xff0c;下面给大家详细介绍腾讯云双11服务器活动及价格表。 一、腾讯云双11活动入口 活动入口&#xff1a;txy.ink/1111/ 二、腾讯云双11活动时间 即日起至…

智慧巡查平台(Ionic/Vite/Vue3 移动端) 问题记录

目录 1.环境搭建 1.1 安装 node 16 版本 1.2 安装 ionic7 1.3 创建 vue 项目 2.index.html 3.main.ts 3.1 如何默认使用 ios 样式&#xff1f; 3.2 如何使用 ElmentPlus 国际化&#xff1f; 4.router/xxx 5.打包二三事 5.1 添加打包相关文件 5.1.1 .env.developmen…

3、电路综合原理与实践---单双端口理想微带线(伪)手算S参数与时域波形

电路综合原理与实践—单双端口理想微带线&#xff08;伪&#xff09;手算S参数与时域波形与时域波形 1、单理想微带线&#xff08;UE&#xff09;的S参数理论推导 参考&#xff1a;Design of Ultra Wideband Power Transfer Networks的第四章&#xff0c;之后总结推导过程 自…

Kubernetes中如何使用CNI?

一、CNI 是什么 它的全称是 Container Network Interface&#xff0c;即容器网络的 API 接口。 它是 K8S 中标准的一个调用网络实现的接口。Kubelet 通过这个标准的 API 来调用不同的网络插件以实现不同的网络配置方式。实现了这个接口的就是 CNI 插件&#xff0c;它实现了一…

长连接的原理

Apollo的长连接实现是 Spring的DeferredResult来实现的,先看怎么用 import ...RestController RequestMapping("deferredResult") public class DeferredResultController {private Map<String, Consumer<DeferredResultResponse>> taskMap new HashMa…

如何恢复u盘删除文件?2023最新分享四种方法恢复文件

U盘上删除的文件怎么恢复&#xff1f;使用U盘存储文件是非常方便的&#xff0c;例如&#xff1a;在办公的时候&#xff0c;会使用U盘来存储网络上查找到的资料、产品说明等。在学习的时候&#xff0c;会使用U盘来存储教育机构分享的教学视频、重点知识等。而随着U盘存储文件的概…

[数据结构】二叉树

1.概念 一棵二叉树是结点的一个有限集合&#xff0c;该集合&#xff1a; 1. 或者为空 2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成 从上图我们可以发现&#xff1a; 1.二叉树不存在大于2 的度 2.二叉树的子树有左右之分&#xff0c;次序不能颠倒。是有…

关于内存泄漏的经典面试题

目录 前言 一、内存泄漏基本概念 二、如何判断并查找内存泄漏 1、方案设计 2、方案实现 前言 对于C/C程序员来说&#xff0c;或多或少都会被面试官问到关于内存泄漏的问题&#xff0c;内存泄漏是程序的bug&#xff0c;他会一点一点的侵蚀你的内存&#xff0c;导致程序运行…

jmeter报Java.NET.BindException: Address already in use: connect

1、windows10和window11上&#xff1a; 修改注册表的内容&#xff1a; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters&#xff1a; 新建dword&#xff08;值&#xff09;的类型&#xff1a; MaxUserPort 65334 TcpTimedWaitDelay 30window