记人脸识别要取到灰度图这是个忽略点。cv2.face 依赖opencv-contrib-python 的功能先下载 pip install opencv-contrib-python4.8.1.78 numpy1.26.4importosimportcv2importtimeimportdatetimeimportnumpyasnpfromsqlalchemyimportcreate_engineimportpandasaspdimportmatplotlib.pyplotasplt# 生成报告所需库fromreportlab.platypusimportSimpleDocTemplate,Paragraphfromreportlab.lib.stylesimportgetSampleStyleSheetfromreportlab.pdfbaseimportpdfmetricsfromreportlab.pdfbase.ttfontsimportTTFontfromreportlab.platypusimportImagefromreportlab.lib.unitsimportinch# 存放采集的图片文件地址COLLECT_LOCAL./item/images/# 存放模型文件地址及文件名称MODEL_LOCAL./item/models/LBPHFaceRecognizer_train.yaml# 一次采集照片数量SAVE_NUM5# 获取指定文件夹下的文件defget_dir_file(url):# 获取指定路径下的所有文件list_diros.listdir(url)returnlist_dir# 数据库enginecreate_engine(mysqlpymysql://root:123456localhost:3306/face?charsetutf8mb4)# 封装数据库的 添加操作defadd_sql(data,table_name):dfpd.DataFrame(data)df.to_sql(table_name,conengine,if_existsappend,indexFalse)# 人脸信息采集defcapture_face_for_training(eid,name,dept,save_numSAVE_NUM):capcv2.VideoCapture(0)ifcap.isOpened():time.sleep(2)# 先获取文件夹下的文件filesget_dir_file(COLLECT_LOCAL)# 循环几次i0# 存放目录中 有几个文件 从第几个开始begin_plen(files)iflen(files)0else0# 插入sql数据 先判断数据有没有这个人dfpd.read_sql(fselect * from employees where eid{eid},conengine)iflen(df)0:print(您已采集过人脸请勿重复采集)returnFalsewhileisave_num:ret,framecap.read()ifnotret:print(摄像头读取失败)breakgraycv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 加载opencv预训练的Haar特征人脸模型文件face_detectorcv2.CascadeClassifier(./data/models/haarcascade_frontalface_default.xml)facesface_detector.detectMultiScale(gray,1.1,10)# 遍历每一个人脸矩形框画出该框for(x,y,w,h)infaces:# 画矩形cv2.rectangle(frame,(x,y),(xw,yh),(0,255,0),2)# 取图片的人脸部分作为 数据集face_roigray[y:yh,x:xw]full_pathos.path.join(COLLECT_LOCALf{eid}-{begin_p1}.jpg)cv2.imwrite(full_path,face_roi)print(f采集员工编号为{eid}第{i1}张图片)i1begin_p1# 数据库无重复 即插入数据data{eid:[eid],name:[name],dept:[dept],face_id:[COLLECT_LOCALf{eid}-{begin_p1}.jpg]}add_sql(data,employees)cap.release()returnTrueelse:print(无法获取到摄像头)returnFalse# 训练模型deftrain_model():# 获取文件夹下的文件filesget_dir_file(COLLECT_LOCAL)# 存放数据集图片集imgs[]# 标签labels[]# 判断数量的计数器 当累加计数等于保存的数量时重置为0count0forfileinfiles:pathos.path.join(COLLECT_LOCALfile)graycv2.imread(path,0)imgs.append(gray)# 获取到对应人员的eideidfile.split(-)[0]labels.append(int(eid))count1ifcountSAVE_NUM:count0# 获得模型对象modelcv2.face.LBPHFaceRecognizer_create()# 训练模型 标签当前是python数组 要变为numpy数组model.train(imgs,np.array(labels))# 保存模型文件# print(imgs,imgs)# print(labels,labels)model.save(MODEL_LOCAL)print(模型训练完成)# 使用模型 进行人脸检测识别# user_img 是 打开传入的图片mode是 区分上下班打卡defmode_check(user_img,mode):# 先获得模型对象modelcv2.face.LBPHFaceRecognizer_create()# 模型加载训练的文件model.read(MODEL_LOCAL)# 模型预测 label是对应的标签 confidence是置信度越小越准确label,confidencemodel.predict(user_img)# 根据label中 对应的 eid 去数据库 获取用户信息dfpd.read_sql(fselect * from employees where eid{label},conengine)iflen(df)0:print(未找到该员工信息)returnFalseifconfidence60:# 检查是否重复打卡最近一小时内是否已有记录nowdatetime.datetime.now()recent_recordpd.read_sql(fSELECT * FROM attendance WHERE eid{label}AND ts {now-datetime.timedelta(minutes60)},conengine)iflen(recent_record)0:print(f员工{df[name][0]}已在{recent_record[ts][0]}打过卡请勿重复操作)returnFalsedata{eid:[df[eid][0]],ts:[datetime.datetime.now()],attendance_type:[mode],source:[camera_01]}add_sql(data,attendance)print(检测成功:,df[name][0])returnTrueelse:print(检测失败)returnFalse# 打卡启动defstart_scan_face(mode):# 打开摄像头capcv2.VideoCapture(0)ifcap.isOpened():whileTrue:ret,framecap.read()ifnotret:print(摄像头读取失败)cap.release()breakgraycv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 加载opencv预训练的Haar特征人脸模型文件face_detectorcv2.CascadeClassifier(./data/models/haarcascade_frontalface_default.xml)facesface_detector.detectMultiScale(gray,1.1,10)# 遍历每一个人脸矩形框画出该框for(x,y,w,h)infaces:# 画矩形cv2.rectangle(frame,(x,y),(xw,yh),(0,255,0),2)face_roigray[y:yh,x:xw]mode_check(face_roi,mode)cv2.imshow(img,frame)ifcv2.waitKey(100)0xFFord(q):cap.release()breakelse:print(无法获取到摄像头)returnFalse# 每日/每周自动生成出勤报表Pandas 处理# mode参数 day本日 week本周defcreate_work_report(mode):# 先获取今日 / 本周 的 数据库的考勤记录 获取到名字ifmodeday:dfpd.read_sql(select a.*,e.name from attendance as a left join employees e on e.eid a.eid where a.ts NOW() - INTERVAL 1 DAY AND a.ts NOW(),conengine)else:dfpd.read_sql(select a.*,e.name from attendance as a left join employees e on e.eid a.eid where YEARWEEK(a.ts) YEARWEEK(NOW()),conengine)# 按员工ID和日期分组统计上下班次数df[date]df[ts].dt.date# 提取日期# size()统计每个分组的记录数即每个员工在每周的上班 / 下班打卡次数# unstack(fill_value0)将 attendance_type打卡类型从 行索引 转换为 列名并填充缺失值为 0。# unstack()将多级索引的某一层“展开”为列。# fill_value0如果某些组合没有数据如某员工某周没有下班打卡则填充 0reportdf.groupby([eid,name,date,attendance_type]).size().unstack(fill_value0)# 重命名列可选reportreport.rename(columns{in:上班打卡次数,out:下班打卡次数,})filename./item/report/daily_report.csvifmodedayelse./item/report/week_report.csvreport.to_csv(filename)print(f生成{ 今日 if mode day else 本周 }报表成功)returnTrue# 可视化展示部门级考勤情况Matplotlibdefshow_draw():plt.rcParams[font.sans-serif][SimHei]# windows系统dfpd.read_sql(select a.*,e.dept from attendance a left join employees e on e.eid a.eid,conengine)dfgdf.groupby([dept,attendance_type]).size().unstack(fill_value0)dfpd.DataFrame(dfg.values,indexdfg.index,columns[上班打卡,下班打卡])# 2. 绘制分组柱状图df.plot(kindbar,# 柱状图figsize(8,5),# 图表大小宽×高color[skyblue,salmon],# 颜色in天蓝色out橙红色width0.7,# 柱子宽度0~1alpha0.8# 透明度0~1)# 3. 调整样式plt.title(各部门签到in与签退out人数对比,fontsize14)# 标题plt.xlabel(部门,fontsize12)# x轴标签plt.ylabel(人数,fontsize12)# y轴标签plt.xticks(rotation0)# x轴标签不旋转plt.legend(title考勤类型,fontsize10)# 图例plt.grid(axisy,linestyle--,alpha0.7)# y轴网格线plt.tight_layout()# 自动调整布局plt.savefig(./item/report/show_draw.png)# 4. 显示图表plt.show()# 使用 ReportLab 生成可导出的 PDF 报告。defcreate_pdf_report():# 解决中文乱码fontTTFont(SimHei,c:/Windows/Fonts/simhei.ttf)# windows系统pdfmetrics.registerFont(font)# 创建一个文档对象docSimpleDocTemplate(./item/report/pdf_report.pdf)# 通过它获取已设置好的简单样式stylesgetSampleStyleSheet()styles[Normal].fontNameSimHei# windowsstyles[h1].fontNameSimHei# windowsstyles[h2].fontNameSimHei# windows# 存放内容的列表story[]# 创建一级标题段落p_titleParagraph(考勤报告,styles[h1])story.append(p_title)# 创建正文p_bodyParagraph(本报告展示了对人脸考勤系统的数据分析,styles[Normal])story.append(p_body)p_title2Paragraph(以下是部门级考勤情况,styles[h2])story.append(p_title2)# 绘制图片imgImage(./item/report/show_draw.png,width4*inch,height2.5*inch)story.append(img)doc.build(story)print(PDF报告已生成)if__name____main__:print(-----------欢迎使用考勤打卡系统-----------)input_keyint(input(1.采集人脸信息\n2.开始训练模型\n3.开启上班时间打卡\n4.开启下班时间打卡\n5.生成今日或者本周报表\n6.展示部门级考勤情况\n7.生成PDF报告))ifinput_key1:eidint(input(请输入要录入的员工号))nameinput(f请输入员工号{eid}的员工姓名)deptinput(f请输入员工号{eid}的部门)capture_face_for_training(eid,name,dept)elifinput_key2:train_model()elifinput_key3:start_scan_face(in)elifinput_key4:start_scan_face(out)elifinput_key5:tipinput(今日报表请输入day本周报表请输入week)create_work_report(tip)elifinput_key6:show_draw()elifinput_key7:create_pdf_report()区分点:haarcascade_frontalface_default.xml人脸检测用于在图像或视频中定位人脸区域即“在哪里有人脸”。输出返回人脸的矩形坐标如(x, y, w, h)不涉及身份识别。示例在摄像头画面中框出所有人脸但无法区分是谁。LBPHFaceRecognizer_trainLBPH算法人脸识别在已知人脸区域的基础上识别具体身份即“这是谁”。输出返回匹配的标签如姓名或ID和置信度。示例在检测到的人脸中识别出“张三”或“李四”。思路我用haar在注册用户时先截取人脸照片并插入数据到数据库注册完成后开始打卡使用LBPH算法验证这个照片的标签值从而拿到标签去数据库查询对应的人。