diff --git a/ruoyi-fastapi-backend/003测试海康url.py b/ruoyi-fastapi-backend/003测试海康url.py new file mode 100644 index 0000000..be27311 --- /dev/null +++ b/ruoyi-fastapi-backend/003测试海康url.py @@ -0,0 +1,11 @@ +from urllib.parse import urlparse + +url = "https://192.168.10.251:8001/artemis/test_v1" + +parsed = urlparse(url) + +path_and_query = parsed.path +if parsed.query: + path_and_query += "?" + parsed.query + +print(path_and_query) \ No newline at end of file diff --git a/ruoyi-fastapi-backend/006测试定时任务.py b/ruoyi-fastapi-backend/006测试定时任务.py new file mode 100644 index 0000000..1bd35ad --- /dev/null +++ b/ruoyi-fastapi-backend/006测试定时任务.py @@ -0,0 +1,25 @@ +from apscheduler.schedulers.blocking import BlockingScheduler +from apscheduler.triggers.cron import CronTrigger +import datetime +import time + +def my_job(): + print("任务执行时间:", datetime.datetime.now()) + +if __name__ == '__main__': + scheduler = BlockingScheduler(timezone="Asia/Shanghai") + + # # 每天凌晨 1 点执行 + # scheduler.add_job(my_job, trigger="cron", hour=1, minute=0) + + # 或者用 CronTrigger + trigger = CronTrigger(minute='*', second=0) + scheduler.add_job(my_job, trigger=trigger) + + scheduler.start() + + try: + while True: + time.sleep(1) + except (KeyboardInterrupt, SystemExit): + scheduler.shutdown() \ No newline at end of file diff --git a/ruoyi-fastapi-backend/007测试python文件.py b/ruoyi-fastapi-backend/007测试python文件.py new file mode 100644 index 0000000..9b53e56 --- /dev/null +++ b/ruoyi-fastapi-backend/007测试python文件.py @@ -0,0 +1,5 @@ +import os + +txt_path = "./test/image_base64.txt" +print(os.path.dirname(txt_path)) +# os.makedirs(, exist_ok=True) \ No newline at end of file diff --git a/ruoyi-fastapi-backend/compreface_face_detection.json b/ruoyi-fastapi-backend/compreface_face_detection.json new file mode 100644 index 0000000..58b1125 --- /dev/null +++ b/ruoyi-fastapi-backend/compreface_face_detection.json @@ -0,0 +1,18 @@ +{ + "result": [ + { + "pose": { + "pitch": -13.898311223378556, + "roll": 0.8241647740114217, + "yaw": 0.30893387084981927 + }, + "box": { + "probability": 0.9989770650863647, + "x_max": 874, + "y_max": 1127, + "x_min": 375, + "y_min": 418 + } + } + ] +} \ No newline at end of file diff --git a/ruoyi-fastapi-backend/compreface_face_recognition.json b/ruoyi-fastapi-backend/compreface_face_recognition.json new file mode 100644 index 0000000..aed763c --- /dev/null +++ b/ruoyi-fastapi-backend/compreface_face_recognition.json @@ -0,0 +1,33 @@ +{ + "result": [ + { + "gender": { + "probability": 1.0, + "value": "male" + }, + "box": { + "probability": 0.99898, + "x_max": 874, + "y_max": 1127, + "x_min": 375, + "y_min": 418 + }, + "subjects": [ + { + "subject": "刘昊天_访客", + "similarity": 0.99991 + } + ], + "execution_time": { + "gender": 3.0, + "detector": 32.0, + "calculator": 9.0 + } + } + ], + "plugins_versions": { + "gender": "insightface.GenderDetector", + "detector": "insightface.FaceDetector@retinaface_r50_v1", + "calculator": "insightface.Calculator@arcface-r100-msfdrop75" + } +} \ No newline at end of file diff --git a/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py b/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py new file mode 100644 index 0000000..229d923 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py @@ -0,0 +1,79 @@ +""" + 系统定时任务 +""" + +from config.get_scheduler import scheduler +from apscheduler.triggers.cron import CronTrigger +from apscheduler.triggers.interval import IntervalTrigger +from module_admin.service.haikang_service import HaiKangService +from module_admin.service.compreface_service import ComprefaceService +from utils.log_util import logger + + +# 定时获取访客图片并上传compreface +async def download_visitor_image_and_upload_compreface(): + """ + 定时任务:获取海康访客图片并上传到compreface进行人脸识别 + 同时删除已过期的访客人脸数据 + 执行频率:每2分钟一次 + """ + try: + logger.info('开始执行定时任务:获取访客图片并上传compreface') + + # 获取所有有效状态的访客图片(visitorStatus=1表示正常状态) + visitor_name_list = await HaiKangService.get_all_visitor_pictures(visitorStatus=1) + logger.info(f'获取到 {len(visitor_name_list)} 张有效访客图片') + + # 如果有新的访客图片,将访客图片上传到compreface + if visitor_name_list: + result = await ComprefaceService.face_addition_batch_service(visitor_name_list) + logger.info(f'访客图片上传compreface结果: {result}') + else: + logger.info('没有新的访客图片需要处理') + + # 删除过期的访客人脸数据 + # 从visitor_name_list中提取所有访客的名字 + active_visitor_names = [name for _, name in visitor_name_list] if visitor_name_list else [] + + logger.info(f'开始删除过期访客人脸,当前有效访客数量: {len(active_visitor_names)}') + delete_result = await ComprefaceService.delete_expired_visitor_faces_service(active_visitor_names) + + if delete_result.get('error'): + logger.error(f'删除过期访客失败: {delete_result.get("error")}') + else: + logger.info( + f'删除过期访客完成 - 成功删除: {delete_result.get("deleted_count")} 个, ' + f'失败: {delete_result.get("failed_count")} 个' + ) + if delete_result.get('deleted_subjects'): + logger.info(f'已删除的访客: {", ".join(delete_result.get("deleted_subjects"))}') + if delete_result.get('failed_subjects'): + logger.warning(f'删除失败的访客: {delete_result.get("failed_subjects")}') + + logger.info('定时任务执行完成:获取访客图片并上传compreface') + except Exception as e: + logger.error(f'定时任务执行失败:获取访客图片并上传compreface - {str(e)}', exc_info=True) + + +# 初始化定时任务 +def init_scheduled_tasks(): + """ + 初始化所有定时任务 + 在应用启动时调用此方法 + """ + # 添加访客图片上传任务,每2分钟执行一次 + scheduler.add_job( + func=download_visitor_image_and_upload_compreface, + trigger=IntervalTrigger(minutes=2), + id='download_visitor_image_task', + name='定时获取访客图片并上传compreface', + replace_existing=True, + max_instances=1, # 同一时间只允许一个实例运行 + ) + logger.info('定时任务已添加:download_visitor_image_and_upload_compreface (每2分钟执行一次)') + + + + + + diff --git a/ruoyi-fastapi-backend/module_admin/service/compreface_service.py b/ruoyi-fastapi-backend/module_admin/service/compreface_service.py new file mode 100644 index 0000000..21f9a53 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/compreface_service.py @@ -0,0 +1,130 @@ +from utils.compreface_util import ComprefaceUtil +from config.env import ComprefaceConfig + +class ComprefaceService(ComprefaceUtil): + + """compreface服务类""" + + # 人脸检测 + @classmethod + async def face_detection_service(cls, image, options: dict = {}): + """ 人脸检测 + + Args: + image_path (str): 图片路径 + options (dict, optional): 检测参数. Defaults to {}. + + Returns: + [type]: [description] + """ + options={ + "limit": 0, + "det_prob_threshold": 0.8, + "prediction_count": 1, + # 可选参数 age,gender,landmarks,calculator + "face_plugins": "pose", + "status": "false", + } + result = await ComprefaceUtil.face_detection(image, options) + return result["result"] + + # 人脸识别 + @classmethod + async def face_recognition_service(cls, image, options: dict = {}): + """ 人脸识别 + + Args: + image_path (str): 图片路径 + options (dict, optional): 参数. Defaults to {}. + + Returns: + [type]: [description] + """ + result = await ComprefaceUtil.face_recognition(image, options) + subject = result.get('result')[0].get('subjects')[0] + name = subject.get('subject') + similarity = subject.get('similarity') + if similarity < ComprefaceConfig.COMPREFACE_SIMILARITY_THRESHOLD: + return { + "name": "未知", + "role": "陌生人" + } + else: + t = name.split("_") + return { + "name": t[0], + "role": t[1] if len(t) > 1 else "员工" + } + + # 添加图像到人脸库 + @classmethod + async def face_addition_service(cls, image, name): + result = await ComprefaceUtil.face_addition(image, name) + return result + + # 批量添加图像到人脸库 + @classmethod + async def face_addition_batch_service(cls, t): + result_list = [] + for image, name in t: + result = await ComprefaceUtil.face_addition(image, name) + result_list.append(result) + return result_list + + # 删除过期的访客人脸 + @classmethod + async def delete_expired_visitor_faces_service(cls, active_visitor_names: list): + """删除CompreFace中已过期的访客人脸数据 + + Args: + active_visitor_names (list): 当前有效的访客名字列表 + + Returns: + dict: 包含删除成功和失败的subject列表 + """ + try: + # 获取CompreFace中所有的subjects + all_subjects_result = await ComprefaceUtil.get_all_subjects() + all_subjects = all_subjects_result.get('subjects', []) + + deleted_subjects = [] + failed_subjects = [] + + # 遍历所有subjects,删除不在active_visitor_names中的访客 + for subject in all_subjects: + # 假设访客的subject格式为 "姓名_访客" 或 "姓名" + # 只删除访客类型的人脸,员工人脸不删除 + if subject not in active_visitor_names: + # 如果subject不在活跃访客列表中,且不是员工(员工一般包含"_员工"等标识) + # 这里假设访客没有特殊后缀,或者可以根据实际情况调整判断逻辑 + # 如果subject包含"_员工"等标识,则跳过 + if "_员工" in subject or "_职员" in subject or "_工作人员" in subject: + continue + + try: + # 删除该subject + await ComprefaceUtil.delete_subject(subject) + deleted_subjects.append(subject) + except Exception as e: + failed_subjects.append({"subject": subject, "error": str(e)}) + + return { + "deleted_count": len(deleted_subjects), + "deleted_subjects": deleted_subjects, + "failed_count": len(failed_subjects), + "failed_subjects": failed_subjects + } + except Exception as e: + return { + "error": f"删除过期访客失败: {str(e)}", + "deleted_count": 0, + "deleted_subjects": [], + "failed_count": 0, + "failed_subjects": [] + } + + + + + + diff --git a/ruoyi-fastapi-backend/utils/haikang_util.py b/ruoyi-fastapi-backend/utils/haikang_util.py index eeeae3b..3b332cb 100644 --- a/ruoyi-fastapi-backend/utils/haikang_util.py +++ b/ruoyi-fastapi-backend/utils/haikang_util.py @@ -492,7 +492,7 @@ class HaikangUtil: os.makedirs(save_path, exist_ok=True) name = save_path.split('/')[-1] - save_path = os.path.join(save_path, name+".jpg") + save_path = os.path.join(save_path, name+"_访客"+".jpg") with open(save_path, 'wb') as f: for chunk in back.iter_bytes(chunk_size=8192):