基于 opencv 的人脸识别上课考勤系统,附源码,可作为毕业设计
一、简介
这个人脸识别考勤签到系统是基于大佬的人脸识别陌生人报警系统二次开发的。
项目使用Python实现,基于OpenCV框架进行人脸识别和摄像头硬件调用,同时也用OpenCV工具包处理图片。交互界面使用pyqt5实现。
该系统实现了从学生信息输入、人脸数据录入、人脸数据训练,学生信息多条件搜索、修改,多选删除,人脸数据训练,人脸识别、追踪、签到等完整流程的各项功能。甚至允许生成签到表格和导出Excel格式签到表。
根据功能分配,系统分为三个部分实现各部分流程,
- 录入端负责数据导入,
- 管理端负责数据删改查以及人脸数据训练,
- 监控端负责人脸识别以及签到功能。
二、效果图
源码下载地址
源码下载地址https://download.csdn.net/download/u013749113/87897481
监控端使用界面如图所示:
管理端使用界面如图所示:
录入端使用界面如图所示:
三、开发过程
本系统在原始项目的框架基础上做了大量修改,针对系统功能的不同以及部分模块实现的不完整做了补充和优化。原项目在功能上实现了完整的人脸数据录入过程,并已经存在数据管理、数据录入、核心(Core)三个基本模块。
监控端修改部分
(1)人脸识别实现
在主要部分【核心】的实现上,原项目采用LBPH实现了人脸识别的基本功能,并使用haar-like实现人脸位置捕获,使用dlib目标追踪器实现同一画面下多个人脸的目标追踪,尝试主要追踪,次要捕获的方式优化人脸捕获的过程。识别速度与画面帧率很高,
但识别准确率并不理想,并且严重受到光照条件影响。【实际使用时证明,若录入人脸数据时存在单方面光源照射,在识别时,光源位置一旦改变就完全识别不出来了】
并且录入数据时需要大量人脸数据集【100张人脸图以上】,才能获得较高的识别置信度。
二次开发后,保留原本速度较快的LBPH人脸识别,新增效果更好的dlib_face_recognition_resnet_model深度学习残差网络识别模型。实际使用时发现,虽然实时视频流帧率明显降低,但是识别准确率大大提高。
(2)摄像头启动与关闭
在原项目中,在摄像头调用的设计上,录入端和监控端同样都是用了摄像头,并进行人脸捕获,但监控端只允许打开和关闭一次摄像头,之后就禁用了摄像头控制按钮,而录入端则允许随意多次的打开和关闭摄像头。
其原因是具体实现上,因为监控端需要启动单独线程执行人脸识别任务,而录入端没有人脸识别的复杂处理过程,在一个线程中就可以实现需要的功能。监控端开启摄像头后,同时启动人脸识别线程处理单帧图像,并启动报警监听线程,设计思路上是报警线程一旦开启,全程保持监听,直到程序关闭,而关闭摄像头时代码实现上只关闭,也只能够关闭人脸识别线程,对于报警监听线程无法控制。导致如果容许摄像头二次开启,人脸识别线程能够启动,但作为同一个线程实例的报警监听模块将被二次启动,导致程序出错。
这里的人脸识别线程继承自QTread类,允许使用.stop()函数控制线程实例的开始与终止,而报警监听模块则是普通的threading.Thread(),开启后无法控制关闭,导致出现上述问题。改进方法是将报警监听线程使用QTread实现,使用与人脸识别线程相同的控制方法,摄像头开启时启动,摄像头关闭时终止,实现二次启动功能。
(3)报警系统改为签到系统
因为系统的功能修改前后有所改变,但是实现的技术实际上是一样的。原始项目的报警功能实际上可以作为签到系统的监听线程。原始代码逻辑为,发现置信度低于阈值的脸即陌生人脸,随机人脸识别线程通过通信队列告知报警系统执行截图拍摄、消息推送、报警响铃等功能。改变为签到系统后,人脸识别线程将置信度阈值以上【即数据库中存在并认为可信的脸】,且匹配度最高的人脸作为签到信号,通过队列通信发送给签到线程,并执行之前没有的数据库录入操作。
简单来说就是,之前是把认不出来的脸进行记录和报警,转变为认出来且最像的人脸进行记录和执行声音提示。
(4)签到表格的创建
这个属于签到系统特有的新增功能,说道考勤签到就一定会想到教师用来点名的签到表,因此该签到系统需要生成一个包含当前课程需要签到的所有学生信息,通过预处理进行创建,并在人脸识别过程中实时修改记录签到信息,主要内容为学生姓名、学号、签到时间。该功能内建了一个基于MySQL查询的QTableWidget控件,用于方便用户之间从数据库中选择并创建需要签到的学生名单。
管理端修改部分
(1)学生信息管理
原始的管理端非常简陋,存储信息仅包含学生姓名、faceID、学号这样的普通信息,想要查询学生信息也只能进行单人查询,并且只能通过学号,查询的唯一目的就是通过学号删除查询结果。因此无论是查询还是删除都非常简陋。甚至根本不存在修改信息的功能。
修改后的管理端新增了多条件模糊查询,并且大大增加了信息维数,允许直接双击修改学生信息,并实时同步到数据库中。同时删除信息的功能也和信息查询分离开来,信息查询结果动态显示在QTableWidget控件中【与原项目使用的技术相同,只是做了更大的功能扩展】。支持用户多选删除,不不必“查一个,删一个”。
(2)人脸数据训练
原项目中的人脸数据训练功能同样集成在管理端中,原作者自定义了一个读取数据集并将其与学生通过faceID进行唯一匹配的函数,也就是LBPH数据训练中的人脸数据(faces)与分类标签(labels)。读取并整理成LBPH.train()所需要的数据结构后即可直接将数据作为参数调用封装好的函数进行训练。
值得一提的是,LBPH的数据训练非常快,即使在三百人的,每人人脸数据集平均20张的情况下,训练时间依然能控制在十几秒内【但是结果其实并不好】,因此原作者并没有将其作为单独的线程来执行,而是直接甩给用户一个提示:训练期间系统窗口可能会无响应,请耐心等待。。。
这样的结果是导致用户体验极差,会以为程序崩了,其实只是训练计算时间过长,导致windows消息监听一段时间内无回应,被认为是程序无响应。。。
二次开发后我将其独立为一个线程单独执行,并为执行函数增加了进度条,让用户直观的看到训练过程。顺便,在代码实现过程中,发现大量时间实际上消耗在图片数据集的读取上,训练的过程反倒没有那么久,因此进度条实际展示的是读取过程,逻辑上是先进行的数据读取,再计算特征值,然后继续读取下一个数据集,因此将数据读取作为进度来衡量不会有时间上的偏差。
三、代码具体实现
这里就简单贴一下数据录入端的代码。项目完整代码还请移步本文开始位置的链接。
#!/usr/bin/env python3
# Author: kuronekonano <god772525182@gmail.com>
# 人脸信息录入
import re
import string
import timeimport cv2
import pymysql
import shutilfrom PyQt5.QtCore import QTimer, QRegExp, pyqtSignal, QThread
from PyQt5.QtGui import QImage, QPixmap, QIcon, QRegExpValidator, QTextCursor
from PyQt5.QtWidgets import QDialog, QApplication, QWidget, QMessageBox, QFileDialog, QProgressBar
from PyQt5.uic import loadUiimport logging
import logging.config
import queue
import threading
import os
import sys
import xlrd
import randomfrom datetime import datetime# 用户取消了更新数据库操作
class OperationCancel(Exception):pass# 采集过程中出现干扰
class RecordDisturbance(Exception):passclass DataRecordUI(QWidget):receiveLogSignal = pyqtSignal(str)messagebox_signal = pyqtSignal(dict)# 日志队列logQueue = queue.Queue()def __init__(self):super(DataRecordUI, self).__init__()loadUi('./ui/DataRecord.ui', self) # 读取UI布局self.setWindowIcon(QIcon('./icons/icon.png'))self.setFixedSize(1528, 856)# OpenCV# 摄像头self.cap = cv2.VideoCapture()# 分类器self.faceCascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')# 图像捕获self.isExternalCameraUsed = Falseself.useExternalCameraCheckBox.stateChanged.connect(lambda: self.useExternalCamera(self.useExternalCameraCheckBox))self.startWebcamButton.toggled.connect(self.startWebcam)self.startWebcamButton.setCheckable(True)# 定时器self.timer = QTimer(self)self.timer.timeout.connect(self.updateFrame)# 人脸检测self.isFaceDetectEnabled = Falseself.enableFaceDetectButton.toggled.connect(self.enableFaceDetect)self.enableFaceDetectButton.setCheckable(True)# 数据库# self.database = 'users'self.datasets = './datasets'self.isDbReady = Falseself.initDbButton.setIcon(QIcon('./icons/warning.png'))self.initDbButton.clicked.connect(self.initDb)# 用户信息self.isUserInfoReady = Falseself.userInfo = {'stu_id': '','cn_name': '','en_name': '','stu_grade': '','stu_class': '','stu_sex': '','major': ''}self.addOrUpdateUserInfoButton.clicked.connect(self.addOrUpdateUserInfo)self.migrateToDbButton.clicked.connect(self.migrateToDb) # 插入新数据按键绑定# 人脸采集self.startFaceRecordButton.clicked.connect(lambda: self.startFaceRecord(self.startFaceRecordButton)) # 开始人脸采集按钮绑定,并传入按钮本身用于结束状态控制# self.startFaceRecordButton.setCheckable(True)self.faceRecordCount = 0 # 已采集照片计数器self.minFaceRecordCount = 100 # 最少采集照片数量self.isFaceDataReady = Falseself.isFaceRecordEnabled = Falseself.enableFaceRecordButton.clicked.connect(self.enableFaceRecord) # 按键绑定录入单帧图像# 日志系统self.receiveLogSignal.connect(lambda log: self.logOutput(log)) # pyqtsignal信号绑定self.messagebox_signal.connect(lambda log: self.message_output(log))self.logOutputThread = threading.Thread(target=self.receiveLog, daemon=True)self.logOutputThread.start()# 批量导入self.isImage_path_ready = False# self.ImagepathButton.clicked.connect(self.import_images_data) # 使用同一线程会导致窗口无响应self.ImagepathButton.clicked.connect(self.import_image_thread) # 使用多线程实现图片导入self.isExcel_path_ready = Falseself.ExcelpathButton.clicked.connect(self.import_excel_data)self.ImportPersonButton.clicked.connect(self.person_import_thread)@staticmethoddef connect_to_sql():conn = pymysql.connect(host='localhost',user='root',password='******',db='mytest',port=3306,charset='utf8')cursor = conn.cursor()return conn, cursor# 单人导入图片集【主线程】弃用def import_person_imageset(self):if self.isUserInfoReady: # 学生信息确认stu_id = self.userInfo.get('stu_id')self.ImportPersonButton.setIcon(QIcon('./icons/success.png'))image_paths = QFileDialog.getOpenFileNames(self, '选择图片',"./",'JEPG files(*.jpg);;PNG files(*.PNG)')if not os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):os.makedirs('{}/stu_{}'.format(self.datasets, stu_id))image_paths = image_paths[0]for index, path in enumerate(image_paths):try:img = cv2.imread(path)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图faces = self.faceCascade.detectMultiScale(gray, 1.3, 5, minSize=(90, 90)) # 分类器侦测人脸if len(faces) == 0:self.logQueue.put('图片{}中没有检测到人脸!'.format(path))continuefor (x, y, w, h) in faces:if len(faces) > 1:raise RecordDisturbancecv2.imwrite('{}/stu_{}/img.{}-{}.jpg'.format(self.datasets, stu_id, index, ''.join(random.sample(string.ascii_letters + string.digits, 4))),img[y - 20:y + h + 20, x - 20:x + w + 20]) # 灰度图的人脸区域except RecordDisturbance:logging.error('检测到多张人脸或环境干扰')self.logQueue.put('Warning:检测到图片{}存在多张人脸或环境干扰,已忽略。'.format(path))continueexcept Exception as e:logging.error('写入人脸图像文件到计算机过程中发生异常')self.logQueue.put('Error:无法保存人脸图像,导入该图片失败')print(e)self.migrateToDbButton.setEnabled(True) # 允许提交至数据库self.isFaceDataReady = Trueelse:self.ImportPersonButton.setIcon(QIcon('./icons/error.png'))self.ImportPersonButton.setChecked(False)self.logQueue.put('Error:操作失败,系统未检测到有效的用户信息')# 表格导入学生信息def import_excel_data(self):excel_paths = QFileDialog.getOpenFileNames(self, '选择表格',"./",'EXCEL 文件 (*.xlsx;*.xls;*.xlm;*.xlt;*.xlsm;*.xla)')excel_paths = excel_paths[0]conn, cursor = self.connect_to_sql()error_count = 0for path in excel_paths:sheets_file = xlrd.open_workbook(path)for index, sheet in enumerate(sheets_file.sheets()):self.logQueue.put("正在读取文件:" + str(path) + "的第" + str(index) + "个sheet表的内容...")for row in range(sheet.nrows):row_data = sheet.row_values(row)if row_data[1] == '姓名':continueself.userInfo['stu_id'] = row_data[4]self.userInfo['cn_name'] = row_data[1]self.userInfo['en_name'] = row_data[0]self.userInfo['stu_grade'] = '20' + self.userInfo['stu_id'][:2]self.userInfo['stu_class'] = row_data[3].rsplit('-', 1)[1]self.userInfo['stu_sex'] = row_data[5]self.userInfo['major'] = row_data[2]self.userInfo['province'] = row_data[-1]self.userInfo['nation'] = row_data[-2]# print(self.userInfo)try:stu_id = row_data[4]if not os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):os.makedirs('{}/stu_{}'.format(self.datasets, stu_id))db_user_count = self.commit_to_database(cursor)self.dbUserCountLcdNum.display(db_user_count) # 数据库人数计数器except OperationCancel:passexcept Exception as e:print(e)logging.error('读写数据库异常,无法向数据库插入/更新记录')self.logQueue.put('Error:读写数据库异常,同步失败')error_count += 1self.logQueue.put('导入完毕!其中导入失败 {} 条信息'.format(error_count))cursor.close()conn.commit()conn.close()# 启用新线程导入图片,并添加进度条def import_image_thread(self):self.image_paths = QFileDialog.getOpenFileNames(self, '选择图片',"./",'JEPG files(*.jpg);;PNG files(*.PNG)')self.image_paths = self.image_paths[0]if len(self.image_paths) != 0: # 点击导入但是没有选择文件时不需启动线程progress_bar = ActionsImportImage(self)print('import success!')# 启用新线程 单人图片导入 使用进度条def person_import_thread(self):if self.isUserInfoReady: # 学生信息确认stu_id = self.userInfo.get('stu_id')self.ImportPersonButton.setIcon(QIcon('./icons/success.png'))image_paths = QFileDialog.getOpenFileNames(self, '选择图片',"./",'JEPG files(*.jpg);;PNG files(*.PNG)')self.image_paths = image_paths[0]if len(self.image_paths) != 0: # 点击导入但是没有选择文件时不需启动线程if not os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):os.makedirs('{}/stu_{}'.format(self.datasets, stu_id))progress_bar = ActionsPersonImport(self)self.migrateToDbButton.setEnabled(True) # 允许提交至数据库self.isFaceDataReady = Trueelse:self.ImportPersonButton.setIcon(QIcon('./icons/error.png'))self.ImportPersonButton.setChecked(False)self.logQueue.put('Error:操作失败,系统未检测到有效的用户信息')# 图片批量导入【主线程】弃用def import_images_data(self):image_paths = QFileDialog.getOpenFileNames(self, '选择图片',"./",'JEPG files(*.jpg);;PNG files(*.PNG)')image_paths = image_paths[0]error_count = 0self.logQueue.put('开始读取图片数据...')for index, path in enumerate(image_paths):stu_id = os.path.split(path)[1].split('.')[0]# print(stu_id)if not os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):text = '命名错误!'informativeText = '<b>文件 <font color=red>{}</font> 存在问题,数据库中没有以该图片名为学号的用户。</b>'.format(path)DataRecordUI.callDialog(QMessageBox.Critical, text, informativeText, QMessageBox.Ok)error_count += 1continuedstpath = '{}/stu_{}/img.{}.jpg'.format(self.datasets, stu_id, stu_id + '-0')try:shutil.copy(path, dstpath)except:text = '命名格式错误!'informativeText = '<b>文件 <font color=red>{}</font> 命名格式不正确。</b>'.format(path)DataRecordUI.callDialog(QMessageBox.Critical, text, informativeText, QMessageBox.Ok)error_count += 1self.logQueue.put('图片批量导入完成!其中导入失败 {} 张图片'.format(error_count))# 是否使用外接摄像头def useExternalCamera(self, useExternalCameraCheckBox):if useExternalCameraCheckBox.isChecked():self.isExternalCameraUsed = Trueelse:self.isExternalCameraUsed = False# 打开/关闭摄像头def startWebcam(self, status):if status:if not self.cap.isOpened():camID = 1 if self.isExternalCameraUsed else 0 + cv2.CAP_DSHOWself.cap.open(camID)self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)ret, frame = self.cap.read() # 获取摄像头调用结果if not ret:logging.error('无法调用电脑摄像头{}'.format(camID))self.logQueue.put('Error:初始化摄像头失败')self.cap.release()self.startWebcamButton.setIcon(QIcon('./icons/error.png'))self.startWebcamButton.setChecked(False)else:self.timer.start(5)self.enableFaceDetectButton.setEnabled(True)self.startWebcamButton.setIcon(QIcon('./icons/success.png'))self.startWebcamButton.setText('关闭摄像头')else:if self.cap.isOpened():if self.timer.isActive():self.timer.stop()self.cap.release()self.faceDetectCaptureLabel.clear()self.faceDetectCaptureLabel.setText('<font color=red>摄像头未开启</font>')self.startWebcamButton.setText('打开摄像头')self.enableFaceDetectButton.setEnabled(False)self.startWebcamButton.setIcon(QIcon())# 开启/关闭人脸检测def enableFaceDetect(self, status):if self.cap.isOpened():if status:self.enableFaceDetectButton.setText('关闭人脸检测')self.isFaceDetectEnabled = Trueelse:self.enableFaceDetectButton.setText('开启人脸检测')self.isFaceDetectEnabled = False# 采集当前捕获帧def enableFaceRecord(self):if not self.isFaceRecordEnabled:self.isFaceRecordEnabled = True# 开始/结束采集人脸数据def startFaceRecord(self, startFaceRecordButton):if startFaceRecordButton.text() == '开始采集人脸数据': # 只能用==判断,不能用isif self.isFaceDetectEnabled:if self.isUserInfoReady: # 学生信息确认self.addOrUpdateUserInfoButton.setEnabled(False) # 采集人脸数据时禁用修改学生信息if not self.enableFaceRecordButton.isEnabled(): # 启用单帧采集按钮self.enableFaceRecordButton.setEnabled(True)self.enableFaceRecordButton.setIcon(QIcon())self.startFaceRecordButton.setIcon(QIcon('./icons/success.png'))self.startFaceRecordButton.setText('结束当前人脸采集') # 开始采集按钮状态修改为结束采集else:self.startFaceRecordButton.setIcon(QIcon('./icons/error.png'))self.startFaceRecordButton.setChecked(False)self.logQueue.put('Error:操作失败,系统未检测到有效的用户信息')else:self.startFaceRecordButton.setIcon(QIcon('./icons/error.png'))self.logQueue.put('Error:操作失败,请开启人脸检测')else: # 根据按钮文本信息判断是结束采集还是开始采集if self.faceRecordCount < self.minFaceRecordCount:text = '系统当前采集了 <font color=blue>{}</font> 帧图像,采集数据过少会导致较大的识别误差。'.format(self.faceRecordCount)informativeText = '<b>请至少采集 <font color=red>{}</font> 帧图像。</b>'.format(self.minFaceRecordCount)DataRecordUI.callDialog(QMessageBox.Information, text, informativeText, QMessageBox.Ok)else:text = '系统当前采集了 <font color=blue>{}</font> 帧图像,继续采集可以提高识别准确率。'.format(self.faceRecordCount)informativeText = '<b>你确定结束当前人脸采集吗?</b>'ret = DataRecordUI.callDialog(QMessageBox.Question, text, informativeText,QMessageBox.Yes | QMessageBox.No,QMessageBox.No)if ret == QMessageBox.Yes:self.isFaceDataReady = True # 结束采集,人脸数据准备完毕if self.isFaceRecordEnabled:self.isFaceRecordEnabled = Falseself.enableFaceRecordButton.setEnabled(False) # 结束采集,单帧采集按钮禁用self.enableFaceRecordButton.setIcon(QIcon())self.startFaceRecordButton.setText('开始采集人脸数据') # 修改按钮文本为开始状态self.startFaceRecordButton.setEnabled(False) # 不可重新开始采集self.startFaceRecordButton.setIcon(QIcon())self.migrateToDbButton.setEnabled(True) # 允许提交至数据库# 定时器,实时更新画面def updateFrame(self):ret, frame = self.cap.read()# frame = cv2.flip(frame, 1) # 水平翻转图片if ret:# self.displayImage(frame) # ?两次输出?if self.isFaceDetectEnabled: # 人脸检测detected_frame = self.detectFace(frame)self.displayImage(detected_frame)else:self.displayImage(frame)# 检测人脸def detectFace(self, frame):gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 灰度图faces = self.faceCascade.detectMultiScale(gray, 1.3, 5, minSize=(90, 90)) # 分类器侦测人脸# 1.image为输入的灰度图像# 2.objects为得到被检测物体的矩形框向量组# 3.scaleFactor为每一个图像尺度中的尺度参数,默认值为1.1。scale_factor参数可以决定两个不同大小的窗口扫描之间有多大的跳跃,# 这个参数设置的大,则意味着计算会变快,但如果窗口错过了某个大小的人脸,则可能丢失物体。# 4.minNeighbors参数为每一个级联矩形应该保留的邻近个数,默认为3。# minNeighbors控制着误检测,默认值为3表明至少有3次重叠检测,我们才认为人脸确实存。# 6.cvSize()指示寻找人脸的最小区域。设置这个参数过大,会以丢失小物体为代价减少计算量。stu_id = self.userInfo.get('stu_id')# 遍历所有人脸,只允许有一个人的脸for (x, y, w, h) in faces:if self.isFaceRecordEnabled:try: # 创建学号对应的图片数据集if not os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):os.makedirs('{}/stu_{}'.format(self.datasets, stu_id))if len(faces) > 1:raise RecordDisturbancecv2.imwrite('{}/stu_{}/img.{}.jpg'.format(self.datasets, stu_id, self.faceRecordCount + 1),frame[y - 20:y + h + 20, x - 20:x + w + 20]) # 灰度图的人脸区域except RecordDisturbance:self.isFaceRecordEnabled = Falselogging.error('检测到多张人脸或环境干扰')self.logQueue.put('Warning:检测到多张人脸或环境干扰,请解决问题后继续')self.enableFaceRecordButton.setIcon(QIcon('./icons/warning.png'))continueexcept Exception as e:logging.error('写入人脸图像文件到计算机过程中发生异常')self.enableFaceRecordButton.setIcon(QIcon('./icons/error.png'))self.logQueue.put('Error:无法保存人脸图像,采集当前捕获帧失败')else:self.enableFaceRecordButton.setIcon(QIcon('./icons/success.png'))self.faceRecordCount = self.faceRecordCount + 1self.isFaceRecordEnabled = False # 单帧拍摄完成后马上关闭self.faceRecordCountLcdNum.display(self.faceRecordCount) # 更新采集数量cv2.rectangle(frame, (x - 5, y - 10), (x + w + 5, y + h + 10), (0, 0, 255), 2) # 红色追踪框return frame
源码下载地址
源码下载地址https://download.csdn.net/download/u013749113/87897481
相关文章:
基于 opencv 的人脸识别上课考勤系统,附源码,可作为毕业设计
一、简介 这个人脸识别考勤签到系统是基于大佬的人脸识别陌生人报警系统二次开发的。 项目使用Python实现,基于OpenCV框架进行人脸识别和摄像头硬件调用,同时也用OpenCV工具包处理图片。交互界面使用pyqt5实现。 该系统实现了从学生信息输入、人脸数据…...
.editorconfig 配置
有人会问:既然项目已经使用了 eslint 和 prettier,为什么还需要 EditorConfig? 为什么需要 EditorConfig? .editorconfig 是一个用于定义和维护跨不同编辑器和开发环境的一致编码样式的文件。它可以确保整个团队在使用不同编辑器…...
Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider
介绍 首先明确一下什么是延迟查找,一般来说通过Autowired注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到…...
VSCode--Config
1. basic 1.1 调整字体 1.2 调整 remote login 输入框都在 TERMINAL 中实现 1.3 界面设置成中文 安装插件: 然后配置即可。 2.Linux 2.1 Install 2.1.1 offline Install vscode server 问题描述 内网开发,vscode 自身通过代理安装完 remote 插件后…...
代码随想录刷题第48天|LeetCode198打家劫舍、LeetCode213打家劫舍II、LeetCode337打家劫舍III
1、LeetCode198打家劫舍 题目链接:198、打家劫舍 1、dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。 2、递推公式: 如果偷第i房间,那么dp[i] dp[i - 2] nums[i] …...
C# NTS 获取MuliiLineString中的所有线
/// <summary>/// 获取多段线的所有线/// </summary>/// <param name"ml"></param>/// <returns></returns>public static List<LineString> GetLineStrings(this MultiLineString ml){List<LineString> lineString…...
CodeWhisperer插件使用体验
官方教程点击跳转 使用工具 1.vscode 2.插件(AWS Toolkit),免费使用 安装以后如何使用 1.首先要有一个aws账号 2.插件下载好以后登录aws账号,我们主要用这款插件的CodeWhisperer这个功能,其它的自行看官方教程了解。 注意事项:我们在从vs…...
机器学习笔记 - 多实例学习(MIL)弱监督学习
一、多实例学习概述 多实例学习(MIL)是一种弱监督学习形式,其中训练实例被排列在称为袋的集合中,并为整个袋提供标签。这种方式越来越受到人们的关注,因为它自然适合各种问题,并允许利用弱标记数据。因此,它被应用于计算机视觉和文档分类等不同的应用领域。 多实例学习(…...
SQL Server 2008 定时自动备份和自动删除方法
SQL Server 2008 数据定时自动备份和自动删除方法,同一个计划兼备数据备份数数据删除的操作方法 工具/原料 SQL Server 2008 方法/步骤 1、 点击实例名下的【管理】-【维护计划】-点击鼠标右键,点击【维护计划向导】,填写计划名称&…...
代码生成器实现
代码生成器实现 实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的 构造数据模型 需求分析 借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而 言,其强调 数据模型 模板 文件 的思…...
【Python基础】Python函数(基本函数)
文章目录 Python函数1、函数多返回值2、函数多种传参方式(1)位置参数(2)关键字参数(3)缺省参数(4)不定长参数位置传递关键字传递 3、小结 Python函数 1、函数多返回值 Q:如果一个函数要有多个返回值,该如何书写代码? # 使用多个变量&#…...
Vue3 + TS + Vite —— 大屏可视化 项目实战
前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_彩色之外的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2014…...
EasyExcel 批量导入并校验数据
文章目录 前言一、pom二、使用步骤1.导入对象2.读入数据并保存 前言 EasyExcel 批量导入并校验数据 一、pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></depend…...
亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成
卖家搭建一套完整的测评系统,卖家自己能够养出批量优质账号,并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广,防御反击,同时节省运营成本,实现高效的测评运营。 我们的系统支…...
springboot的目录结构作用
springboot单体项目结构大概如下。 代码都在src/main下, java是后端代码 java下最基本的包 dao(mapper) entity(model) service controller 其他的包根据项目需求扩展。 resources下是配置文件。 如果不是前后端分离,resources下放的是静态文件…...
微信小程序基础使用-请求数据并渲染
小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象,不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…...
代码随想录训练营Day55| 392.判断子序列 ;115.不同的子序列
392.判断子序列 class Solution {public boolean isSubsequence(String s, String t) {int m s.length();int n t.length();if(m>n) return false;int[][] dp new int[m1][n1];dp[0][0]0;for(int i1;i<m;i){for(int j1;j<n;j){if(s.charAt(i-1)t.charAt(j-1)){dp[i…...
网络作业9【计算机网络】
网络作业9【计算机网络】 前言推荐网络作业9一. 单选题(共12题,36分)二. 多选题(共1题,3分)三. 填空题(共2题,10分)四. 阅读理解(共1题,17分&…...
C++ QT 上传图片至mysql数据库
以下是一个简单的C QT上传图片至MySQL数据库的代码示例: #include <QtSql> #include <QFile> #include <QByteArray> int main() { //连接数据库 QSqlDatabase db QSqlDatabase::addDatabase("QMYSQL"); …...
2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端
🎈 限时活动领体验会员:可下载程序网创项目短视频素材 🎈 🎉 有需要的朋友记得关赞评,阅读文章底部来交流!!! 🎉 ✨ 源码介绍 一个基于uniapp写的小程序,后端…...
【ChatGPT】数据科学 ChatGPT Cheat Sheet 书籍分享(阿里云盘下载)
封皮 以下为书中部分内容的机器翻译 我们的重要提示指南 1. 以 AI 角色的描述开始提示。 例如,“你是{x}”或“我希望你扮演{x}”。如果您不确定,请尝试“你是一个有帮助的助手”。 例如,您是 OpenAI 的数据科学家,您正在研究大型…...
使用 Docker-compose 搭建lnmp
服务编排: 应用编排: 单机环境下:shell/python脚本多机/集群环境下:ansible、saltstack、pubbet docker容器编排: 单机:docker-compose多机/集群:docker swarm,mesos marathon&a…...
chatgpt赋能python:Python中的矩阵合并方法:介绍和使用方法
Python中的矩阵合并方法: 介绍和使用方法 矩阵合并是Python编程中常用的操作之一,特别是针对数据分析、机器学习和深度学习等领域。Python提供了多种方法来合并矩阵,本文将介绍这些方法并分享如何在实际应用中使用它们。 普通矩阵合并 最基础的矩阵合…...
Java动态代理:优化静态代理模式的灵活解决方案
文章目录 代理模式定义具体实现分析优缺点 优化使用动态代理解决优化相关知识动态代理种类场景应用 代理模式 定义 代理模式,为其他对象提供一种代理以控制对这个对象的访问 具体实现 代理模式的具体实现描述可以分为以下几个步骤: 创建抽象对象接…...
四种Bootloader程序安全机制设计
正文 大家周末好,我是bug菌~ 不管是玩单片机还是嵌入式linux,基本上都会接触到bootloader,所以bootloader程序也是一个关键的组件,进行硬件初始化,应用程序的合法性、完成性检测、升级功能等等都与其息息相关。 像一些…...
【DBA 警世录之习惯性命令---读书笔记】
👈【上一篇】 💖The Begin💖点点关注,收藏不迷路💖 【下一篇】👉 🔻【💣 话题引入:既然 DBA 这个职业如此危险,那么哪些习惯是 DBA 必须养成的呢&#x…...
Vue中如何进行状态持久化(LocalStorage、SessionStorage)
Vue中如何进行状态持久化(LocalStorage、SessionStorage)? 在Vue应用中,通常需要将一些状态进行持久化,以便在用户关闭浏览器或刷新页面后,仍能保留之前的状态。常见的持久化方式包括LocalStorage和Sessio…...
【30天熟悉Go语言】6 Go 复杂数据类型之指针
文章目录 一、前言二、数据类型总览三、指针1、特殊运算符& *2、内存角度来看指针3、使用指针修改数据4、指针使用的注意事项5、对比着看Java的引用类型 三、总结 一、前言 Go系列文章: GO开篇:手握Java走进Golang的世界2 Go开发环境搭建、Hello Wor…...
Linux内核使用红黑树的场景
进程调度队列 (Process Scheduling):内核需要对进程按照一定的调度策略进行排队,以便更好地利用 CPU 的时间片。因此,内核使用红黑树作为查找和管理进程调度队列的数据结构,以支持快速的查找、插入和删除操作。 文件系统 (File S…...
遗留的 AppSec 工具迷失在云端
随着应用程序开发步伐的加快,IT 和安全团队正在对旧的应用程序安全(AppSec) 工具失去信心。 根据 Backslash 对 300 名 CISO、AppSec 经理和工程师的调查,遗留工具无法跟上并陷入永远的追赶游戏。 影响是深远的,大多数组织都看到云原生 App…...
温州做网站的企业/市场营销是做什么的
从左到右依次排列,如果出现重复值,则按照右侧的排序规则进行排序; 例如:分数倒序排序,但是遇到重复值,则再按照class_id倒序排 例如:分数倒序排序,没有重复值,进行了正常…...
郑州网站托管公司/网站设计的流程
http://www.liangjing.org/qiyejianzhan/Ch/Net_Index.html http://www.phpweb.net/index.php转载于:https://www.cnblogs.com/liulf/archive/2011/04/01/2002745.html...
wp网站搬家教程/百度账号快速注册入口
从Java 5 开始引入了静态导入语法(import static)使用静态导入可以使被导入类的静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名。package cn.itcast.p6.staticimport;import java.util.*; import static java.uti…...
iis 发布网站 500/百度手机助手app下载安装
如果运行一些程序后,远行yum命令出现“rpmdb: Lock table is out of available locker entries...”的问题时, 你可以按照如下操作来修复它:错误表现如下:rpmdb: Lock table is out of available locker entrieserror: db4 error(22) from db->close:…...
实时新闻/seo权重优化软件
简介在早前的博客中曾经写过 Spring 程序通过 Bean 映射实现配置信息的读取。 在SpringBoot 框架中读取配置的方式变得非常多样,这导致读者在搜寻资料时反而容易迷糊。到底,SpringBoot 是按什么顺序加载配置?相应的,我们该选择什么…...
国外 网站 欣赏/公司网站建设流程
写在前面 很多小伙伴留言说让我写一些工作过程中的真实案例,写些啥呢?想来想去,写一篇我在以前公司从零开始到用户超千万的数据库架构升级演变的过程吧。 本文记录了我之前初到一家创业公司,从零开始到用户超千万,系统…...