当前位置: 首页 > news >正文

OpenCV 入门(五) —— 人脸识别模型训练与 Windows 下的人脸识别

OpenCV 入门系列:

OpenCV 入门(一)—— OpenCV 基础
OpenCV 入门(二)—— 车牌定位
OpenCV 入门(三)—— 车牌筛选
OpenCV 入门(四)—— 车牌号识别
OpenCV 入门(五)—— 人脸识别模型训练与 Windows 下的人脸识别
OpenCV 入门(六)—— Android 下的人脸识别
OpenCV 入门(七)—— 身份证识别

本文主要内容:

  1. 如何训练 OpenCV 的人脸识别模型
  2. 如何在 Windows 下利用 OpenCV 进行人脸识别

1、概述

人脸识别需要人脸模型(特征集合)的支持,人脸定位的速度与准确度取决于模型。

OpenCV 提供了已经训练好的模型,无论是 Windows 版本还是 Android 版本的 SDK,都在 etc 目录下提供了两种级联分类器模型:

2024-3-31.OpenCV两种级联分类器模型

“Haarcascades” 和 “Lbpcascades” 都是级联分类器模型,用于目标检测和识别,特别是在人脸检测领域中常被使用:

  • Haarcascades 基于 Haar-like 特征(哈尔特征)的级联分类器。Haar-like 特征是一种基于像素差值的特征描述方法,通过计算图像中不同区域的像素值之和的差异,来捕捉图像中的纹理和形状信息。Haarcascades 模型使用了这些特征来构建级联分类器,以在图像中快速检测人脸或其他目标
  • Lbpcascades 使用的是局部二值模式(Local Binary Patterns,LBP)特征的级联分类器。LBP 特征是一种描述图像纹理的方法,通过比较像素点与其邻域像素的灰度值,将其转化为二进制编码。Lbpcascades 模型利用这些二进制编码来构建级联分类器,用于目标检测和识别,例如人脸检测

两个文件夹内都提供了多种模型用以识别物体,比如 lbpcascades 文件夹下的 lbpcascade_frontalface.xml 可以用于识别人脸,lbpcascade_frontalcatface.xml 用于识别猫脸:

2024-3-28.OpenCV提供的人脸特征集合文件

Windows 版本的 SDK 提供了文档与演示代码,在 opencv\sources\doc\tutorials 目录下,比如 objdetect 就是介绍物体识别的。比如 cascade_classifier.markdown 介绍级联分类器,traincascade.markdown 介绍如何训练模型。当然这些文档都是英文的,如果想查看中文文档,可以去 OpenCV 的中国 Wiki 论坛提供的中文文档,比如级联分类器训练。此外演示代码在 opencv\sources\samples\ 目录下,Android 相关的演示在 android 目录中。

除了使用 OpenCV 提供的级联分类器模型,我们也可以使用 OpenCV 提供的工具自己训练模型,具体的操作方法会在下一节介绍。

编码时 Windows 和 Android SDK 使用的都是 4.1.0 版本。

2、人脸模型训练

OpenCV 识别事务实际上就是对事物进行分类。给 OpenCV 各种样本去学习,使得 OpenCV 学习区分不同类别的事物。以人脸为例,给 OpenCV 的正样本全都是各种人脸,负样本全是与人脸无关的事物,那么 OpenCV 就能识别出什么人脸,什么不是。因此供 OpenCV 学习的样本越多越好。

OpenCV 提供的模型可以识别所有人脸,假如现在有个需求,就是只识别某一个人脸,其他的不识别,该如何实现呢?

这就需要自己训练模型了。假如要求只识别你的人脸,那么在采集图像时,保证摄像头内只有你的脸,通过 OpenCV 识别到人脸,然后将人脸部分转为 24 * 24 的灰度图保存到指定的目录内。用目录内的这些小图片通过 OpenCV 提供的工具进行训练,就可以训练出只识别你的脸的模型。

说到 OpenCV 的训练工具,这里要特别说明一下。我们写 Demo 用的是 OpenCV 的 4.1.0 版本,但是在该版本中,训练工具被移除了。在 3.4.X 版本中,这个工具是存在的(如 3.4.6 或 3.4.16 等版本都行):

2024-4-24.OpenCV训练工具

因此我们需要再下载一个包含训练工具的 OpenCV 版本,然后在 opencv\build\x64\vc15\bin 目录下找到 opencv_createsamples.exe 和 opencv_traincascade.exe 工具,可以将目录添加到环境变量中。

稍微提一下为什么 OpenCV 4.X 版本中移除了训练工具。早期的 OpenCV 提供了 C 和 C++ 两种风格的 API。比如 C 风格的 CvMat、CvVideoCapture,而 C++ 风格 API 对应的就是 Mat、VideoCapture。

从 4.X 版本开始,OpenCV 移除了 C 风格的 API,而训练工具是用 C 写的,因此在源码 /opencv410/sources/app 目录下的 CMakeLists.txt 文件中,你能看到生成两个训练工具的代码被注释掉了:

2024-4-24.OpenCV4.X版本没有训练工具的原因

