c++基于Resnet(Dlib库)+opencv3的高精度人脸识别

2023-10-29

Resnet

产生原因

介绍残差网络Resnet之前,先介绍一下卷积神经网络过程中会遇到的问题,分别有:

  1. 计算资源的消耗
  2. 模型容易过拟合
  3. 梯度消失/梯度爆炸问题的产生

问题1可以通过GPU集群来解决,对于一个企业资源并不是很大的问题;问题2的过拟合通过采集海量数据,并配合Dropout正则化等方法也可以有效避免;问题3通过Batch Normalization也可以避免。貌似我们只要无脑的增加网络的层数,我们就能从此获益,但实验数据给了我们当头一棒。事实上,随着网络层数的增加,网络发生了退化(degradation)的现象:随着网络层数的增多,训练集loss逐渐下降,然后趋于饱和,当你再增加网络深度的话,训练集loss反而会增大。注意这并不是过拟合,因为在过拟合中训练loss是一直减小的。

从信息论的角度讲,由于DPI(数据处理不等式)的存在,在前向传输的过程中,随着层数的加深,Feature Map包含的图像信息会逐层减少,而ResNet的直接映射的加入,保证了下一层的网络一定比上层包含更多的图像信息。基于这种使用直接映射来连接网络不同层直接的思想,残差网络应运而生。

理论介绍

本文以学习和应用为主,具体的公式推导不做过多的赘述,只是大概了解一下残差网络的原理即可。

残差网络是由一系列残差块组成的(图1)。一个残差块可以用表示为:

残差块分成两部分直接映射部分和残差部分。 xl 是直接映射,反应在图1中是左边的曲线;F(xl,Wl) 是残差部分,一般由两个或者三个卷积操作构成,即图1中右侧包含卷积的部分。

简单来说,就是将上一层的输出,即本层的输入直接和通过本层网络后的网络参数值做一个叠加,从而保留了上层网络的信息,网络就不会出现退化的现象,也就是说随着层数的增加,网络一定会越来越优化。

但是实际上来说,当层数增加到一定程度上也会出现退化的现象,具体原因暂时还不理解。

详细网络推导参考:详解残差网络 - 知乎

前期准备

框架思路

整个过程大致分为四步,分别是:人脸检测——人脸截取——人脸矫正——人脸识别,用ResNet实现对一个人脸的编码,生成一个128维向量,然后通过计算两个向量之间的距离来实现识别。

环境搭建

需要用到的有:

这里还得重点说明一下,由于opencv3以后的版本缺少contrib函数,所以不能用里面的Directory函数,这个函数本来是来得到文件夹目录中的所有特定类型的文件名,所以导致原来那个代码不能使用。为此,我去网上找了很多contrib库的安装教程,这个过程遇到的问题越来越多,最后成功了,但是因为文件地址的问题发现还是不能正常使用,于是找到了opencv3以后版本中的glob函数写了一个类,最后成功了。总之简单来说,如果要用参考里面的代码,就直接装opencv2,要是用我的代码,就不用管3以上的版本。

此外,还必须在dlib模型库中,下载链接为:http://dlib.net/files/   ,下载两个文件并解压,分别是为:

shape_predictor_68_face_landmarks.dat

dlib_face_recognition_resnet_model_v1.dat

第一个是人脸特征点识别模型,可以识别68个特征点,第二个基于Resnet的人脸识别模型。

代码实现

代码基本都给出了非常详细的说明,我是在图片进行人脸识别之后修改的,然后调用摄像头进行动态是识别,FPS不高,但是整体效果还不错,建议大家先从图片识别开始入手,读取路径那些代码基本没动,只是注释掉了,最好还是参考一下这篇文章里面的代码。

文章链接:ResNet:用dlib实现人脸识别—C++,包括人脸检测和人脸矫正(附代码)_意疏的博客-CSDN博客_dlib resnet

完整代码(输入图片识别)

