基于OpenCV的人脸识别考勤系统
考勤系统设计
学生上课考勤系统最初的方式是采用的人工纸质点名,目前仍旧有一部分学校依旧采用此种方法点名,这种方法也一直是被认为最有效的签到点名方式。但由于课程繁多加上学生人数众多, 代替点名现象普遍存在, 而且传统的现场点名签到方式费时费力, 直接影响到授课质量。人脸签到系统解决了这一问题,可以实时监测所到的成员数量和质量。
其整体结构图为
人脸数据库的建立
系统采集成员的人脸图像,将这些人脸分类标号保存,并建立人脸库。图像采集
摄像头采集人脸图像,前期用于训练模型,后期用于对人脸的预测。图像预处理
由于摄像头设备存在采集图像方法、提取人脸角度、图像背景以及光照变化等干扰信号,使得识别正确率出现不同程度的降低。所以,需将采集到的图像以及检测出来的人脸通过图像处理算法处理。其中处理方法有尺度归一化、图像灰度化、灰度变换、图像增强、以及图像降噪等。人脸特征提取及模型训练
图像特征提取和特征描述是图像目标识别的关键技术,特征提取结果的好坏,直接影响模型训练结果,进而影响了目标识别的效果,在图像检索系统中,特征描述的好坏也会直接影响目标匹配和图像检索的精度。图像处理中特征点的检测与匹配是机器视觉最重要的部分。
特征提取指的是使用计算机提取视频中的图像信息,决定每个图像的点是否属于一个图像特征。特征提取的结果是把图像上的点分为不同的子集,这些子集往往属于孤立的点、连续的曲线或者连续的区域。特征的好坏对泛化性能有至关重要的影响。
图像的模型训练即是通过对所提取的人脸特征,不断调整模型参数使人脸图像针对于模型之间的误差值达到最小。用于之后对人脸的预测。人脸识别
提取的人脸图像的特征数据与数据库中存储的特征模板进行搜索匹配,通过设定一个阈值,当相似度超过这一阈值,则把匹配得到的结果输出。人脸识别就是将待识别的人脸特征与已得到的人脸特征模板进行比较,根据相似程度对人脸的身份信息进行判断。这一过程又分为两类:一类是确认,是一对一进行图像比较的过程,另一类是辨认,是一对多进行图像匹配对比的过程。在图像匹配过程中有一个重要特征是:每个识别匹配都具有转置信(confidence)评分,因此可在实际应用中通过对其设置阈值来进行筛选。
人脸识别是利用提取好的特征,进行身份确认或在人脸库中比对搜索最巧似者。因此,需要选择合适的算法进行识别匹配。在OpenCV中有三种人脸识别的方法,它们分别基于三种不同的算法:Eigenfaces、FisherFaces和Local Binary Pattern Histogram(LBPH)。
(1)Eigenfaces算法是通过PCA来处理。PCA的本质是识别某个训练集上的主成分,并计算出训练集(图像或帧中的检测到的人脸)相对于数据库的发散程度,并输出一个值。该值越小,表明人脸数据库和检测到的人脸之间的差别就越小;0值表示完全匹配。
(2)Fisherfaces算法是从PCA中衍生发展出来的,采用更复杂的逻辑;尽管计算更加密集,但比Eigenfaces更容易得到准确效果。
(3)LBPH算法将检测到的人脸分为小单元,并将其与模型中的对应单元比较,对每个区域匹配值产生一个直方图。
predict()函数返回含有两个元素的数组:第一个元素是所识别个体的标签,第二个是信度评分。所有的算法都有一个置信度评分阈值,置信度评分用来衡量所识别人脸与原模型的差距,0表示完全匹配。可能有时不想保留所有的识别结果,则需进一步处理,因此可用自己的算法来估算识别的置信度评分;例如,如果正在试图识别视频中的人,则可能要分析后续帧的置信度评分来估计识别是否成功。在这种情况下,可通过算法来检查得到的置信度评分,然后得出自己的结论。
显示签到结果
分析人脸的特征通过所训练的模型与数据库中进行对比,得到具体人的具体信息,进行签到记入数据库
功能实现
UI界面设计
人脸识别考勤系统共有三个界面:人脸识别考勤系统主界面、人脸数据采集及训练界面和进入考勤系统三部分。具体见下图:人脸识别考勤系统界面整体设计。
(1)人脸识别考勤系统主界面
人脸识别考勤系统主界面(具体见下图)包含三个按钮,分别是“人脸数据采集及训练”、“进入考勤系统”和“退出考勤”。点击“人脸数据采集及训练”即可进入“人脸数据采集及训练”界面。
(2)人脸数据采集及训练界面
人脸数据采集及训练界面(具体见下图)包含三个按钮分别是“开始采集”、“人脸采集训练”和“信息采集”(信息采集指text文本框中学生基本信息的输入)。在这一界面首先要输入信息采集框内的具体信息,然后点击“开始采集”按钮即可打开摄像头采集人脸信息并保存至人脸信息数据集;点击“人脸采集训练”按钮即进行人脸数据集信息模型训练;
(3) 进入人脸识别考勤界面
进入人脸识别考勤界面(具体见下图)包含三个按钮分别是“显示审核信息”(显示审核信息指text文本框中学生基本信息的输出)、“考勤”和“考勤表”。在课前考勤是需先打开本系统,点击“考勤”即可加载摄像头进行人脸识别,人脸识别成功后“显示审核信息”栏内会输出学生基本信息,即代表签到成功。“考勤表”是为方便老师上课时清点签到人数而设置,点击“考勤表”。即显示已签到人人数和具体人员基本信息。
数据库设计
数据库设计主要分为学生信息和考勤信息两部分。
(1)学生信息
学生信息总表(具体见下表)录入信息时将学号sid和姓名name录入并设置考勤总次数times为0。当进行考勤时,考勤一次将对应人的总次数times增加1。
(2)考勤表
考勤表(具体见表2)记录考勤人员名单,当进行考勤时,后台将视频识别到的考勤人员信息插入此表,老师可根据查询考勤名单查看考勤人员。
系统具体功能
- 人脸信息采集及存储
首先,需要录入班级所有同学基本信息:姓名、学号并进行对应的人脸信息采集,并保存人脸图像信息,以便后期进行模型训练与人脸识别,具体人脸信息采集及存储流程图见图8.
人脸信息采集首先需要输入被采集学生的基本信息,打开摄像头进行图像采集,如果此学生已存在则会提示已经存在,如若没有则会对此学生进行过人脸采集。人脸采集是在摄像头采集的图片的基础上进行的,由于采集的图片信息周围环境嘈杂,需要进行专门的人脸检测采集人脸信息,在本系统中采用Cascade级联分类器进行人脸检测,主要步骤为先加载OpenCV自带人脸检测器,使用cvtColor函数对图片灰度化处理,在人脸检测中,有效图像的预处理不仅可以提高人脸检测正确率,还可以极大地缩减检测时间,再使用detectMultiScale函数进行人脸识别检测人脸,如若检测到人脸信息,则裁剪灰度帧的区域,大小为200*200像素。整个采集过程中,人脸信息会自动编号保存,采集200张。然后将采集到的人脸信息保存到以此人名称命名的文件夹下,文件名后缀为.pgm,具体如下图所示。
- 人脸数据集模型训练处理
由于基础的人脸图像无法完成人脸识别匹配,因此需要对采集到的人脸信息图像进行特征分析与统计建立分类模型并进行模型训练。具体人脸数据集模型训练处理流程图见图10。
首先需要从上一步保存的人脸信息数据集中进行图像检索,如若没有图像文件信息则会提示文件不存在并跳出训练过程,若检测到文件存在则会创建特征检测器;使用IMREAD_GRAYSCALE将原始图像转化为灰度化图像打开;然后resize调整大小;返回图像和标签列表;并对其进行人脸特征提取,特征提取结果的好坏,直接影响模型训练结果,进而影响了目标识别的效果;此时我们对获取得人脸信息进行模型训练,并将训练结果保存,具体结果见图11。
- 人脸识别
人脸识别是整个系统的关键,本系统中采用LBPH进行人脸识别,需要在课前打开摄像头进行人脸考勤,具体人脸识别流程图见图12。
在打开人脸考勤系统时,后台会首先调取数据库中学生基本信息;初始化LBPH人脸识别模型;导入上一步训练好的人脸信息模型训练集;然后调用摄像头,对考勤学生进行人脸信息采集时相同步骤的人脸检测,对摄像头中检测到的人脸进行对比分析和算法比对;预测读取到的人脸信息,返回label和置信度评分。人脸识别中通过置信度评分来丢弃结果,LBPH算法中好的识别参考值要低于50,任何高于80的参考值都会被认为是低的置信度评分。根据置信度评分低于50和label返回数据库中学生信息,人脸识别结束。
- 人脸识别信息与数据库连接,签到表反馈
在上一步的人脸识别出考勤学生后,会将考勤学生的基本学生信息通过数据库插入考勤表中,通过点击考勤表按钮,直观的给教师反馈出实时到的考勤人员信息,具体考勤表、点击考勤表后控制台反馈信息见下图。
主要代码
主函数
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from MainWindow0 import MyWindowif __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)w = MyWindow()w.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint)w.setFixedSize(w.width(), w.height())w.show()sys.exit(app.exec_())
功能函数
import sys
import cv2
import os
import numpy as np
from PyQt5.QtCore import QCoreApplication
from PyQt5 import QtWidgets,QtMultimediaWidgets
from MainWindow import Ui_MainWindow
from kaoqin_list import Ui_kaoqin_list
from face_data_collect import Ui_face_data_collect
from face_recognition_sys import Ui_face_recognition_sys
import face_data_collect
import kaoqin_list
import face_recognition_sys
import pymysql
import re#from collect import *
#from training import *
#from recognition import *
#这是考勤导出窗体
class Mykaoqin(QtWidgets.QWidget, Ui_kaoqin_list):def __init__(self, parent=None):super(Mykaoqin, self).__init__(parent)self.setupUi(self)def ButtonEvenbinding(self):self.Button.clicked.connect(self.pushButtonclick)def pushButton1click(self):print('kaoqin')#这里面写功能class mysqloper():def __init__(self, parent=None):# super(mysqloper, self).__init__(parent)self.db = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="199808", db="sa")# 得到一个可以执行SQL语句的光标对象self.cursor = self.db.cursor() # 使用 execute() 方法执行 SQL 查询self.cursor.execute('SELECT VERSION()')self.data = self.cursor.fetchone()print("Database version : %s " % self.data)# 关闭光标对象self.cursor.close()# 链接数据库def mysqlconnect(host="127.0.0.1", port=3306, user="root", passwd="199808", db="sa"):db = pymysql.connect(host=host, port=port, user=user, passwd=passwd, db=db)return db# 执行语句def executeCreate(self,con, sql):cursor = con.cursor() # 使用 execute() 方法执行 SQL 查询ret = cursor.execute(sql)cursor.close()return ret# 执行插入和更新def executeInsertAndUpdate(self,con, sql, listParams):try:cursor = con.cursor() # 使用 execute() 方法执行 SQL 查询print("前")ret = cursor.execute(sql, listParams)print("hou")# 提交事务self.db.commit()cursor.close()return retexcept:print("跳出")return 0# 执行查询def executeQuery(self,con, sql, listParams):cursor = con.cursor() # 使用 execute() 方法执行 SQL 查询cursor.execute(sql, listParams)results = cursor.fetchall()cursor.close()return resultsdef table_exists(self,con, table_name): # 这个函数用来判断表是否存在sql = "show tables;"cursor = con.cursor()cursor.execute(sql)tables = [cursor.fetchall()]table_list = re.findall('(\'.*?\')', str(tables))table_list = [re.sub("'", '', each) for each in table_list]cursor.close()if table_name in table_list:return 1 # 存在返回1else:return 0 # 不存在返回0# 插入函数def Insert(self,table, sid, name):times = 0# 判断是否存在表user1existflag = self.table_exists(self.db, table)if existflag == 0: # 不存在# 创建表 # 定义要执行的SQL语句sql = """CREATE TABLE {0}(sid INT(15) PRIMARY KEY ,name CHAR(10) NOT NULL UNIQUE,times TINYINT );"""# 执行SQL语句ret = self.executeCreate(self.db, sql.format(table))if ret:print('执行成功')else:print('执行失败')# 插入 更新、删除sql = "INSERT INTO {0}(name,sid,times) VALUES (%s,%s, %s);"# 执行SQL语句# print(sql.format(table))ret = self.executeInsertAndUpdate(self.db, sql.format(table), [name, sid, times])if ret:print('执行成功')else:print('执行失败')# 关闭数据库连接self.db.close()def InsertOnly(self,table,sid,name):existflag = self.table_exists(self.db, table)# print(existflag)if existflag == 0: # 不存在# 创建表 # 定义要执行的SQL语句sql = """CREATE TABLE {0}(sid INT PRIMARY KEY,name CHAR(10) NOT NULL UNIQUE);"""# 执行SQL语句ret = self.executeCreate(self.db, sql.format(table))if ret:print('考勤表创建成功')else:print('考勤表shibai')# 插入 更新、删除sql = "INSERT INTO {0}(sid,name) VALUES (%s,%s);"# 执行SQL语句print(sql.format(table))ret = self.executeInsertAndUpdate(self.db, sql.format(table), [sid,name])print("AAA")if ret:print('考勤成功')else:print('已考勤')# 关闭数据库连接self.db.close()#def QueryID(self,table, name):# 查询sql = "select sid from %s where name={0}"results = self.executeQuery(self.db, sql.format(name), table)print(results)# 关闭数据库连接self.db.close()return results# 查询表中的名字def Query(self,table):# 查询sql = "select * from {0}"results = self.executeQuery(self.db, sql.format(table), [])names = []sids=[]# 遍历结果for row in results:sids.append(row[0])names.append(row[1])# 关闭数据库连接self.db.close()return names,sidsdef Increase(self,table,face_ID):sql = "update {0} set times=times+1 where sid={1}"ret = self.executeInsertAndUpdate(self.db, sql.format(table,face_ID), [])if ret:print('总表插入成功')else:print('总表插入失败')# 关闭数据库连接self.db.close()#这是识别窗体
class myrecognition(QtWidgets.QWidget, Ui_face_recognition_sys):def __init__(self, parent=None):super(myrecognition, self).__init__(parent)self.setupUi(self)def ButtonEvenbinding(self):self.Button1.clicked.connect(self.pushButton1click)self.Button2.clicked.connect(self.pushButton2click)def NameList(self):# 查询数据库中的名字self.mysql=mysqloper()names,sid=self.mysql.Query('student')return names,siddef cognize(self):names,sid = self.NameList()recognizer = cv2.face.LBPHFaceRecognizer_create()# 读模型recognizer.read('./trainer/trainer.yml')cascadePath = "./haarcascade_frontalface_default.xml"faceCascade = cv2.CascadeClassifier(cascadePath)font = cv2.FONT_HERSHEY_SIMPLEX# 窗口self.cap = cv2.VideoCapture(0)minW = 0.1 * self.cap.get(3)minH = 0.1 * self.cap.get(4)count=0while True:count=count+1ret, img = self.cap.read()gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)faces = faceCascade.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=5,minSize=(int(minW), int(minH)))for (x, y, w, h) in faces:cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)label, confidence = recognizer.predict(gray[y:y + h, x:x + w])confidence = "{0}%".format(round(confidence))print(names[label],confidence)cv2.putText(img, str(names[label]), (x + 5, y - 5), font, 1, (0, 0, 255), 1)cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1)cv2.imshow('camera', img)k = cv2.waitKey(10)if k == 27:breakif k == ord('q'):break# if count > 20:# breakself.cap.release()cv2.destroyAllWindows()return names[label],sid[label]def pushButton1click(self):face_name,face_ID=self.cognize()print("------")print(face_name)print(face_ID)print("------")self.lineEdit6.setText(str(face_name))self.lineEdit7.setText(str(face_ID))print("-------")face_ID=int(face_ID)mysqloper().InsertOnly('student1', face_ID, face_name)print("--")mysqloper().Increase('student',face_ID)print("考勤成功")#这里面写功能def pushButton2click(self):name,sid=mysqloper().Query('student1')print("考勤列表")print(name)
#这是人脸数据采集及训练窗体
class Myface_collect(QtWidgets.QWidget, Ui_face_data_collect):#button1开始采集,button2人脸数据采集训练def __init__(self, parent=None):super(Myface_collect, self).__init__(parent)self.setupUi(self)def ButtonEvenbinding(self):self.Button1.clicked.connect(self.pushButton1click)self.Button2.clicked.connect(self.pushButton2click)#点击采集后实现的功能函数def video_demo_collect(self):self.cap = cv2.VideoCapture(0)face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')face_name = self.lineEdit1.text()# print(face_name)face_ID = self.lineEdit2.text()# print(face_ID)#face_name = input('请输入你要采集的人的姓名(拼音):')if not os.path.exists('./Facedata/%s/' % str(face_ID)):os.mkdir('./Facedata/%s' % str(face_ID))mysqloper().Insert('student', face_ID,face_name)else:print("已经存在此文件,正在重新读入数据")count = 0while (True):ref, frame = self.cap.read()gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)faces = face_detector.detectMultiScale(gray, 1.3, 5)if count > 200:breakfor (x, y, w, h) in faces:img = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)# font = cv2.FONT_HERSHEY_TRIPLEXf = cv2.resize(gray[y:y + h, x:x + w], (200, 200))# 保存到路径下# 保存图像cv2.imwrite('./Facedata/%s/%s.pgm' % (str(face_ID), str(count)), f)print(count)count += 1cv2.imshow('image', frame)# 保持画面的持续。k = cv2.waitKey(10) & 0xffif k == 27:breakprint("退出")self.cap.release()cv2.destroyAllWindows()def read_images(self,path, sz=None):c = 0X, y = [], []for dirname, dirnames, filenames in os.walk(path):for subdirname in dirnames:print(subdirname)# subdirname指文件名即人名subject_path = os.path.join(dirname, subdirname)for filename in os.listdir(subject_path):try:if (filename == ".directory"):continuefilepath = os.path.join(subject_path, filename)im = cv2.imread(os.path.join(subject_path, filename), cv2.IMREAD_GRAYSCALE)if (im is None):print("image " + filepath + " is none")# resize to given size (if given)if (sz is not None):im = cv2.resize(im, sz)X.append(np.asarray(im, dtype=np.uint8))y.append(c)# data[subdirname] = data.get(subdirname, []) + Xexcept IOError as e:print("I/O error({0}): {1}".format(e.errno, e.strerror))except:print("Unexpected error:", sys.exc_info()[0])raisec = c + 1return [X, y]def video_demo_train(self):path = './Facedata/'[X, y] = self.read_images(path)y = np.asarray(y, dtype=np.int32)# 创建模型model = cv2.face.LBPHFaceRecognizer_create()# 训练模型model.train(np.asarray(X), np.asarray(y))# 模型预测[p_label, p_confidence] = model.predict(np.asarray(X[0]))# 保存模型model.write('./trainer/trainer.yml')print("训练完成")def pushButton1click(self):self.video_demo_collect()#这里面写功能def pushButton2click(self):self.video_demo_train()#这是主窗体
class MyWindow(QtWidgets.QWidget, Ui_MainWindow):def __init__(self, parent=None):super(MyWindow, self).__init__(parent)self.setupUi(self)self.ButtonEvenbinding()self._isWinodowCreated = Trueself._shouldDrawDebugRects = True@propertydef isWindowCreated(self):return self._isWindowCreated# 每加一个界面就要增加一句self.模块名字.clicked.connect(lambda:self.自己定义的名字())# 然后在下面加# def 自定义名字1(self):# self.form1=QtWidgets.QWidget() 实例化# self.s = 界面Py文件名字.界面文件名字的类()# self.s.setupUi(self.form1)# self.form1.show()def ButtonEvenbinding(self):self.Button1.clicked.connect(self.pushButton1click)self.Button2.clicked.connect(self.pushButton2click)self.Button3.clicked.connect(QCoreApplication.instance().quit)
#这个地方其实就是相当于pushButton1click的main函数def pushButton1click(self):self.form1=QtWidgets.QWidget()self.s = Myface_collect()self.s.setupUi(self.form1)self.s.ButtonEvenbinding()self.form1.show()
#这个地方就相当于pushButton2click的main函数def pushButton2click(self):self.form2 = QtWidgets.QWidget()self.s = myrecognition()self.s.setupUi(self.form2)self.s.ButtonEvenbinding()self.form2.show()
基于OpenCV的人脸识别考勤系统相关推荐
- 基于OpenCV的人脸识别考勤系统(一)
本文旨在叙述我基于OpenCV和百度智能云的人脸识别考勤项目, 根据此系列, 应该可以复现出完整的项目. 该项目是在Ubuntu 16.04系统下使用OpenCV技术进行开发的,如果想要成功复现,最好 ...
- 基于OpenCV的人脸识别考勤系统(三)
目录 六.百度智能云人脸库的创建 七.人脸识别SDK的导入 八.百度云平台的接入 六.百度智能云人脸库的创建 在百度智能云的人脸识别控制台中,申请领取免费资源,在进一步页面中我们选择领取全部免费接口即 ...
- 基于OpenCV的人脸识别考勤系统(二)
该部分承接(一),将会实现视频图像的处理以及人脸的识别 目录 四.图像处理 1. cvtColor 2. equalizeHist 五.脸部识别 1. 导入训练文件 2. 绘制人脸框 3. 截取人脸并 ...
- 基于Python的人脸识别考勤系统
基于Python的人脸识别考勤系统 Python源文件: 基于Python3.7编程环境开发 需要安装 tkinter pil face_recognition OpenCV2 库来实现人脸识别 需要 ...
- 基于OpenCV的人脸识别签到系统
1. 摘要 随着人工智能技术的发展,人脸识别技术应用到了生活的很多方面,本文利用人脸识别技术实现了人脸识别签到功能.具体采用 Python 语言以及 dlib 库.face_recognition 库 ...
- 基于OpenCV的人脸识别系统的pyhon源代码
本论文主要阐述了基于OpenCV的人脸识别原型系统.基于生物特征识别的身份认证方法有指纹.掌纹.眼睛虹膜.人脸等,其中,由于人脸的稳定性和可见性,人脸识别的研究与应用成为热点,本文针对出租公寓安全管理 ...
- 【毕业设计_课程设计】基于opencv、dilb的员工人脸识别考勤系统
文章目录 0 项目说明 1 需求分析 2 总体设计 3 详细设计 4 效果展示 5 实验心得 6 项目源码 7 最后 0 项目说明 基于opencv.dilb的员工人脸识别考勤系统 提示:适合用于课程 ...
- python人脸识别考勤系统 dlib+OpenCV和Pyqt5、数据库sqlite 人脸识别系统 计算机 毕业设计 源码
一.项目介绍 Python语言.dlib.OpenCV.Pyqt5界面设计.sqlite3数据库 本系统使用dlib作为人脸识别工具,dlib提供一个方法可将人脸图片数据映射到128维度的空间向量,如 ...
- OpenCV+ Qt Designer 开发人脸识别考勤系统
文章目录 1. 系统介绍 2. 系统架构 3. 开发步骤 3.1 安装必要的库 3.2 设计用户界面 3.3 编写代码 3.3.1 导入库 3.3.2 连接数据库 3.3.3 定义主窗口类 3.3.4 ...
最新文章
- Jenkins实现SVN+Maven+Java项目的持续集成
- IIS6.0限制上传文件大小的解决办法
- C语言解释器的实现--存储结构(一)
- P4198 楼房重建
- Q-learning家族【强化学习】
- Python爬虫使用浏览器的cookies:browsercookie
- 7号团队-团队任务5:项目总结
- 一本院校大三萌妹子须臾:我的大数据之路
- ImageMagick内存占用过高被杀掉
- 各大主流编程语言简介
- 美式橄榄球(NFL)基本规则
- CTGU实验5_2-创建借书触发器
- android时间24小时,安卓时间显示TextClock显示日期时间,24小时制和12小时制(自定义...
- 专科计算机课程作业2理解题,计算机第二模块练习题
- new bing聊天机器人免翻命令行使用--大佬逆向工程api
- VB.NET excel 列排序
- 35岁的程序员:第26章,回家
- UCML应用框架平台的特点
- HLS / Chisel 实现CORDIC算法双曲系统
- centos7系统root分区扩容