即便打开注释也无法编译出这两个训练工具,因为 C 的源码已经被移除了,所以才需要再下载一个带有训练工具的 3.4.X 版本,这也是 OpenCV 官方给出的解决方案。

2.1 训练步骤

训练模型需要正样本和负样本:

  • 正样本就是与目标模型相关性强的。比如训练只识别你的人脸的模型,那么前面收集的人脸灰度图就是正样本
  • 负样本就是与目标模型相关性弱的,甚至没有相关性的。比如训练人脸识别模型,那么负样本就可以是风景图等等

我们将正样本存入 pos 目录,将负样本存入 neg 目录,然后再创建正样本和负样本的描述文件 positive.txt 和 neg.txt:

2024-3-30.OpenCV训练库文件路径

样本描述文件格式为文件名、人脸数量、每个人脸的起始坐标与宽高范围:

2 个人脸分别为 (100,200) 处为左上角,宽高为 50x50 的范围和 (50,30) 处为左上角,宽高为 25x25 的范围
pos/1.jpg 2 100 200 50 50   50 30 25 25

按照上述格式,我们的正样本可以写为:

pos/0.jpg 1 0 0 24 24
pos/1.jpg 1 0 0 24 24
pos/2.jpg 1 0 0 24 24
pos/3.jpg 1 0 0 24 24
pos/4.jpg 1 0 0 24 24
pos/5.jpg 1 0 0 24 24
pos/6.jpg 1 0 0 24 24
中间省略...
pos/61.jpg 1 0 0 24 24

负样本也是类似的操作。当然需要注意正负样本的比例最好是 1:3,比如正样本有 100 个,负样本最好就是 300 个。

接下来就使用 OpenCV 提供的工具训练模型:

  • 首先运行 opencv_createsamples 命令创建正样本的向量文件:

    # -info: 正样本描述文件
    # -vec : 输出的正样本向量
    # -num : 正样本数量
    # -w -h: 输出样本的大小
    C:\Users\Desktop\train>opencv_createsamples -info positive.txt -vec pos.vec -num 61 -w 24 -h 24
    

    如果运行成功则会如上图所示在当前目录下生成 pos.vec 文件,log 会输出:

    Create training samples from images collection...
    Done. Created 61 samples
    

    如果因为文件路径不匹配,则运行会报错:

    Create training samples from images collection...
    Unable to open image: pos/pos/1.jpg
    OpenCV: terminate handler is called! The last OpenCV error is:
    OpenCV(3.4.16) Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in cv::Mat::Mat, file C:\build\3_4_winpack-build-win64-vc15\opencv\modules\core\src\matrix.cpp, line 751
    
  • 然后运行 opencv_traincascade 进行训练:

    # -data : 需要手动创建,训练的模型作为结果会输出到这个目录
    # -vec  : 正样本	
    # -bg	  : 负样本
    # -numPos :每级分类器训练时所用到的正样本数目
    # -numNeg :每级分类器训练时所用到的负样本数目,可以大于 -bg 数目
    # -numStages:训练分类器的级数,如果层数多,分类器的误差就更小,但是检测速度慢。(15-20)
    # -featureType: 采用 LBP 算法
    # -w -h:负样本的宽高可以设置的随意些,只要起始点 + 宽高不超过图片像素范围即可
    C:\Users\Desktop\train>opencv_traincascade -data data -vec pos.vec -bg neg.txt -numPos 61 -numNeg 300 -numStages 15 -featureType LBP -w 24 -h 24
    

    如果你没有手动创建 data 目录,运行上述命令会报错说无法打开 data/params.xml 文件:

    ===== TRAINING 0-stage =====
    <BEGIN
    POS count : consumed   61 : 61
    NEG count : acceptanceRatio    300 : 1
    Precalculation time: 0.026
    +----+---------+---------+
    |  N |    HR   |    FA   |
    +----+---------+---------+
    |   1|        1|        0|
    +----+---------+---------+
    END>
    Parameters can not be written, because file data/params.xml can not be opened.
    

    创建 data 后再次运行可能会有如下结果之一:

    # 1.训练成功
    Training until now has taken 0 days 0 hours 0 minutes 10 seconds.# 2.可以认为该训练阶段是成功的,达到了所需的叶子误报率,并且分支训练已经终止(样本太少,模型质量不行)
    Required leaf false alarm rate achieved. Branch training terminated.# 3.错误
    Bad argument < Can not get new positive sample. The most possible reason is insufficient count of samples in given vec-file.
    

训练成功后会在 data 目录下得到如下文件:

2024-3-30.OpenCV训练库结果

cascade.xml 就是我们训练出的库文件,将其拷贝到手机中,修改代码,用 cascade.xml 替代 OpenCV 提供的 lbpcascade_frontalface.xml:

		// 初始化 OpenCVval path = File(Environment.getExternalStorageDirectory(),/*"lbpcascade_frontalface.xml"*/"cascade.xml").absolutePathmOpenCVJNI.init(path)

使用 cascade.xml 模型去做人脸识别时,就只会识别训练样本中的人脸,而不会像 OpenCV 提供的 lbpcascade_frontalface.xml 识别所有人脸,这种识别特定人脸的需求与我们上班打卡的机器原理是类似的。