/*-------------------------------------------------------------------------------------
这是一个例子,说明使用DLIB C++的深度学习工具库。在这里,我们将展示如何进行人脸识别。此示例使用预先培训过的
DLib_人脸识别_resnet_model_v1模型,可从DLIB网站下载。该模型在标准LFW面上的精度为99.38%。识别基准,与其他
最先进的面部识别方法相比截至2017年2月的认可。
on_images_ex.cpp示例。
------------------------------------------------------------------------------------*/
#include <cstdio>
#include <vector>
#include<algorithm>
#include<cstdio>

#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>

#include "opencv\cv.h"
#include "opencv2\core.hpp"
#include "opencv2\highgui\highgui.hpp"
#include "opencv2\imgproc\imgproc.hpp"
#include "opencv2\videoio.hpp"
//#include "contrib.hpp"


#include <dlib/dnn.h>
#include <dlib/gui_widgets.h>
#include <dlib/clustering.h>
#include <dlib/string.h>
#include <dlib/image_io.h>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib\opencv.h>


using namespace dlib;
using namespace std;
using namespace cv;

/*-----------------------------------------------------------------------------------*/

//下一位代码定义Resnet网络。基本上是复制的

//并从dnn_imagenet_ex.cpp示例粘贴,但我们替换了损失

//使用损耗度量进行分层,使网络变得更小。去读导论吧

//dlib dnn示例了解所有这些内容的含义。

//另外,dnn_metric_learning_on_images_ex.cpp示例显示了如何训练此网络。

//本例使用的dlib_face_recognition_resnet_model_v1模型是使用

//基本上是dnn_metric_learning_on_images_ex.cpp中显示的代码,除了

//小批量大(35x15而不是5x5),迭代没有进展

//设置为10000,训练数据集由大约300万个图像组成,而不是

//55。此外,输入层被锁定为150大小的图像。
/*------------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------------*/
template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = add_prev1<block<N, BN, 1, tag1<SUBNET>>>;

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = add_prev2<avg_pool<2, 2, 2, 2, skip1<tag2<block<N, BN, 2, tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET>
using block = BN<con<N, 3, 3, 1, 1, relu<BN<con<N, 3, 3, stride, stride, SUBNET>>>>>;

template <int N, typename SUBNET> using ares = relu<residual<block, N, affine, SUBNET>>;
template <int N, typename SUBNET> using ares_down = relu<residual_down<block, N, affine, SUBNET>>;

template <typename SUBNET> using alevel0 = ares_down<256, SUBNET>;
template <typename SUBNET> using alevel1 = ares<256, ares<256, ares_down<256, SUBNET>>>;
template <typename SUBNET> using alevel2 = ares<128, ares<128, ares_down<128, SUBNET>>>;
template <typename SUBNET> using alevel3 = ares<64, ares<64, ares<64, ares_down<64, SUBNET>>>>;
template <typename SUBNET> using alevel4 = ares<32, ares<32, ares<32, SUBNET>>>;

using anet_type = loss_metric<fc_no_bias<128, avg_pool_everything<
	alevel0<
	alevel1<
	alevel2<
	alevel3<
	alevel4<
	max_pool<3, 3, 2, 2, relu<affine<con<32, 7, 7, 2, 2,
	input_rgb_image_sized<150>
	>>>>>>>>>>>>;

/*------------------------------------------------------------------------------------*/
//截取文件名函数
String Getfilename(String &Orgfolder, String &Orgfilenames)
{
	char a[20];//保存字符
	int i = 0;
	int n = Orgfilenames.size() - Orgfolder.size()-1;//文件名长度
	for (i; i < n-4; i++)
	{
		a[i] = Orgfilenames[Orgfolder.size() + i+1];
	}
	a[i] = '\0';//结束标志
	String filename = a;//构造String类型数据
	return filename;
}