这里简单说下训练样本数量的选取标准。

minHitRate 是分类器的每一级希望得到的最小检测率。当设置为 0.995 时如果正训练样本个数为 100 个,那么其中的 0.5 个就很可能不被检测,第二次选择的时候必须多选择后面的 5 个,按照这种规律我们为后面的每级多增加 numPos*minHitRate 个正样本。

实际准备的正样本数量应该(读入 vec 的正样本数) >= numPos + (numStage - 1) * numPos * (1 - minHitRate)

按照此公式计算: x+14*x*0.005 = 1.07*x,也就是正样本数量要大于等于 1.07*x 而我们正样本是 100,所以 x = 93.45,但是此处传 100 也可以。

因为实际的检测率会比 minHitRate 高,所以在设置 numPos 时可以将其设置的稍微再大些,最终的目的是要尽量让所有的正样本都参与到训练中。但是,过大就会出错。

2.2 算法简介

LBP(Local Binary Patterns,局部二值模式)是一种用于纹理分析和模式识别的图像特征描述算法。它于 1994 年由 Ojala 等人提出,并被广泛应用于人脸识别、纹理分类、物体检测和图像检索等领域。

LBP 算法的基本思想是对图像中的每个像素点,根据其周围像素的灰度值进行编码,形成一个局部的二值模式。该编码方法具有旋转不变性和灰度不变性的特点,使得 LBP 特征适用于处理灰度图像。

LBP 算法的步骤如下:

  1. 对于图像中的每个像素点,选择一个固定大小的邻域窗口(通常为 3 × 3 或 5 × 5 的正方形)。

  2. 将邻域窗口中心像素的灰度值与邻域窗口中的其他像素逐一比较,若中心像素的灰度值大于或等于相邻像素的灰度值,则该像素点的位置被标记为 1,否则标记为 0。

  3. 将邻域窗口中的 8 个二值编码按顺时针或逆时针顺序排列,形成一个 8 位二进制数,即得到该像素点的 LBP 编码。

  4. 遍历图像中的所有像素点,重复步骤 2 和步骤 3,得到整幅图像的 LBP 编码图像。

  5. 统计 LBP 编码图像中不同 LBP 模式的出现频率,作为图像的 LBP 特征向量。

LBP 算法的主要优点是计算简单、特征表达能力强、对光照变化具有一定的不变性。然而,它也有一些不足之处,例如对噪声和旋转变化敏感。

我们要清楚,人工智能都是基于大数据的。用上万张图片训练出模型用于物体识别。

3、Windows 人脸识别

这一节现在 Windows 上实现人脸识别,因为 Windows 上查看中间结果(灰度图、直方图等等)比较方便。我们在 Visual Studio 中新建项目,驱动电脑的摄像头进行人脸识别。

3.1 代码实现

实现过程大致可分为三步:

  1. 加载 OpenCV 提供的级联分类器以具备人脸识别能力
  2. 打开摄像头
  3. 对摄像头采集到的数据进行灰度化、均衡化处理后进行人脸识别,在识别出人脸的位置画一个矩形

代码如下:

void detect() {// 1.加载级联分类器if (!face_CascadeClassifier.load("G:/Tools/OpenCV/build/etc/haarcascades/haarcascade_frontalface_alt.xml")) {cout << "级联分类器加载失败!" << endl;}// 2.开启摄像头进行录制VideoCapture capture;capture.open(0);if (!capture.isOpened()){cout << "OpenCV 打开摄像头失败!\n" << endl;return;}// 3.处理采集到的图像Mat frame; // 摄像头彩色图像Mat gray; // 摄像头灰度图像while (true){// 采集到的图像存入 framecapture >> frame;if (frame.empty()) {cout << "OpenCV 读取摄像头图像失败!" << endl;return;}// 灰度化处理,注意 OpenCV 颜色排序为 BGRcvtColor(frame, gray, COLOR_BGR2GRAY);// 直方图均衡化,增强对比度equalizeHist(gray, gray);// 一张图片可能包含多张人脸,因此保存结果的是一个集合vector<Rect> faces;// 对灰度图进行人脸识别,识别结果保存在 faces 集合中face_CascadeClassifier.detectMultiScale(gray, faces);for each (Rect face in faces){// 在 frame 这张图片的 face 上画一个 BGR 颜色为 (0, 0, 255) 即红色的矩形rectangle(frame, face, Scalar(0, 0, 255));// 这种方式来检测相机实时人脸图像非常卡顿!只适合静态图像的检测}// 显示图像imshow("摄像头", frame);// wait 30ms,如果按 Esc 键就退出if (waitKey(30) == 27){break;}}
}

人脸识别不需要图片的颜色,正相反,图片的颜色对于识别是一个干扰项,因此在识别之前通常要进行降噪处理,将图片处理成灰度图。

运行起来会发现虽然确实可以识别出人脸,在人脸位置画一个红色矩形,但是图像非常卡顿。这是因为不论是 OpenCV 还是 TensorFlow,检测人脸都是很耗时的,检测一次大概需要 1 ~ 2 秒的时间。因此我们不能向上面这样,对每一帧视频图片都进行检测,而是先检测到人脸,后续采用人脸跟踪。

下面对上述代码进行改造。

3.2 代码优化

上面提到,优化视频画面卡顿的方法是检测第一帧,检测到后,对后续的帧进行人脸跟踪。那么在 OpenCV 中,人脸检测的任务交给主检测适配器,人脸跟踪的任务交给跟踪检测适配器。这两种适配器必须是 DetectionBasedTracker::IDetector 的子类。我们直接使用 OpenCV 提供的代码示例 opencv\sources\samples\android\face-detection\jni\DetectionBasedTracker_jni.cpp 中定义的 CascadeDetectorAdapter 写入 OpenCV.h:

# pragma once#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;CascadeClassifier face_CascadeClassifier;
cv::Ptr<DetectionBasedTracker> tracker;class CascadeDetectorAdapter : public DetectionBasedTracker::IDetector
{
public:CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector) :IDetector(),Detector(detector){CV_Assert(detector);}// 每张 Image 图片中都可能会有多张人脸 objects,因此可能会多次调用 detect 进行识别void detect(const cv::Mat& Image, std::vector<cv::Rect>& objects){Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours, 0, minObjSize, maxObjSize);}virtual ~CascadeDetectorAdapter(){}private:CascadeDetectorAdapter();cv::Ptr<cv::CascadeClassifier> Detector;
};

接下来我们通过宏定义的方式在原始代码上进行优化,加入使用跟随策略进行人脸检测的代码:

// 定义此宏则收集人脸图片数据保存到指定位置
#define COLLECT_SAMPLES
// 收集的人脸图片编号
int i = 0;// 一次识别,后续跟踪来解决只通过检测的方式的卡顿问题
void track() {
// 如果定义了 DETECT 宏,则每一帧都进行检测,否则只检测一帧,后续跟随,这里我们没有定义这个宏
#ifdef DETECT// 1.加载级联分类器,注意路径一定是斜杠而不是反斜杠,否则会加载失败if (!face_CascadeClassifier.load("G:/Tools/OpenCV/build/etc/haarcascades/haarcascade_frontalface_alt.xml")) {cout << "级联分类器加载失败!" << endl;return;}
#else// 2.创建跟踪器并运行// 2.1 创建主检测适配器cv::Ptr<CascadeDetectorAdapter> mainDetector = makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>("G:/Tools/OpenCV/build/etc/haarcascades/haarcascade_frontalface_alt.xml"));// 2.2 创建跟踪检测适配器cv::Ptr<CascadeDetectorAdapter> trackingDetector = makePtr<CascadeDetectorAdapter>(makePtr<CascadeClassifier>("G:/Tools/OpenCV/build/etc/haarcascades/haarcascade_frontalface_alt.xml"));// 2.3 创建跟踪器DetectionBasedTracker::Parameters DetectorParams;tracker = makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);// 2.4 开始检测tracker->run();
#endif// 3.开启摄像头进行录制VideoCapture capture;capture.open(0);if (!capture.isOpened()){cout << "OpenCV 打开摄像头失败!\n" << endl;return ;}// 4.处理采集到的图像Mat frame; // 摄像头彩色图像Mat gray; // 摄像头灰度图像while (true){// 采集到的图像存入 framecapture >> frame;if (frame.empty()) {cout << "OpenCV 读取摄像头图像失败!\n" << endl;return ;}// 灰度化处理,注意 OpenCV 颜色排序为 BGRcvtColor(frame, gray, COLOR_BGR2GRAY);// 直方图均衡化,增强对比度equalizeHist(gray, gray);// 一张图片可能包含多张人脸,因此要保存在 faces 集合中vector<Rect> faces;
// 如果每帧都识别,则通过 detectMultiScale,否则用 tracker 进行识别
#ifdef DETECTface_CascadeClassifier.detectMultiScale(gray, faces);
#elsetracker->process(gray);tracker->getObjects(faces);
#endif // DETECTfor each (Rect face in faces){// 在 frame 这张图片的 face 上画一个 BGR 颜色为 (0, 0, 255) 即红色的矩形rectangle(frame, face, Scalar(0, 0, 255));// 这种方式来检测相机实时人脸图像非常卡顿!只适合静态图像的检测#ifdef COLLECT_SAMPLES// 采集人脸样本,转换为 24 * 24 的灰度图保存到指定路径的文件中Mat sample;frame(face).copyTo(sample);resize(sample, sample, Size(24, 24));cvtColor(sample, sample, COLOR_BGR2GRAY);char p[100];// 目录需要手动创建,否则不会自动生成sprintf(p, "D:/opencv/train/face/pos/%d.jpg", i++);//imread 读取文件图像imwrite(p, sample);//将Mat写入文件	
#endif // COLLECT_SAMPLES}// 显示图像imshow("摄像头", frame);// Esc 键退出if (waitKey(30) == 27){break;}}
#ifndef DETECTtracker->stop();
#endif // !DETECT
}