int main()
{
	VideoCapture capture(0);    // 打开摄像头
	if (!capture.isOpened())    // 判断是否打开成功
	{
		cout << "open camera failed. " << endl;
		return -1;
	}
	/*------------------------------------------------------------------------------------------------------*/

	cv::Mat II, III;                              //定义Mat矩阵存放摄像头图片
	std::vector<matrix<float, 0, 1>> vec;        //定义一个向量组,用于存放每一个人脸的编码;
	float vec_error[30];                         //定义一个浮点型的数组,用于存放一个人脸编码与人脸库的每一个人脸编码的差值;

	String dir_path = "C:\\Users\\18xlt\\Desktop\\Learning\\Photo\\train";//定义人脸库路径
	//cout << "Enter the path of picture set:";    //也可以输入人脸库文件夹路径
	//std::cin >> dir_path;
	string test_path;                              //定义测试集图片路径
	std::vector<cv::String> fileFullNames;            //将人脸库中每张图片创建路径列表
	std::vector<string> fileNames;                   //将人脸库中每张图片名字列表

	cv::glob(dir_path, fileFullNames, false);       //统计文件夹里jpg格式文件的个数,并将每个文件的名字保存
	cout << "The number of picture is:" << fileFullNames.size() << endl;

	for (int i = 0; i < fileFullNames.size(); i++)     //输出人脸库每张图片的绝对路径和对应姓名
	{
		string fileName = Getfilename(dir_path, fileFullNames[i]);
		fileNames.push_back(fileName);

		string fileFullName = dir_path + "//" + fileName + ".JPG";
		cout << "file name:" << fileName << endl;
		cout << "file paht:" << fileFullName << endl << endl;
	}
		//加载所有模型。首先,因为我们需要在图像中查找人脸我们需要人脸检测器:
		frontal_face_detector detector = get_frontal_face_detector();

		//我们还将使用人脸标记模型将人脸与标准姿势对齐:(有关介绍,请参见Face_Landmark_Detection_ex.cpp)
		shape_predictor sp;
		deserialize("D:/WorkSoftware/dlib/model/shape_predictor_68_face_landmarks.dat") >> sp;

		//终于我们加载Resnet模型进行人脸识别
		anet_type net;
		deserialize("D:/WorkSoftware/dlib/model/dlib_face_recognition_resnet_model_v1.dat") >> net;

		matrix<rgb_pixel> img, img1, img3;            //定义dlib型图片,彩色

	 /*-------------------------------------------------------------------------*/
	//此下为建立人脸编码库代码
		for (int k = 0; k < fileNames.size(); k++)  //依次加载完图片库里的文件
		{
			string fileFullName = dir_path + "/" + fileNames[k] + ".JPG";//图片地址+文件名
			load_image(img, fileFullName);//加载图片

			std::vector<dlib::rectangle> dets = detector(img);  //用dlib自带的人脸检测器检测人脸,然后将人脸位置大小信息存放到dets中
			img1 = img;
			cv::Mat I = dlib::toMat(img1);                     //dlib转成opencv
			std::vector<full_object_detection> shapes;
			if (dets.size() < 1)                               //判断是否单人脸图片
				cout << "There is no face" << endl;
			else if (dets.size() > 1)
				cout << "There is to many face" << endl;
			else
			{
				shapes.push_back(sp(img, dets[0]));             //画人脸轮廓,68点

				if (!shapes.empty()) {
					for (int j = 0; j < 68; j++) {
						circle(I, cvPoint(shapes[0].part(j).x(), shapes[0].part(j).y()), 3, cv::Scalar(255, 0, 0), -1);

						//	shapes[0].part(i).x();//68¸ö
					}
				}

				dlib::cv_image<rgb_pixel> dlib_img(I);//dlib<-opencv


													  // Run the face detector on the image of our action heroes, and for each face extract a
													  // copy that has been normalized to 150x150 pixels in size and appropriately rotated
													  // and centered.
													  //复制已规格化为150x150像素并适当旋转的

													  //居中。
				std::vector<matrix<rgb_pixel>> faces;//定义存放截取人脸数据组

				auto shape = sp(img, dets[0]);
				matrix<rgb_pixel> face_chip;
				extract_image_chip(img, get_face_chip_details(shape, 150, 0.25), face_chip);//截取人脸部分,并将大小调为150*150
				faces.push_back(move(face_chip));
				image_window win1(img); //显示原图

				win1.add_overlay(dets[0]);//在原图上框出人脸
				image_window win2(dlib_img);  //显示68点图

				image_window win3(faces[0]);//显示截取的人脸图像
											// Also put some boxes on the faces so we can see that the detector is finding
											// them.
											//同时在表面放置一些盒子,这样我们可以看到探测器正在寻找他们。

											// This call asks the DNN to convert each face image in faces into a 128D vector.
											// In this 128D vector space, images from the same person will be close to each other
											// but vectors from different people will be far apart.  So we can use these vectors to
											// identify if a pair of images are from the same person or from different people.  
											//此调用要求dnn将面中的每个面图像转换为128d矢量。

											//在这个128d向量空间中,同一个人的图像会彼此靠近

											//但是来自不同人群的向量会相差很远。所以我们可以用这些向量

											//标识一对图像是来自同一个人还是来自不同的人。
				std::vector<matrix<float, 0, 1>> face_descriptors = net(faces);//将150*150人脸图像载入Resnet残差网络,返回128D人脸特征存于face_descriptors

																			   //sprintf(vec, "%f", (double)length(face_descriptors[0]);
																			   //printf("%f\n", length(face_descriptors[0]));
																			   //vec[0] = face_descriptors[0];                              
				vec.push_back(face_descriptors[0]);                       //保存这一个人脸的特征向量到vec向量的对应位置
				cout << "The vector of picture " << fileNames[k] << "is:" << trans(face_descriptors[0]) << endl;//打印该人脸的标签和特征向量

																												/*-----------------------------------------------------------------------------------*/

			}
		}
		/*---------------------------------------------------------------------------------*/
		   //此下为识别部分代码

		while (1) 
		{

			cout << "input the path of test picture:";
			cin >> test_path;
			cout << test_path << endl;
			//capture.read(III);    // 读取图像帧至III
			//flip(III, II, 1);      //镜像翻转
			load_image(img3, test_path);
			//dlib::cv_image<rgb_pixel> img3(II);
			//image_window win4(img4);
			std::vector<matrix<rgb_pixel>> faces_test;
			for (auto face_test : detector(img3))
			{
				auto shape_test = sp(img3, face_test);
				matrix<rgb_pixel> face_chip_test;
				extract_image_chip(img3, get_face_chip_details(shape_test, 150, 0.25), face_chip_test);
				faces_test.push_back(move(face_chip_test));
				// Also put some boxes on the faces so we can see that the detector is finding
				// them.
			}

			std::vector<dlib::rectangle> dets_test = detector(img3);
			std::vector<matrix<float, 0, 1>> face_test_descriptors = net(faces_test);


			// In particular, one simple thing we can do is face clustering.  This next bit of code
			// creates a graph of connected faces and then uses the Chinese whispers graph clustering
			// algorithm to identify how many people there are and which faces belong to whom.
			std::vector<sample_pair> edges;
			for (size_t i = 0; i < face_test_descriptors.size(); ++i)                 //比对,识别
			{
				size_t m = 100;
				float error_min = 100.0;
				for (size_t j = 0; j < vec.size(); ++j)
				{

					// Faces are connected in the graph if they are close enough.  Here we check if
					// the distance between two face descriptors is less than 0.6, which is the
					// decision threshold the network was trained to use.  Although you can
					// certainly use any other threshold you find useful.

					vec_error[j] = (double)length(face_test_descriptors[i] - vec[j]);
					cout << "The error of two picture is:" << vec_error[j] << endl;

					//if (length(face_descriptors[i] - face_descriptors[j]) < 0.6)
					if (vec_error[j] < error_min)
					{
						error_min = vec_error[j];
						m = j;
					}
				}
				cout << "min error of two face:" << error_min << endl;
				II = dlib::toMat(img3);//dlib图片转成opencv的MAT
				std::string text = "Other face";
				if ((error_min < 0.5) && (m <= 27))
					text = fileNames[m];  //通过m定位文件,得到文件名


				int font_face = cv::FONT_HERSHEY_COMPLEX;
				double font_scale = 1;
				int thickness = 2;
				int baseline;
				//获取文本框的长宽
				cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, &baseline);

				//将文本框居中绘制
				cv::Point origin;

				cv::rectangle(II, cv::Rect(dets_test[i].left(), dets_test[i].top(), dets_test[i].width(), dets_test[i].width()), cv::Scalar(0, 0, 255), 1, 1, 0);//画矩形框
				origin.x = dets_test[i].left();
				origin.y = dets_test[i].top();
				cv::putText(II, text, origin, font_face, font_scale, cv::Scalar(255, 0, 0), thickness, 2, 0);//给图片加文字
			}
			dlib::cv_image<rgb_pixel> img4(II);
			image_window win4(img4);

			if (!II.empty())	// 判断是否为空
			{
				imshow("人脸识别", II);
			}
			//system("pause");
			if (cv::waitKey(50) == 27) { break; };
		}
	}

结果

  • 先读取文件夹中图片数量和每张图片的绝对路径和名字,方便调试而已,可以注释掉。

  • 输入测试图片的绝对路径(加文件名),打印特征向量以及找到和测试图片误差最小的训练图片。

  • 最后识别结果。

总结

个人觉得这种方法做出来的效果确实非常不错,默认的是误差小于0.4就认为是同一个人,但是在我实际用摄像头进行调节的过程中发现在头转动一定角度之后或者场景的光强等因素变化的时候会出现有时候不能识别的情况,于是微调了一下阈值,小于0.5就认为是同一个人,效果就非常不错了。但是可能出现相似的人识别错误的问题,暂时还未发现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++基于Resnet(Dlib库)+opencv3的高精度人脸识别 的相关文章

  • 在 Vulkan 中,图形队列系列与当前队列系列分离是否有益?

    据我所知 队列系列可能支持呈现到屏幕但不支持图形 假设我有一个同时支持图形和呈现的队列系列 以及另一个仅支持呈现的队列系列 我应该为两个进程使用第一个队列系列 还是应该将第一个队列系列委托给图形 将后者委托给呈现 或者这两种方法之间没有明显
  • C# 静态类型不能用作参数

    public static void SendEmail String from String To String Subject String HTML String AttachmentPath null String Attachme
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 混合模型优先和代码优先

    我们使用模型优先方法创建了一个 Web 应用程序 一名新开发人员进入该项目 并使用代码优先方法 使用数据库文件 创建了一个新的自定义模型 这 这是代码第一个数据库上下文 namespace WVITDB DAL public class D
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • if constexpr 中的 not-constexpr 变量 – clang 与 GCC

    struct A constexpr operator bool const return true int main auto f auto v if constexpr v A a f a clang 6 接受该代码 GCC 8 拒绝它
  • Unity手游触摸动作不扎实

    我的代码中有一种 错误 我只是找不到它发生的原因以及如何修复它 我是统一的初学者 甚至是统一的手机游戏的初学者 我使用触摸让玩家从一侧移动到另一侧 但问题是我希望玩家在手指从一侧滑动到另一侧时能够平滑移动 但我的代码还会将玩家移动到您点击的
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • LinkLabel 无下划线 - Compact Framework

    我正在使用 Microsoft Compact Framework 开发 Windows CE 应用程序 我必须使用 LinkLabel 它必须是白色且没有下划线 因此 在设计器中 我将字体颜色修改为白色 并在字体对话框中取消选中 下划线
  • 条件类型定义

    如果我有一小段这样的代码 template
  • 如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView?

    我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表 MousePos 是一个自定义类 它保存鼠标 X Y 坐标 类型为 Point 和该位置的运行计数 我有一个线程 System Timers Timer
  • SQLAPI++ 的免费替代品? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何免费 也许是开源 的替代品SQLAPI http www sqlapi com 这个库看起来
  • 以编程方式创建 Blob 存储容器

    我有一个要求 即在创建公司时 在我的 storageaccount 中创建关联的 blob 存储容器 并将容器名称设置为传入的字符串变量 我已尝试以下操作 public void AddCompanyStorage string subDo
  • 在 C# 的 WebAPI 中的 ApiController 上使用“传输编码:分块”提供数据

    我需要服务分块传输使用编码数据API控制器 因为我无权访问HttpContext or the Http请求 我有点不知道在哪里写入响应以及在哪里刷新它 设置如下 public class MyController ApiControlle
  • 如何获取带有某个属性注释的所有属性?

    我刚刚从 Roslyn 开始 我想找到所有用属性名称 OneToOne 注释的属性 我启动了 SyntaxVisualizer 并能够获取对该节点的引用 但我想知道是否有更简单的方法来实现此目的 这就是我所拥有的 var prop docu
  • 任何人都可以清楚地告诉如何在不使用像 这样的预定义函数的情况下找到带有小数值或小数值的指数吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 例如 2 0 5 1 414 所以想要 我是 c 的新手 所以请解释简单的逻辑 如果不是复杂的逻辑也足够了 在数学中 从整数取幂到实数
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其
  • 如何在 ASP.NET Core 中注入泛型的依赖关系

    我有以下存储库类 public class TestRepository Repository
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐

  • C#时间字符串转换

    class Program static void Main string args DateTime datetime DateTime Now 打印当前时间 Console WriteLine 时间为 n datetime n 方法1
  • Generator 函数

    Generator 函数 基本概念 与 Iterator 接口的关系 next 方法的参数 for of 循环 Generator prototype throw Generator prototype return next throw
  • docker quick start

    文章目录 安装 验证 设置阿里云加速代理 测试 常用命令 镜像和容器存储位置 容器生命周期 Dockerfile 构建镜像 部署tomcat 基础指令 构建redis镜像 容器间Link单向通信 容器间Bridge网桥双向通信 volume
  • 【Node.js实战】一文带你开发博客项目之日志(文件读写、stream流、写日志)

    个人简介 个人主页 前端杂货铺 学习方向 主攻前端方向 也会涉及到服务端 个人状态 在校大学生一枚 已拿多个前端 offer 秋招 未来打算 为中国的工业软件事业效力n年 推荐学习 前端面试宝典 Vue2 Vue3 Vue2 Vue3项目实
  • memcache清除缓存

    打开cmd 输入 telnet localhost 11211 flush all
  • 为博客添加樱花飘落的效果

    偶然看见别人博客中樱花飘落的效果 感觉很棒 于是我也给博客加上了樱花 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 新建 sakura js 新建一个 sakura js 文件 在里面填入下面的代码 代码来自于 折月
  • 快速排序的优化

    1 快速排序的基本思想 快速排序使用分治的思想 通过一趟排序将待排序列分割成两部分 其中一部分记录的关键字均比另一部分记录的关键字小 之后分别对这两部分记录继续进行排序 以达到整个序列有序的目的 2 快速排序的三个步骤 1 选择基准 在待排
  • 24. 在QML界面中嵌入QWidget窗口

    1 说明 目前QT官方主推的界面开发方式是采用QML进行界面设计 但在QML未流行之前 很多的项目都是采用QWidget开发的 把之前的代码全部转换为QML代码显然工作量非常大 如果能将QWidget窗口嵌入到QML界面中 那么开发效率将提
  • WTL 界面设计篇(CImageEx)

    头文件声明 CImageEx h pragma once include
  • chrony实现同步阿里云时间

    1 安装chrony yum install chrony y vim etc chrony conf 修改chrony配置文件 替换默认的centos server ntp1 aliyun com iburst server ntp2 a
  • 【Winform Vlc.DotNet Fill拉伸控件实现全屏】

    其实也有WPF得实现 但是这个效率更高 cpu占用在2 以内 基本都素在1 上下浮动 但是WPF基本就是3 起步了 想看WPF实现得右转 https blog csdn net weixin 38138153 article details
  • Linux usb子系统(二) _usb-skeleton.c精析

    drivers usb usb skeleton c 是内核提供给usb设备驱动开发者的海量存储usb设备的模板程序 程序不长 通用性却很强 十分经典 深入理解这个文件可以帮助我们更好的理解usb子系统以及usb设备驱动框架 写出更好的us
  • Python爬虫解析网页内容

    Python爬虫是一种自动化程序 可以模拟人类用户访问网页 获取网页中的内容 爬虫在信息采集 数据分析和网络监测等领域有着广泛的应用 在爬虫过程中 解析网页内容是非常重要的一步 Python提供了许多强大的库和工具 用于解析网页内容 其中
  • 基于MMDetection训练VOC格式数据集

    一 环境说明 基于前述安装MMDetection 数据集为VOC格式 主要版本如下 Python 3 7 8 CUDA 11 3 cuDNN 8 4 0 torch 1 12 0 torchvision 0 13 0 mmcv full 1
  • js逆向之猿人学-反混淆刷题平台第十八题(jsvmp)

    链接 第十八题 jsvmp 洞察先机 猿人学 内容分析 题目要求 抓取这5页的数字 计算加和并提交结果 接口分析 F12 点击第二页 这里我们能看到有三个请求参数 page t v 一看就能知道 page 页码 t 时间戳 v 加密参数 所
  • python编写字典爆破网站登录

    在做渗透时 因为太菜了 不会用网上的爆破工具 自己就即兴写了一个简单的字典爆破 新手小白可以借鉴思路 就当学习了 大佬请绕道 如果有好用的爆破工具 欢迎大佬推荐 方便理解 我简单说一下背景 渗透的是某靶机的网站 该靶机只用输入正确密码就能成
  • python idle 清屏

    使用 python idle 写代码时 写满屏幕后 总想清屏一下 类似Windows dos窗口的 cls 命令 百度查到好多种方法 经过测试 发现均不好用 本人使用版本为 2 7 8 1 在shell中输入 import os os sy
  • C++枚举类型用法总结(enum)(可以用枚举字符常量代替常量)

    C 枚举类型enum表示枚举 通常用于定义个新类型的字符常量 比如enum January February March 枚举类型的作用域一般是整个文件或者类内 当枚举类型定义在文件开头时 则作用域是从定义位置开始往后 不包括函数内定义 当
  • 《Keras深度学习:入门、实战与进阶》之印第安人糖尿病诊断

    本文摘自 Keras深度学习 入门 实战与进阶 1 数据理解 本节使用Pima Indians糖尿病发病情况数据集 该数据集最初来自国家糖尿病 消化 肾脏疾病研究所 数据集的目标是基于数据集中包含的某些诊断测量来诊断性的预测患者是否患有糖尿
  • c++基于Resnet(Dlib库)+opencv3的高精度人脸识别

    Resnet 产生原因 介绍残差网络Resnet之前 先介绍一下卷积神经网络过程中会遇到的问题 分别有 计算资源的消耗 模型容易过拟合 梯度消失 梯度爆炸问题的产生 问题1可以通过GPU集群来解决 对于一个企业资源并不是很大的问题 问题2的