简要说明:

  • 在第 2 步创建跟踪器时,使用了 OpenCV 的智能指针 Ptr 模板类,它采用引用计数型的句柄类实现计数。自动管理对象的释放,Ptr 中调用 release() 会将引用计数器减 1,如果计数器为 0 则会删除该对象。使用 Ptr 声明的对象可以不用手动释放
  • 创建的 mainDetector 负责检测,trackingDetector 负责跟随,调用 tracker->run() 会开启一个线程,其内部有一个无限循环,当 tracker->process() 传入灰度图开始检测后,检测到的人脸数据可以通过 tracker->getObjects(faces) 获取,faces 是一个 vector<Rect> 类型的入参出参数据,保存着一张图片中的所有人脸
  • 我们定义了 COLLECT_SAMPLES 宏用来收集人脸数据,将采集到的人脸图像转成灰度图再把尺寸设置为 24 * 24 保存在指定目录中,这些图片可以帮助我们使用 2.1 节中介绍的方法训练自己的模型

应用以上代码后就可以流畅的识别出人脸了。

相关文章:

OpenCV 入门(五) —— 人脸识别模型训练与 Windows 下的人脸识别

OpenCV 入门系列&#xff1a; OpenCV 入门&#xff08;一&#xff09;—— OpenCV 基础 OpenCV 入门&#xff08;二&#xff09;—— 车牌定位 OpenCV 入门&#xff08;三&#xff09;—— 车牌筛选 OpenCV 入门&#xff08;四&#xff09;—— 车牌号识别 OpenCV 入门&#xf…...

C++基础-编程练习题2

文章目录 前言一、查找“支撑数”二、数组元素的查找三、爬楼梯四、数字交换五、找高于平均分的人 前言 C基础-编程练习题和答案 一、查找“支撑数” 【试题描述】 在已知一组整数中&#xff0c; 有这样一种数非常怪&#xff0c; 它们不在第一个&#xff0c; 也不在最后一个&…...

Linux下GraspNet复现流程

Linux&#xff0c;Ubuntu中GraspNet复现流程 文章目录 Linux&#xff0c;Ubuntu中GraspNet复现流程1.安装cuda和cudnn2.安装pytorch3.编译graspnetAPIReference &#x1f680;非常重要的环境配置&#x1f680; ubuntu 20.04cuda 11.0.1cudnn v8.9.7python 3.8.19pytorch 1.7.0…...

Linux——MySQL5.7编译安装、RPM安装、yum安装

文章目录 Linux——MySQL5.7编译安装、RPM安装、yum安装一、编译安装二、RPM安装三、yum安装 Linux——MySQL5.7编译安装、RPM安装、yum安装 卸载mysql # 查看是否安装了mysql [rootcsq ~]# rpm -qa |grep mysql mysql-community-server-5.7.36-1.el7.x86_64 mysql-community-c…...

LSTM递归预测(matlab)

LSTM&#xff08;长短期记忆&#xff09;递归预测原理及步骤详解如下&#xff1a; LSTM递归预测&#xff08;matlab&#xff09;代码获取戳此处代码获取戳此处代码获取戳此处 一、LSTM递归预测原理 LSTM是一种特殊的递归神经网络&#xff08;RNN&#xff09;&#xff0c;它能够…...

计算机网络 备查

OSI 七层模型 七层模型协议各层实现的功能 简要 详细 TCP/IP协议 组成 1.传输层协议 TCP 2.网络层协议 IP 协议数据单元&#xff08;PDU&#xff09;和 封装 数据收发过程 数据发送过程 1. 2.终端用户生成数据 3.数据被分段&#xff0c;并加上TCP头 4.网络层添加IP地址信息…...

查看软件包依赖关系

列出软件包依赖文件列表 rpm -ql 命令用于列出已安装软件包的文件列表。它显示软件包中包含的文件及其对应的路径。 具体来说&#xff0c;-q 选项表示查询已安装的软件包&#xff0c;而 -l 选项表示列出软件包中的文件列表。 例如&#xff0c;如果要查看已安装的 nginx 软件…...

C++ 中 strcmp(a,b) 函数的用法

【C 中 strcmp(a,b) 函数的用法】 ● 若 len(a)>len(b)&#xff0c;则返回1。 ● 若 len(a)len(b)&#xff0c;则返回0。 ● 若 len(a)<len(b)&#xff0c;则返回-1。【C 中 strcmp(a,b) 函数的用法代码一】 #include <bits/stdc.h> using namespace std;int main…...

Servlet(一些实战小示例)

文章目录 一、实操注意点1.1 代码修改重启问题1.2 Smart Tomcat的日志1.3 如何处理错误 一. 抓自己的包二、构造一个重定向的响应&#xff0c;让页面重定向到百度主页三、让服务器返回一个html数据四、表白墙4.1 约定前后端数据4.2 前端代码4.3 后端代码4.4 保存在数据库的版本…...

【JVM】垃圾回收机制(Garbage Collection)

目录 一、什么是垃圾回收&#xff1f; 二、为什么要有垃圾回收机制&#xff08;GC&#xff09;&#xff1f; 三、垃圾回收主要回收的内存区域 四、死亡对象的判断算法 a&#xff09;引用计数算法 b&#xff09;可达性分析算法 五、垃圾回收算法 a&#xff09;标记-清除…...

C++中的priority_queue模拟实现

目录 priority_queue模拟实现 priority_queue类定义 priority_queue构造函数 priority_queue类push()函数 priority_queue类pop()函数 priority_queue类size()函数 priority_queue类empty()函数 priority_queue类top()函数 仿函数与priority_queue类模拟实现 仿函数 …...

【Kafka】1.Kafka核心概念、应用场景、常见问题及异常

Kafka 是一个分布式流处理平台&#xff0c;最初由 LinkedIn 开发&#xff0c;后成为 Apache 软件基金会的顶级项目。 它主要用于构建实时数据管道和流式应用程序。它能够高效地处理高吞吐量的数据&#xff0c;并支持消息发布和订阅模型。Kafka 的主要用途包括实时分析、事件源、…...

LTE的EARFCN和band之间的对应关系

一、通过EARFCN查询对应band 工作中经常遇到只知道EARFCN而需要计算band的情况&#xff0c;因此查了相关协议&#xff0c;找到了他们之间的对应关系&#xff0c;可以直接查表&#xff0c;非常方便。 具体见&#xff1a; 3GPP TS 36.101 5.7.3 Carrier frequency and EAR…...

解决问题:Docker证书到期(Error grabbing logs: rpc error: code = Unknown)导致无法查看日志

问题描述 Docker查看日志时portainer报错信息如下&#xff1a; Error grabbing logs: rpc error: code Unknown desc warning: incomplete log stream. some logs could not be retrieved for the following reasons: node klf9fdsjjt5tb0w4hxgr4s231 is not available报错…...

【C语言】预处理器

我们在开始编写一份程序的时候&#xff0c;从键盘录入的第一行代码&#xff1a; #include <stdio.h>这里就使用了预处理&#xff0c;引入头文件。 C预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C预处理器只不过是一…...

QtConcurrent::run操作界面ui的注意事项(2)

前面的“QtConcurrent::run操作界面ui的注意事项&#xff08;1&#xff09;”&#xff0c;末尾说了跨线程的问题&#xff0c;引出了Qt千好万好&#xff0c;就是跨线程不好。下面是认为的最简单的解决办法&#xff1a;使用QMetaObject::invokeMethod&#xff08;相比较信号-槽&a…...

黑马程序员HarmonyOS4+NEXT星河版入门到企业级实战教程笔记

HarmonyOS NEXT是纯血鸿蒙&#xff0c;鸿蒙原生应用&#xff0c;彻底摆脱安卓 本课程是基于harmony os4的&#xff0c;与next仅部分api有区别 套件 语言&框架 harmony os design ArkTs 语言 ArkUI 提供各种组件 ArkCompiler 方舟编译器 开发&测试 DevEco Studio 开发…...

嵌入式全栈开发学习笔记---C语言笔试复习大全13(编程题9~16)

目录 9.查找字符数组中字符位置&#xff08;输入hello e 输出2&#xff09;&#xff1b; 10、查找字符数组中字符串的位置&#xff08;输入hello ll 输出3&#xff09;&#xff1b; 11、字符数组中在指定位置插入字符&#xff1b;&#xff08;输入hello 3 a 输出heallo…...

https网站安全证书的作用与免费申请办法

HTTPS网站安全证书&#xff0c;也称为SSL证书&#xff0c;网站通过申请SSL证书将http协议升级到https协议 HTTPS网站安全证书的作用 1 增强用户信任&#xff1a;未使用https协议的网站&#xff0c;用户访问时浏览器会有“不安全”弹窗提示 2 提升SEO排名&#xff1a;搜索引擎…...

自动化测试再升级,大模型与软件测试相结合

近年来&#xff0c;软件行业一直在迅速发展&#xff0c;为了保证软件质量和提高效率&#xff0c;软件测试领域也在不断演进。如今&#xff0c;大模型技术的崛起为软件测试带来了前所未有的智能化浪潮。 软件测试一直是确保软件质量的关键环节&#xff0c;但传统的手动测试方法存…...

centos7 基础命令

一、基础信息&#xff1a; 查看IP地址&#xff1a; ip add 重启网络服务&#xff1a; service network restart 查看网卡配置&#xff1a; cat /etc/sysconfig/network-scripts/ifcfg-ens33 启动网卡: ifup ens33 查看内存: free -m 查看CPU&#xff1a; cat /proc/cpuin…...

【设计模式】之单例模式

系列文章目录 【设计模式】之责任链模式【设计模式】之策略模式【设计模式】之模板方法模式 文章目录 系列文章目录 前言 一、什么是单例模式 二、如何使用单例模式 1.单线程使用 2.多线程使用&#xff08;一&#xff09; 3.多线程使用&#xff08;二&#xff09; 4.多线程使用…...

3d模型实体显示有隐藏黑线?---模大狮模型网

在3D建模和设计领域&#xff0c;细节决定成败。然而&#xff0c;在处理3D模型时&#xff0c;可能会遇到模型实体上出现隐藏黑线的问题。这些黑线可能影响模型的视觉质量和呈现效果。因此&#xff0c;了解并解决这些隐藏黑线的问题至关重要。本文将探讨隐藏黑线出现的原因&#…...

共享购:全新消费模式的探索与实践

在消费模式日益创新的今天&#xff0c;共享购模式以其独特的消费与收益双重机制&#xff0c;吸引了众多消费者的目光。这一模式不仅为消费者带来了全新的购物体验&#xff0c;也为商家和平台带来了可观的收益。 一、会员体系&#xff1a;共享购的基石 在共享购模式下&#xff…...

Java集合 总结篇(全)

Java集合 集合底层框架总结 List 代表的有序&#xff0c;可重复的集合。 ArrayList -- 数组 -- 把他想象成C中的Vector就可以&#xff0c;当数组空间不够的时候&#xff0c;会自动扩容。 -- 线程不安全 LinkedList -- 双向链表 -- 可以将他理解成一个链表&#xff0c;不支持…...

Dubbo分层架构深度解析

引言 Dubbo作为一款备受欢迎的高性能、轻量级的Java RPC框架&#xff0c;在现代分布式系统中扮演着至关重要的角色。随着互联网行业的快速发展&#xff0c;服务间的通信变得越来越频繁&#xff0c;这也使得对于高效、可靠的远程通信方案的需求变得愈发迫切。在这样的背景下&am…...

LocalDate 数据库不兼容问题,因为LocalDate 是 long 类型的

我今天遇到一报错&#xff1a; SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession316f9272] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection2127597288 wrapping com.mysql.cj.jdbc…...

RVM(相关向量机)、CNN_RVM(卷积神经网络结合相关向量机)、RVM-Adaboost(相关向量机结合Adaboost)

当我们谈到RVM&#xff08;Relevance Vector Machine&#xff0c;相关向量机&#xff09;、CNN_RVM&#xff08;卷积神经网络结合相关向量机&#xff09;以及RVM-Adaboost&#xff08;相关向量机结合AdaBoost算法&#xff09;时&#xff0c;每种模型都有其独特的原理和结构。以…...

Java--方法的使用

1.1什么是方法 方法顾名思义就是解决问题的办法&#xff0c;在程序员写代码的时候&#xff0c;会遇到很多逻辑结构一样&#xff0c;解决相同问题时&#xff0c;每次都写一样的代码&#xff0c;这会使代码看起来比较绒余&#xff0c;代码量也比较多&#xff0c;为了解决这个问题…...

linux - 主次设备号自动申请

alloc_chrdev_region 原型如下&#xff0c;该函数向内核申请一个空闲的主设备号。 alloc_chrdev_region(&g_aputriger_dev, 0, APUTRIGER_MAX_NUM, "aputriger0"); 第四个参数是我们使用cat /proc/devices 看到的名称 /*** alloc_chrdev_region() - register a…...

我写了一套几乎无敌的参数校验组件!基于 SpEL 的参数校验组件「SpEL Validator」

前言 大家好&#xff0c;我是阿杆&#xff0c;不是阿轩。 参数校验这个东西&#xff0c;很多情况下都是比较简单的&#xff0c;用 NotNull、Size 等注解就可以解决绝大多数场景&#xff0c;但也有一些场景是这些基本注解解决不了的&#xff0c;只能用一些其他的方式处理&…...

输入序列太长 gan CGAN

transformer序列长度大导致计算复杂度高 GAN 2. 训练过程 第一阶段&#xff1a;固定「判别器D」&#xff0c;训练「生成器G」。使用一个性能不错的判别器&#xff0c;G不断生成“假数据”&#xff0c;然后给这个D去判断。开始时候&#xff0c;G还很弱&#xff0c;所以很容易被…...

uni-app scroll-view隐藏滚动条的小细节 兼容主流浏览器

开端 想写个横向滚动的列表适配浏览器&#xff0c;主要就是隐藏一下滚动条在手机上美观一点。 但是使用uni-app官方文档建议的::-webkit-scrollbar在目标标签时发现没生效。 .scroll-view_H::-webkit-scrollbar{display: none; }解决 F12看了一下&#xff0c;原来编译到浏览…...

Java常用API之LinkedList类解读

写在开头&#xff1a;本文用于作者学习我将官方文档中LinkedList 1.6版本中类中绝大部分API全测了一遍并打印了结果&#xff0c;日拱一卒&#xff0c;常看常新。 自己补充了一些对该数据结构的理解&#xff0c;如有不对的地方&#xff0c;请各位指正&#xff0c;谢谢。 首先&…...

移动端自适应

基本实现核心思想 基本原则上是&#xff0c;布局更多地使用flex&#xff0c;然后尺寸使用rem&#xff0c;vw&#xff0c;vh为单位如果是根据不同的屏幕需要有不同的布局了&#xff0c;一般通过检测屏幕尺寸换不同的站点或者媒体查询使用css rem 以html字体太小为1rem的大小&…...

自动化运维工具-Ansible

一、Ansible概述 Ansible是一种基于python开发的自动化运维工具&#xff0c;它只需要在服务端安装ansible&#xff0c;无需在每个客户端安装客户端程序&#xff0c;通过ssh的方式来进行客户端服务器的管理&#xff0c;基于模块来实现批量数据配置、批量设备部署以及批量命令执…...

力扣:62. 不同路径

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&…...

store内路由跳转router.push

选择action还是mutation 选择action mutation 是用来改变state的&#xff0c;不应该包含路由相关操作mutation是同步执行的&#xff0c;不应该包含异步操作&#xff0c;而路由是异步操作 action中进行路由跳转 因为vuex中没有this&#xff0c;所以不能用this.$router&#…...

ChatGPT Web Midjourney一键集成最新版

准备工具 服务器一台 推荐使用浪浪云服务器 稳定 安全 有保障 chatgpt api 推荐好用白嫖的api 项目演示 项目部署 浏览器访问casaos 添加软件原添加 https://gitee.com/langlangy_1/CasaOS-AppStore-LangLangy/raw/master/chatmjd.zip 安装此软件 等待安装 安装后再桌面设置…...

springboot mongodb分片集群事务

前置 mongodb分片集群想要使用事务,需要对应分片没有仲裁节点 代码 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId><version>2.1.0.RELEASE</version></d…...

node报错——解决Error: error:0308010C:digital envelope routines::unsupported——亲测可用

今天在打包vue2项目时&#xff0c;遇到一个报错&#xff1a; 最关键的代码如下&#xff1a; Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:80:19)百度后发现是node版本的问题。 在昨天我确实操作了一下node&…...

golang系统内置函数整理

go语言中有很多系统内置的函数&#xff0c; 为了方便学习&#xff0c;对系统内置函数的函数定义 入参和返回值做如下整理&#xff0c;以方便学习和记忆。 Go语言系统级别的内置函数不多&#xff0c;但是包含的知识点可不少&#xff0c;是学习go语言说必须要搞明白的基础知识 …...

武汉星起航:五对一服务体系,助力创业者成功进军跨境电商市场

随着全球化的深入发展和互联网的普及&#xff0c;跨境电商已成为越来越多国内创业者的首选。然而&#xff0c;跨境电商市场的复杂性和多变性使得许多新手创业者望而却步。在这样的背景下&#xff0c;武汉星起航电子商务有限公司以其独特的五对一服务体系&#xff0c;为创业者提…...

C++常用库函数——strcmp、strchr

1、strcmp&#xff1a;比较两个字符串的值是否相等 例如 char a1[6] "AbDeG",*s1 a1;char a2[6] "AbdEg",* s2 a2;s1 2;s2 2;printf("%d \n", strcmp(s1, s2));return(0); s1指向a1&#xff0c;s2指向a2&#xff0c;strcmp表示比较s1和s…...

vue3怎么使用vant的IndexBar 索引栏

Vant 是一个基于 Vue 的移动端 UI 组件库&#xff0c;它提供了许多常见的移动端组件&#xff0c;包括 IndexBar 索引栏。以下是如何在 Vue 3 中使用 Vant 的 IndexBar 索引栏的步骤&#xff1a; 安装 Vant 如果你还没有安装 Vant&#xff0c;你可以使用 npm 或 yarn 来安装它…...

VMware常见问题(技巧)总结

目录 问题虚拟机中windows11如何开启vt 虚拟化?虚拟机Windows 11 中的相机使用失败问题? 待续、更新中 问题 虚拟机中windows11如何开启vt 虚拟化? 编辑设置—打对钩 选对正确镜像( 可翻看以往文章,有提到) 虚拟机Windows 11 中的相机使用失败问题? 1 . 没安装合适的驱动 …...

VS Code 保存+格式化代码

在 VSCode 中&#xff0c;使用 Ctrl S 快捷键直接保存并格式化代码&#xff1a; 打开 VSCode 的设置界面&#xff1a;File -> Preferences -> Settings在设置界面搜索框中输入“format on save”&#xff0c;勾选“Editor: Format On Save”选项&#xff0c;表示在保存…...

word启动缓慢之Baidu Netdisk Word Addin

word启动足足花了7秒钟&#xff0c;你知道我这7秒是怎么过来的吗&#xff1f; 原因就是我们可爱的百度网盘等APP&#xff0c;在我们安装客户端时&#xff0c;默认安装了Office加载项&#xff0c;不仅在菜单栏上加上了一个丑陋的字眼&#xff0c;也拖慢了word启动速度........ 解…...

获取波形极值与间距并显示

获取并显示波形的极值与极值间距 1、流程 1、通过signal.find_peaks获取极大值 2、获取极大值下标 3、获取极大值对应的值 4、获取极大值的下标间距(就是隔多远有一个极大值) 5、获取极大值间距的标准差、方差、均值、最大值 6、图形展示波形图并标记极大值2、效果图 3、示…...

视频素材哪个app好?8个视频素材库免费使用

视频内容已成为现代传播中不可或缺的一部分&#xff0c;具备卓越的视频素材对于提升任何媒体作品的质量和吸引力尤为关键。这里列举的一系列精挑细选的全球视频素材网站&#xff0c;旨在为您的商业广告、社交媒体更新或任何其他类型的视觉项目提供最佳支持。 1. 蛙学府&#x…...