58 Commits

Author SHA1 Message Date
397a6925f7 【更新/修复】首次提交及修复保存的数据命名导致的最终人脸识别过程中闪退的现象 2025-10-22 17:26:41 +08:00
fb96e47429 Merge pull request #71 from coni233/master
修复了录入中文名的若干问题
2024-12-10 19:21:56 +08:00
8c855a79e3 Update features_extraction_to_csv.py
修正读取图片时中文路径的问题
2024-11-15 14:33:44 +08:00
fd6c4d9e20 Update get_faces_from_camera_tkinter.py
修复pillow库保存时色彩通道不对的问题
2024-11-15 14:28:25 +08:00
f01fefe64a Update features_extraction_to_csv.py
修正中文名会显示乱码的问题
2024-11-14 22:14:30 +08:00
edaedbe285 Update get_faces_from_camera_tkinter.py
修正了中文名字导致无法保存图像的问题,这个因为OpenCV默认不支持非ASCII字符路径,改为用Pillow保存图像
在Step:1中新增了“更改”和“删除”按钮,“更改”在输入已存在的名字时,点击保存会在已存在的名字目录下添加新照片“删除”在输入已存在的名字时会删除该名字的目录。
保存路径更改为”person_输入的人名“以适应更多情况
新增对没有摄像头的电脑检测和提示
修正因显示fps位数不同而原因导致的界面抖动
2024-11-14 22:13:38 +08:00
13ccabf616 Merge pull request #51 from coneypo/dependabot/pip/numpy-1.22.0
Bump numpy from 1.21.3 to 1.22.0
2022-08-19 12:26:53 +08:00
cd652f5bdd Bump numpy from 1.21.3 to 1.22.0
Bumps [numpy](https://github.com/numpy/numpy) from 1.21.3 to 1.22.0.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.21.3...v1.22.0)

---
updated-dependencies:
- dependency-name: numpy
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-21 22:50:32 +00:00
1571d81764 Fix issue of videoCapture
Should be "cv2.VideoCapture(0)"
2022-01-07 11:34:44 +08:00
ed1e99b769 Update README.rst 2021-11-12 14:27:48 +08:00
636aa84aaf Update README.rst 2021-11-12 14:15:15 +08:00
b77a45ab97 Update README.rst 2021-11-12 14:13:40 +08:00
ab09b7ecbb Update README.rst 2021-11-12 14:13:02 +08:00
97b85ac107 Modify var name when tkinter GUI 2021-11-12 14:01:51 +08:00
932f8bcb9d [Bug fix] Extract failed when empty face folder 2021-11-11 13:25:51 +08:00
9bde8b4985 1. New GUI for face register with Tkinter, support set name when saving
faces;
2. `features_all.csv` modified to n x 129, 129D will be person_name + 128D features;

Signed-off-by: Zhengtian Xie <coneypo@gmail.com>
2021-11-09 15:55:27 +08:00
7dc1071df4 1. Update requirements.txt
2. Update the fps refresh period to 1s
2021-10-28 08:51:42 +08:00
8e8a0032c4 Update repo structure
1. Add performance counter for `face_descriptor_from_camera.py`
2. Rename `face_reco_from_camera_ot_single_person.py` to `face_reco_from_camera_single_face.py`, remove OT in it
3. Update readme
2021-08-16 10:25:30 +08:00
93bb154c8a Remove '.idea/' generated by pycharm
Signed-off-by: coneypo <coneypo@gmail.com>
2021-06-04 14:03:25 +08:00
05b78489a7 1. Using logging to set log level
2. Fix bug in OT with multi-people
3. Set 'reclassify_interval' to do re-classify for OT with multi-people

Signed-off-by: coneypo <coneypo@gmail.com>
2021-06-04 13:51:46 +08:00
8eaad06adc show chinese name in OT script 2021-01-25 11:25:40 +08:00
4e4553d5e9 Show chinese name with OT 2021-01-14 14:58:31 +08:00
e9008e3ad3 Add 're-classification feature for single-person' 2020-12-14 11:50:06 +08:00
0f5adfd5cf remove unused statement 2020-09-16 10:40:31 +08:00
8a4fb563cd add MIT license 2020-09-15 17:09:18 +08:00
2c1b6416af use OT to improve FPS 2020-09-03 15:34:26 +08:00
65c9ec0caf test 2020-08-19 23:19:21 +08:00
3313d91414 push from gitlab test 2020-07-03 13:56:05 +08:00
2b88597aee push from gitlab 2020-07-03 13:26:50 +08:00
e2698f7ae8 test 2020-07-03 11:48:58 +08:00
e9b923a864 supporting names in chinese 2020-05-29 10:34:39 +08:00
7599f86d07 remove chinese character
Some issuse in https://github.com/coneypo/Dlib_face_recognition_from_camera/pull/18;
Reset to old version adn will update when the issue fixed;
2020-05-18 17:53:36 +08:00
fefd44a54c Merge pull request #18 from TianleLin/master
Mod: use PIL to make Chinese title availble
2020-05-13 10:42:27 +08:00
c57db57dfd Mod: use PIL to make Chinese title availble 2020-05-13 10:33:38 +08:00
baaf84226a add video stream fps log 2020-04-19 21:23:02 +08:00
64d797efcb define class 'Face_register' and 'Face_recognizer' 2020-04-19 20:04:11 +08:00
3ca94d6d96 remove cvtColor 2020-04-02 17:41:45 +08:00
262609f91f use 'iloc' instead of 'ix' to fix the bug of pandas version 2020-02-27 07:51:15 +08:00
bd5a4034f4 remove landmarks_5 dat 2019-12-21 01:13:40 +08:00
a0a070ce3a Delete useless code 2019-11-20 00:26:31 +08:00
13b6807441 add the info for "cap.set" 2019-11-19 15:00:55 +08:00
fd13a6b7ec Update get_faces_from_camera.py 2019-11-19 14:56:42 +08:00
c80b1d19b8 Update get_faces_from_camera.py 2019-11-19 14:41:28 +08:00
7e85411ae3 update readme
please install some python packages if needed
2019-08-28 10:36:58 +08:00
08deb0d608 Intro to algorithm and the way to customize names
1. Breif introduction of face reco algorithm: ResNet;
2. Add the patch to customize names instead of "Person 1", "Person 2"...
2019-04-26 15:41:35 +08:00
910ddcfe22 fix the bug the person>10 2019-04-12 15:41:38 +08:00
61d2406de2 fix the bug when people > 10 2019-04-12 14:36:05 +08:00
892c20b429 modify the default setting for save faces 2019-04-12 09:31:32 +08:00
9307bf0b76 face compare with min e_distance and modify overview.png 2019-04-09 11:43:15 +08:00
75e08ab0bf remove personX.csv and fix the bug when no faces for personX 2019-04-05 20:11:10 +08:00
dcd3eaabc7 update readme 2019-03-24 11:28:27 +08:00
8479d60f55 modify readme.rst 2019-03-23 22:53:36 +08:00
922bf93706 add customize name 2019-03-23 22:50:40 +08:00
28b498ea03 add requirements.txt 2019-03-23 22:45:38 +08:00
9b548be9a6 update readme 2019-03-23 22:27:47 +08:00
c1d048ac92 uncomment imshow 2019-03-22 09:42:22 +08:00
7a9191d690 add some comments in english 2019-03-21 22:29:52 +08:00
ecae353dde add the description of how to customize the names 2019-03-21 21:23:27 +08:00
90 changed files with 1341 additions and 1057 deletions

5
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

View File

@ -1,12 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (Dlib_face_recognition_from_camera) (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="projectConfiguration" value="py.test" />
<option name="PROJECT_TEST_RUNNER" value="py.test" />
</component>
</module>

4
.idea/encodings.xml generated
View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@ -1,12 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E501" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

5
.idea/misc.xml generated
View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
<component name="Black">
<option name="sdkName" value="Python 3.10 (Dlib_face_recognition_from_camera) (2)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (Dlib_face_recognition_from_camera) (2)" project-jdk-type="Python SDK" />
</project>

1
.idea/vcs.xml generated
View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

411
.idea/workspace.xml generated
View File

@ -1,411 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="39c25e36-24e3-48b8-a41f-0d53e408e855" name="Default" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.rst" beforeDir="false" afterPath="$PROJECT_DIR$/README.rst" afterDir="false" />
<change beforePath="$PROJECT_DIR$/face_reco_from_camera.py" beforeDir="false" afterPath="$PROJECT_DIR$/face_reco_from_camera.py" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/get_faces_from_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="176">
<caret line="172" selection-start-line="172" selection-end-line="172" />
<folding>
<element signature="e#277#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/get_features_into_CSV.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="270">
<caret line="56" column="24" selection-start-line="56" selection-start-column="24" selection-end-line="56" selection-end-column="24" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/data/features_all.csv">
<provider selected="true" editor-type-id="csv-text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/face_reco_from_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="195">
<caret line="96" selection-start-line="96" selection-end-line="96" />
<folding>
<element signature="e#201#234#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/README.rst">
<provider selected="true" editor-type-id="restructured-text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/how_to_use_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="27" column="13" selection-start-line="27" selection-start-column="13" selection-end-line="27" selection-end-column="13" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>rects</find>
<find>i</find>
<find>dets</find>
<find>cnt_p</find>
<find>im_rd</find>
<find>predictor</find>
<find></find>
<find></find>
<find>pre_clear</find>
<find>np</find>
<find>path_make_dir</find>
<find>path_csvs_from_photos</find>
<find>path_faces_rd</find>
<find>path_csv</find>
<find>path_csv_from_photos_rd</find>
<find>feature_mean</find>
<find>dir_pics</find>
<find>current_face_dir</find>
<find>person_cnt</find>
<find>cnt_ss</find>
</findStrings>
<replaceStrings>
<replace>feature_num</replace>
<replace>faces</replace>
<replace>cnt_ss</replace>
<replace>,</replace>
<replace>img_rd</replace>
<replace>:</replace>
<replace>pre_work</replace>
<replace>path_csvs_from_photos</replace>
<replace>path_photos_from_camera</replace>
<replace>path_csv_from_photos</replace>
<replace>feature_mean_list</replace>
<replace>photos_list</replace>
</replaceStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/use_camera.py" />
<option value="$PROJECT_DIR$/how_to_use_camera.py" />
<option value="$PROJECT_DIR$/get_features_into_CSV.py" />
<option value="$PROJECT_DIR$/test.py" />
<option value="$PROJECT_DIR$/get_faces_from_camera.py" />
<option value="$PROJECT_DIR$/README.rst" />
<option value="$PROJECT_DIR$/face_reco_from_camera.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="438" />
<option name="y" value="28" />
<option name="width" value="1717" />
<option name="height" value="987" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="Dlib_face_recognition_from_camera" type="b2602c69:ProjectViewProjectNode" />
<item name="Dlib_face_recognition_from_camera" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="Dlib_face_recognition_from_camera" type="b2602c69:ProjectViewProjectNode" />
<item name="Dlib_face_recognition_from_camera" type="462c0819:PsiDirectoryNode" />
<item name="data" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="PyConsoleOptionsProvider">
<option name="myPythonConsoleState">
<console-settings is-module-sdk="true">
<option name="myUseModuleSdk" value="true" />
</console-settings>
</option>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Python.face_reco_from_camera">
<configuration name="face_reco_from_camera" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="Dlib_face_recognition_from_camera" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/face_reco_from_camera.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="get_faces_from_camera" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="Dlib_face_recognition_from_camera" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/get_faces_from_camera.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="get_features_into_CSV" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="Dlib_face_recognition_from_camera" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/get_features_into_CSV.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="how_to_use_camera" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="Dlib_face_recognition_from_camera" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/how_to_use_camera.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="Dlib_face_recognition_from_camera" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<list>
<item itemvalue="Python.face_reco_from_camera" />
<item itemvalue="Python.get_faces_from_camera" />
<item itemvalue="Python.get_features_into_CSV" />
<item itemvalue="Python.how_to_use_camera" />
<item itemvalue="Python.test" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.face_reco_from_camera" />
<item itemvalue="Python.get_features_into_CSV" />
<item itemvalue="Python.get_faces_from_camera" />
<item itemvalue="Python.test" />
<item itemvalue="Python.how_to_use_camera" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="39c25e36-24e3-48b8-a41f-0d53e408e855" name="Default" comment="" />
<created>1538622047029</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1538622047029</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="0" y="27" width="1920" height="988" extended-state="6" />
<editor active="true" />
<layout>
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.13502109" />
<window_info id="Structure" order="1" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info active="true" anchor="bottom" id="Run" order="2" visible="true" weight="0.035252646" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.39952996" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Version Control" order="7" />
<window_info anchor="bottom" id="Terminal" order="8" weight="0.4620462" />
<window_info anchor="bottom" id="Event Log" order="9" side_tool="true" />
<window_info anchor="bottom" id="Python Console" order="10" />
<window_info anchor="right" id="Commander" order="0" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<default-breakpoints>
<breakpoint type="python-exception">
<properties notifyOnTerminate="true" exception="BaseException">
<option name="notifyOnTerminate" value="true" />
</properties>
</breakpoint>
</default-breakpoints>
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/use_camera.py" />
<entry file="file://$PROJECT_DIR$/patch" />
<entry file="file://$PROJECT_DIR$/README.md" />
<entry file="file://$PROJECT_DIR$/data/data_csvs_from_camera/person_1.csv" />
<entry file="file://$PROJECT_DIR$/data/data_csvs_from_camera/person_2.csv" />
<entry file="file://$PROJECT_DIR$/data/data_faces_from_camera/person_6/img_face_1.jpg" />
<entry file="file://$PROJECT_DIR$/test.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="28" selection-start-line="2" selection-start-column="6" selection-end-line="2" selection-end-column="28" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.rst">
<provider selected="true" editor-type-id="restructured-text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/how_to_use_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="27" column="13" selection-start-line="27" selection-start-column="13" selection-end-line="27" selection-end-column="13" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/data/features_all.csv">
<provider selected="true" editor-type-id="csv-text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/get_features_into_CSV.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="270">
<caret line="56" column="24" selection-start-line="56" selection-start-column="24" selection-end-line="56" selection-end-column="24" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/get_faces_from_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="176">
<caret line="172" selection-start-line="172" selection-end-line="172" />
<folding>
<element signature="e#277#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/face_reco_from_camera.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="195">
<caret line="96" selection-start-line="96" selection-end-line="96" />
<folding>
<element signature="e#201#234#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2021 coneypo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

0
README.md Normal file
View File

View File

@ -1,109 +0,0 @@
Face recognition from camera
############################
Introduction
************
Detect and recognize single/multi-faces from camera;
调用摄像头进行人脸识别,支持多张人脸同时识别;
#. 摄像头人脸录入 / Face register
.. image:: introduction/get_face_from_camera.png
:align: center
#. 提取特征建立人脸数据库 / Generate database from images captured
#. 利用摄像头进行人脸识别 / Face recognizer
当单张人脸 / When single-face:
.. image:: introduction/face_reco_single_person.png
:align: center
当多张人脸 / When multi-faces:
.. image:: introduction/face_reco_two_people.png
:align: center
** 关于精度 / About accuracy:
* When using a distance threshold of ``0.6``, the dlib model obtains an accuracy of ``99.38%`` on the standard LFW face recognition benchmark.
Steps
*****
#. 进行 face register / 人脸信息采集录入
.. code-block:: python
python3 get_face_from_camera.py
#. 提取所有录入人脸数据存入 features_all.csv
.. code-block:: python
python3 get_features_into_CSV.py
#. 调用摄像头进行实时人脸识别
.. code-block:: python
python3 face_reco_from_camera.py
About Source Code
*****************
Python 源码介绍如下:
#. get_face_from_camera.py:
进行 Face register / 人脸信息采集录入
* 请注意存储人脸图片时,矩形框不要超出摄像头范围,要不然无法保存到本地;
* 超出会有 "out of range" 的提醒;
#. get_features_into_CSV.py:
从上一步存下来的图像文件中,提取人脸数据存入CSV;
* 会生成一个存储所有特征人脸数据的 "features_all.csv";
* size: n*128 , n means n people you registered and 128 means 128D features of the face
#. face_reco_from_camera.py:
这一步将调用摄像头进行实时人脸识别; / This part will implement real-time face recognition;
* Compare the faces captured from camera with the faces you have registered which are saved in "features_all.csv"
* 将捕获到的人脸数据和之前存的人脸数据进行对比计算欧式距离, 由此判断是否是同一个人;
More
****
Tips:
1. Windows下建议不要把代码放到 ``C:\``, 可能会出现权限读取问题
2. 代码最好不要有中文路径
3. 人脸录入的时候先建文件夹再保存图片, 先 ``N````S``
For more details, please refer to my blog (in chinese) or mail to me /
可以访问我的博客获取本项目的更详细介绍,如有问题可以邮件联系我:
* Blog: https://www.cnblogs.com/AdaminXie/p/9010298.html
* Mail: coneypo@foxmail.com
仅限于交流学习, 商业合作勿扰;
Thanks for your support.

View File

0
data/data_dlib/shape_predictor_68_face_landmarks.dat Normal file → Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

1
data/features_all.csv Normal file
View File

@ -0,0 +1 @@
马斌昊,-0.0899050161242485,0.02384429331868887,0.10691650211811066,-0.026448555290699005,-0.12473130598664284,-0.026039691641926765,-0.10633478686213493,-0.10004803165793419,0.0756474919617176,-0.09969355911016464,0.2548818141222,-0.02838892675936222,-0.18727628886699677,-0.04292612988501787,-0.038223471492528915,0.14358164370059967,-0.13197344541549683,-0.08303523808717728,-0.04544464126229286,0.005225636065006256,0.05400150641798973,0.016614584252238274,0.04487479291856289,0.0208868607878685,-0.12604008615016937,-0.3497048169374466,-0.1073501706123352,-0.08952881768345833,-0.058808863162994385,-0.007420409703627229,-0.07389594614505768,0.027185998857021332,-0.14693009108304977,0.032583920285105705,0.002864556387066841,0.08570835366845131,-0.030903122387826443,-0.09934812411665916,0.13845199346542358,-0.052308812737464905,-0.2402488812804222,0.03694266080856323,0.07542015612125397,0.1459449827671051,0.2269286960363388,0.014682859182357788,0.05472549982368946,-0.11605571955442429,0.06804285570979118,-0.17394205182790756,0.028083041310310364,0.10427030548453331,0.05113978683948517,0.05636468715965748,0.0043844198808074,-0.14433760941028595,-0.00407144520431757,0.12244492024183273,-0.09491624310612679,-0.015221940353512764,0.024541491642594337,-0.07212522253394127,-0.001256248913705349,-0.08840961009263992,0.22439686954021454,0.11492674425244331,-0.15236739814281464,-0.1425611972808838,0.06785859912633896,-0.18730811774730682,-0.07806657627224922,0.07729200273752213,-0.1676863580942154,-0.21197973936796188,-0.32569898664951324,0.027618836611509323,0.39188095927238464,0.10446011275053024,-0.17949597537517548,0.014993381686508656,-0.06068217195570469,0.00010182708501815796,0.09084133803844452,0.1250796541571617,-0.021716125309467316,0.008610536344349384,-0.11595302447676659,-0.0045088487677276134,0.27449268102645874,-0.07868175581097603,-0.0018516536802053452,0.2252117618918419,-0.00012384727597236633,0.06658063270151615,0.010239068418741226,0.06843071430921555,-0.024587254971265793,0.015095856040716171,-0.10306384414434433,0.04500553756952286,-0.013208229094743729,-0.07549317926168442,-0.024796511977910995,0.06462816521525383,-0.1793217957019806,0.07073099911212921,-0.008851969614624977,-0.003468985203653574,0.019725533202290535,-0.03128764219582081,-0.11080201715230942,-0.05722355656325817,0.15097910165786743,-0.24694078415632248,0.1483754888176918,0.183498315513134,0.1129513792693615,0.17476002871990204,0.13292384147644043,0.095770925283432,-0.055514756590127945,-0.03940589725971222,-0.17228589206933975,0.006599367829039693,0.07312680780887604,-0.0077499039471149445,0.07860656827688217,0.03986034169793129
1 马斌昊 -0.0899050161242485 0.02384429331868887 0.10691650211811066 -0.026448555290699005 -0.12473130598664284 -0.026039691641926765 -0.10633478686213493 -0.10004803165793419 0.0756474919617176 -0.09969355911016464 0.2548818141222 -0.02838892675936222 -0.18727628886699677 -0.04292612988501787 -0.038223471492528915 0.14358164370059967 -0.13197344541549683 -0.08303523808717728 -0.04544464126229286 0.005225636065006256 0.05400150641798973 0.016614584252238274 0.04487479291856289 0.0208868607878685 -0.12604008615016937 -0.3497048169374466 -0.1073501706123352 -0.08952881768345833 -0.058808863162994385 -0.007420409703627229 -0.07389594614505768 0.027185998857021332 -0.14693009108304977 0.032583920285105705 0.002864556387066841 0.08570835366845131 -0.030903122387826443 -0.09934812411665916 0.13845199346542358 -0.052308812737464905 -0.2402488812804222 0.03694266080856323 0.07542015612125397 0.1459449827671051 0.2269286960363388 0.014682859182357788 0.05472549982368946 -0.11605571955442429 0.06804285570979118 -0.17394205182790756 0.028083041310310364 0.10427030548453331 0.05113978683948517 0.05636468715965748 0.0043844198808074 -0.14433760941028595 -0.00407144520431757 0.12244492024183273 -0.09491624310612679 -0.015221940353512764 0.024541491642594337 -0.07212522253394127 -0.001256248913705349 -0.08840961009263992 0.22439686954021454 0.11492674425244331 -0.15236739814281464 -0.1425611972808838 0.06785859912633896 -0.18730811774730682 -0.07806657627224922 0.07729200273752213 -0.1676863580942154 -0.21197973936796188 -0.32569898664951324 0.027618836611509323 0.39188095927238464 0.10446011275053024 -0.17949597537517548 0.014993381686508656 -0.06068217195570469 0.00010182708501815796 0.09084133803844452 0.1250796541571617 -0.021716125309467316 0.008610536344349384 -0.11595302447676659 -0.0045088487677276134 0.27449268102645874 -0.07868175581097603 -0.0018516536802053452 0.2252117618918419 -0.00012384727597236633 0.06658063270151615 0.010239068418741226 0.06843071430921555 -0.024587254971265793 0.015095856040716171 -0.10306384414434433 0.04500553756952286 -0.013208229094743729 -0.07549317926168442 -0.024796511977910995 0.06462816521525383 -0.1793217957019806 0.07073099911212921 -0.008851969614624977 -0.003468985203653574 0.019725533202290535 -0.03128764219582081 -0.11080201715230942 -0.05722355656325817 0.15097910165786743 -0.24694078415632248 0.1483754888176918 0.183498315513134 0.1129513792693615 0.17476002871990204 0.13292384147644043 0.095770925283432 -0.055514756590127945 -0.03940589725971222 -0.17228589206933975 0.006599367829039693 0.07312680780887604 -0.0077499039471149445 0.07860656827688217 0.03986034169793129

375
face_reco_from_camera.py Normal file → Executable file
View File

@ -1,157 +1,266 @@
# 摄像头实时人脸识别
# Copyright (C) 2018-2021 coneypo
# SPDX-License-Identifier: MIT
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
import dlib
import numpy as np
import cv2
import pandas as pd
import os
import time
import logging
from PIL import Image, ImageDraw, ImageFont
# Created at 2018-05-11
# Updated at 2019-02-26
import dlib # 人脸处理的库 Dlib
import numpy as np # 数据处理的库 numpy
import cv2 # 图像处理的库 OpenCv
import pandas as pd # 数据处理的库 Pandas
# 人脸识别模型,提取128D的特征矢量
# face recognition model, the object maps human faces into 128D vectors
facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
# 计算两个128D向量间的欧式距离
def return_euclidean_distance(feature_1, feature_2):
feature_1 = np.array(feature_1)
feature_2 = np.array(feature_2)
dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
print("e_distance: ", dist)
if dist > 0.4:
return "diff"
else:
return "same"
# 处理存放所有人脸特征的 csv
path_features_known_csv = "data/features_all.csv"
csv_rd = pd.read_csv(path_features_known_csv, header=None)
# 存储的特征人脸个数
# print(csv_rd.shape[0])
# 用来存放所有录入人脸特征的数组
features_known_arr = []
# 读取已知人脸数据
# known faces
for i in range(csv_rd.shape[0]):
features_someone_arr = []
for j in range(0, len(csv_rd.ix[i, :])):
features_someone_arr.append(csv_rd.ix[i, :][j])
features_known_arr.append(features_someone_arr)
print("Faces in Database:", len(features_known_arr))
# Dlib 检测器和预测器
# Dlib 正向人脸检测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
# 创建 cv2 摄像头对象
cap = cv2.VideoCapture(0)
# cap.set(propId, value)
# 设置视频参数,propId 设置的视频参数,value 设置的参数值
cap.set(3, 480)
face_reco_model = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
# 返回一张图像多张人脸的 128D 特征
def get_128d_features(img_gray):
faces = detector(img_gray, 1)
if len(faces) != 0:
face_des = []
for i in range(len(faces)):
shape = predictor(img_gray, faces[i])
face_des.append(facerec.compute_face_descriptor(img_gray, shape))
else:
face_des = []
return face_des
class Face_Recognizer:
def __init__(self):
self.face_feature_known_list = []
self.face_name_known_list = []
self.current_frame_face_cnt = 0
self.current_frame_face_feature_list = []
self.current_frame_face_name_list = []
self.current_frame_face_name_position_list = []
# cap.isOpened() 返回 true/false 检查初始化是否成功
while cap.isOpened():
# Update FPS
self.fps = 0
self.fps_show = 0
self.frame_start_time = 0
self.frame_cnt = 0
self.start_time = time.time()
flag, img_rd = cap.read()
kk = cv2.waitKey(1)
self.font = cv2.FONT_ITALIC
# 安全加载中文字体
try:
self.font_chinese = ImageFont.truetype("simsun.ttc", 30)
except:
print("警告: 无法加载中文字体,使用默认字体")
self.font_chinese = ImageFont.load_default()
# 取灰度
img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY)
# 添加退出标志
self.exit_flag = False
# 人脸数 faces
faces = detector(img_gray, 0)
def get_face_database(self):
if os.path.exists("data/features_all.csv"):
path_features_known_csv = "data/features_all.csv"
csv_rd = pd.read_csv(path_features_known_csv, header=None)
for i in range(csv_rd.shape[0]):
features_someone_arr = []
# 修复:确保姓名为字符串
name = str(csv_rd.iloc[i][0])
self.face_name_known_list.append(name)
for j in range(1, 129):
if csv_rd.iloc[i][j] == '':
features_someone_arr.append('0')
else:
features_someone_arr.append(csv_rd.iloc[i][j])
self.face_feature_known_list.append(features_someone_arr)
logging.info("Faces in Database:%d", len(self.face_feature_known_list))
return 1
else:
logging.warning("'features_all.csv' not found!")
return 0
# 待会要写的字体
font = cv2.FONT_HERSHEY_COMPLEX
@staticmethod
def return_euclidean_distance(feature_1, feature_2):
feature_1 = np.array(feature_1)
feature_2 = np.array(feature_2)
dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
return dist
# 存储当前摄像头中捕获到的所有人脸的坐标/名字
pos_namelist = []
name_namelist = []
def update_fps(self):
now = time.time()
if str(self.start_time).split(".")[0] != str(now).split(".")[0]:
self.fps_show = self.fps
self.start_time = now
self.frame_time = now - self.frame_start_time
self.fps = 1.0 / self.frame_time
self.frame_start_time = now
# 按下 q 键退出
if kk == ord('q'):
break
else:
# 检测到人脸
if len(faces) != 0:
# 获取当前捕获到的图像的所有人脸的特征,存储到 features_cap_arr
features_cap_arr = []
for i in range(len(faces)):
shape = predictor(img_rd, faces[i])
features_cap_arr.append(facerec.compute_face_descriptor(img_rd, shape))
def draw_note(self, img_rd):
cv2.putText(img_rd, "Face Recognizer", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Frame: " + str(self.frame_cnt), (20, 100), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
cv2.putText(img_rd, "FPS: " + str(self.fps_show.__round__(2)), (20, 130), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "Faces: " + str(self.current_frame_face_cnt), (20, 160), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "Q: Quit", (20, 450), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
# 遍历捕获到的图像中所有的人脸
for k in range(len(faces)):
# 让人名跟随在矩形框的下方
# 确定人名的位置坐标
# 先默认所有人不认识,是 unknown
name_namelist.append("unknown")
def draw_name(self, img_rd):
# 在人脸框下面写人脸名字
img = Image.fromarray(cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
for i in range(self.current_frame_face_cnt):
try:
# 安全处理姓名
name = str(self.current_frame_face_name_list[i])
position = tuple(map(int, self.current_frame_face_name_position_list[i]))
draw.text(xy=position, text=name, font=self.font_chinese, fill=(255, 255, 0))
except Exception as e:
print(f"绘制姓名时出错: {e}")
continue
img_rd = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
return img_rd
# 每个捕获人脸的名字坐标
pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top())/4)]))
def check_window_closed(self, window_name="camera"):
"""检查窗口是否被关闭"""
try:
# 尝试获取窗口属性,如果窗口关闭会返回 -1
if cv2.getWindowProperty(window_name, cv2.WND_PROP_VISIBLE) < 1:
return True
return False
except:
# 如果窗口不存在,也会触发异常
return True
def process(self, stream):
# 1. 读取存放所有人脸特征的 csv
if not self.get_face_database():
print("错误: 无法加载人脸数据库")
return
print("人脸识别系统启动成功!")
print("'Q' 键退出程序")
print("或点击窗口关闭按钮退出")
# 创建窗口并设置为正常模式
cv2.namedWindow("camera", cv2.WINDOW_NORMAL)
while stream.isOpened() and not self.exit_flag:
# 检查窗口是否被关闭
if self.check_window_closed():
print("检测到窗口关闭,退出程序")
break
self.frame_cnt += 1
flag, img_rd = stream.read()
if not flag:
print("无法读取视频帧")
break
# 检测按键和窗口关闭
kk = cv2.waitKey(1) & 0xFF
# 按下 q 键退出
if kk == ord('q') or kk == ord('Q'):
print("接收到退出信号,退出程序")
break
# 检查窗口关闭
if cv2.getWindowProperty("camera", cv2.WND_PROP_VISIBLE) < 1:
print("窗口已关闭,退出程序")
break
self.draw_note(img_rd)
self.current_frame_face_feature_list = []
self.current_frame_face_cnt = 0
self.current_frame_face_name_position_list = []
self.current_frame_face_name_list = []
# 2. 检测到人脸
faces = detector(img_rd, 0)
if len(faces) != 0:
# 3. 获取当前捕获到的图像的所有人脸的特征
for i in range(len(faces)):
shape = predictor(img_rd, faces[i])
self.current_frame_face_feature_list.append(face_reco_model.compute_face_descriptor(img_rd, shape))
# 4. 遍历捕获到的图像中所有的人脸
for k in range(len(faces)):
# 先默认所有人不认识
self.current_frame_face_name_list.append("unknown")
# 每个捕获人脸的名字坐标
self.current_frame_face_name_position_list.append(tuple(
[faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]))
# 5. 对于某张人脸,遍历所有存储的人脸特征
current_frame_e_distance_list = []
for i in range(len(self.face_feature_known_list)):
if str(self.face_feature_known_list[i][0]) != '0.0':
e_distance_tmp = self.return_euclidean_distance(
self.current_frame_face_feature_list[k],
self.face_feature_known_list[i]
)
current_frame_e_distance_list.append(e_distance_tmp)
else:
current_frame_e_distance_list.append(999999999)
# 6. 寻找出最小的欧式距离匹配
if current_frame_e_distance_list:
similar_person_num = current_frame_e_distance_list.index(min(current_frame_e_distance_list))
min_distance = min(current_frame_e_distance_list)
if min_distance < 0.4:
self.current_frame_face_name_list[k] = self.face_name_known_list[similar_person_num]
# 对于某张人脸,遍历所有存储的人脸特征
for i in range(len(features_known_arr)):
print("with person_", str(i+1), "the ", end='')
# 将某张人脸与存储的所有人脸数据进行比对
compare = return_euclidean_distance(features_cap_arr[k], features_known_arr[i])
if compare == "same": # 找到了相似脸
# 在这里修改 person_1, person_2 ... 的名字
# 这里只写了前三个
# 可以在这里改称 Jack, Tom and others
# Here you can modify the names shown on the camera
if i == 0:
name_namelist[k] = "Person 1"
elif i == 1:
name_namelist[k] = "Person 2"
elif i == 2:
name_namelist[k] = "Person 3"
# 矩形框
for kk, d in enumerate(faces):
# 绘制矩形框
cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)
cv2.rectangle(img_rd,
(faces[k].left(), faces[k].top()),
(faces[k].right(), faces[k].bottom()),
(255, 255, 255), 2)
# 在人脸框下面写人脸名字
for i in range(len(faces)):
cv2.putText(img_rd, name_namelist[i], pos_namelist[i], font, 0.8, (0, 255, 255), 1, cv2.LINE_AA)
self.current_frame_face_cnt = len(faces)
print("Name list now:", name_namelist, "\n")
# 8. 写名字
img_rd = self.draw_name(img_rd)
cv2.putText(img_rd, "Press 'q': Quit", (20, 450), font, 0.8, (84, 255, 159), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Face Recognition", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
# 显示图像
cv2.imshow("camera", img_rd)
# 窗口显示
cv2.imshow("camera", img_rd)
# 9. 更新 FPS
self.update_fps()
# 释放摄像头
cap.release()
# 清理资源
cv2.destroyAllWindows()
print("程序正常退出")
# 删除建立的窗口
cv2.destroyAllWindows()
def run(self):
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("错误: 无法打开摄像头")
return
# 设置摄像头参数
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
try:
self.process(cap)
except KeyboardInterrupt:
print("\n接收到 Ctrl+C,退出程序")
except Exception as e:
print(f"程序异常: {e}")
finally:
# 确保资源被释放
cap.release()
cv2.destroyAllWindows()
def main():
logging.basicConfig(level=logging.INFO)
print("=== 人脸识别系统启动 ===")
# 检查必要的文件
required_files = [
'data/data_dlib/shape_predictor_68_face_landmarks.dat',
'data/data_dlib/dlib_face_recognition_resnet_model_v1.dat',
'data/features_all.csv'
]
for file in required_files:
if not os.path.exists(file):
print(f"错误: 缺少必要文件 {file}")
return
Face_Recognizer_con = Face_Recognizer()
Face_Recognizer_con.run()
if __name__ == '__main__':
main()

306
face_reco_from_camera_ot.py Normal file
View File

@ -0,0 +1,306 @@
# Copyright (C) 2018-2021 coneypo
# SPDX-License-Identifier: MIT
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
# Mail: coneypo@foxmail.com
# 利用 OT 人脸追踪, 进行人脸实时识别 / Real-time face detection and recognition via OT for multi faces
# 检测 -> 识别人脸, 新人脸出现 -> 不需要识别, 而是利用质心追踪来判断识别结果 / Do detection -> recognize face, new face -> not do re-recognition
# 人脸进行再识别需要花费大量时间, 这里用 OT 做跟踪 / Do re-recognition for multi faces will cost much time, OT will be used to instead it
import dlib
import numpy as np
import cv2
import os
import pandas as pd
import time
import logging
# Dlib 正向人脸检测器 / Use frontal face detector of Dlib
detector = dlib.get_frontal_face_detector()
# Dlib 人脸 landmark 特征点检测器 / Get face landmarks
predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
# Dlib Resnet 人脸识别模型, 提取 128D 的特征矢量 / Use Dlib resnet50 model to get 128D face descriptor
face_reco_model = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
class Face_Recognizer:
def __init__(self):
self.font = cv2.FONT_ITALIC
# FPS
self.frame_time = 0
self.frame_start_time = 0
self.fps = 0
self.fps_show = 0
self.start_time = time.time()
# cnt for frame
self.frame_cnt = 0
# 用来存放所有录入人脸特征的数组 / Save the features of faces in the database
self.face_features_known_list = []
# 存储录入人脸名字 / Save the name of faces in the database
self.face_name_known_list = []
# 用来存储上一帧和当前帧 ROI 的质心坐标 / List to save centroid positions of ROI in frame N-1 and N
self.last_frame_face_centroid_list = []
self.current_frame_face_centroid_list = []
# 用来存储上一帧和当前帧检测出目标的名字 / List to save names of objects in frame N-1 and N
self.last_frame_face_name_list = []
self.current_frame_face_name_list = []
# 上一帧和当前帧中人脸数的计数器 / cnt for faces in frame N-1 and N
self.last_frame_face_cnt = 0
self.current_frame_face_cnt = 0
# 用来存放进行识别时候对比的欧氏距离 / Save the e-distance for faceX when recognizing
self.current_frame_face_X_e_distance_list = []
# 存储当前摄像头中捕获到的所有人脸的坐标名字 / Save the positions and names of current faces captured
self.current_frame_face_position_list = []
# 存储当前摄像头中捕获到的人脸特征 / Save the features of people in current frame
self.current_frame_face_feature_list = []
# e distance between centroid of ROI in last and current frame
self.last_current_frame_centroid_e_distance = 0
# 控制再识别的后续帧数 / Reclassify after 'reclassify_interval' frames
# 如果识别出 "unknown" 的脸, 将在 reclassify_interval_cnt 计数到 reclassify_interval 后, 对于人脸进行重新识别
self.reclassify_interval_cnt = 0
self.reclassify_interval = 10
# 从 "features_all.csv" 读取录入人脸特征 / Get known faces from "features_all.csv"
def get_face_database(self):
if os.path.exists("data/features_all.csv"):
path_features_known_csv = "data/features_all.csv"
csv_rd = pd.read_csv(path_features_known_csv, header=None)
for i in range(csv_rd.shape[0]):
features_someone_arr = []
self.face_name_known_list.append(csv_rd.iloc[i][0])
for j in range(1, 129):
if csv_rd.iloc[i][j] == '':
features_someone_arr.append('0')
else:
features_someone_arr.append(csv_rd.iloc[i][j])
self.face_features_known_list.append(features_someone_arr)
logging.info("Faces in Database: %d", len(self.face_features_known_list))
return 1
else:
logging.warning("'features_all.csv' not found!")
logging.warning("Please run 'get_faces_from_camera.py' "
"and 'features_extraction_to_csv.py' before 'face_reco_from_camera.py'")
return 0
def update_fps(self):
now = time.time()
# 每秒刷新 fps / Refresh fps per second
if str(self.start_time).split(".")[0] != str(now).split(".")[0]:
self.fps_show = self.fps
self.start_time = now
self.frame_time = now - self.frame_start_time
self.fps = 1.0 / self.frame_time
self.frame_start_time = now
@staticmethod
# 计算两个128D向量间的欧式距离 / Compute the e-distance between two 128D features
def return_euclidean_distance(feature_1, feature_2):
feature_1 = np.array(feature_1)
feature_2 = np.array(feature_2)
dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
return dist
# 使用质心追踪来识别人脸 / Use centroid tracker to link face_x in current frame with person_x in last frame
def centroid_tracker(self):
for i in range(len(self.current_frame_face_centroid_list)):
e_distance_current_frame_person_x_list = []
# 对于当前帧中的人脸1, 和上一帧中的 人脸1/2/3/4/.. 进行欧氏距离计算 / For object 1 in current_frame, compute e-distance with object 1/2/3/4/... in last frame
for j in range(len(self.last_frame_face_centroid_list)):
self.last_current_frame_centroid_e_distance = self.return_euclidean_distance(
self.current_frame_face_centroid_list[i], self.last_frame_face_centroid_list[j])
e_distance_current_frame_person_x_list.append(
self.last_current_frame_centroid_e_distance)
last_frame_num = e_distance_current_frame_person_x_list.index(
min(e_distance_current_frame_person_x_list))
self.current_frame_face_name_list[i] = self.last_frame_face_name_list[last_frame_num]
# 生成的 cv2 window 上面添加说明文字 / putText on cv2 window
def draw_note(self, img_rd):
# 添加说明 / Add some info on windows
cv2.putText(img_rd, "Face Recognizer with OT", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Frame: " + str(self.frame_cnt), (20, 100), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "FPS: " + str(self.fps.__round__(2)), (20, 130), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "Faces: " + str(self.current_frame_face_cnt), (20, 160), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "Q: Quit", (20, 450), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
for i in range(len(self.current_frame_face_name_list)):
img_rd = cv2.putText(img_rd, "Face_" + str(i + 1), tuple(
[int(self.current_frame_face_centroid_list[i][0]), int(self.current_frame_face_centroid_list[i][1])]),
self.font,
0.8, (255, 190, 0),
1,
cv2.LINE_AA)
# 处理获取的视频流, 进行人脸识别 / Face detection and recognition wit OT from input video stream
def process(self, stream):
# 1. 读取存放所有人脸特征的 csv / Get faces known from "features.all.csv"
if self.get_face_database():
while stream.isOpened():
self.frame_cnt += 1
logging.debug("Frame " + str(self.frame_cnt) + " starts")
flag, img_rd = stream.read()
kk = cv2.waitKey(1)
# 2. 检测人脸 / Detect faces for frame X
faces = detector(img_rd, 0)
# 3. 更新人脸计数器 / Update cnt for faces in frames
self.last_frame_face_cnt = self.current_frame_face_cnt
self.current_frame_face_cnt = len(faces)
# 4. 更新上一帧中的人脸列表 / Update the face name list in last frame
self.last_frame_face_name_list = self.current_frame_face_name_list[:]
# 5. 更新上一帧和当前帧的质心列表 / update frame centroid list
self.last_frame_face_centroid_list = self.current_frame_face_centroid_list
self.current_frame_face_centroid_list = []
# 6.1 如果当前帧和上一帧人脸数没有变化 / if cnt not changes
if (self.current_frame_face_cnt == self.last_frame_face_cnt) and (
self.reclassify_interval_cnt != self.reclassify_interval):
logging.debug("scene 1: 当前帧和上一帧相比没有发生人脸数变化 / No face cnt changes in this frame!!!")
self.current_frame_face_position_list = []
if "unknown" in self.current_frame_face_name_list:
logging.debug(" 有未知人脸, 开始进行 reclassify_interval_cnt 计数")
self.reclassify_interval_cnt += 1
if self.current_frame_face_cnt != 0:
for k, d in enumerate(faces):
self.current_frame_face_position_list.append(tuple(
[faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]))
self.current_frame_face_centroid_list.append(
[int(faces[k].left() + faces[k].right()) / 2,
int(faces[k].top() + faces[k].bottom()) / 2])
img_rd = cv2.rectangle(img_rd,
tuple([d.left(), d.top()]),
tuple([d.right(), d.bottom()]),
(255, 255, 255), 2)
# 如果当前帧中有多个人脸, 使用质心追踪 / Multi-faces in current frame, use centroid-tracker to track
if self.current_frame_face_cnt != 1:
self.centroid_tracker()
for i in range(self.current_frame_face_cnt):
# 6.2 Write names under ROI
img_rd = cv2.putText(img_rd, self.current_frame_face_name_list[i],
self.current_frame_face_position_list[i], self.font, 0.8, (0, 255, 255), 1,
cv2.LINE_AA)
self.draw_note(img_rd)
# 6.2 如果当前帧和上一帧人脸数发生变化 / If cnt of faces changes, 0->1 or 1->0 or ...
else:
logging.debug("scene 2: 当前帧和上一帧相比人脸数发生变化 / Faces cnt changes in this frame")
self.current_frame_face_position_list = []
self.current_frame_face_X_e_distance_list = []
self.current_frame_face_feature_list = []
self.reclassify_interval_cnt = 0
# 6.2.1 人脸数减少 / Face cnt decreases: 1->0, 2->1, ...
if self.current_frame_face_cnt == 0:
logging.debug(" scene 2.1 人脸消失, 当前帧中没有人脸 / No faces in this frame!!!")
# clear list of names and features
self.current_frame_face_name_list = []
# 6.2.2 人脸数增加 / Face cnt increase: 0->1, 0->2, ..., 1->2, ...
else:
logging.debug(" scene 2.2 出现人脸, 进行人脸识别 / Get faces in this frame and do face recognition")
self.current_frame_face_name_list = []
for i in range(len(faces)):
shape = predictor(img_rd, faces[i])
self.current_frame_face_feature_list.append(
face_reco_model.compute_face_descriptor(img_rd, shape))
self.current_frame_face_name_list.append("unknown")
# 6.2.2.1 遍历捕获到的图像中所有的人脸 / Traversal all the faces in the database
for k in range(len(faces)):
logging.debug(" For face %d in current frame:", k + 1)
self.current_frame_face_centroid_list.append(
[int(faces[k].left() + faces[k].right()) / 2,
int(faces[k].top() + faces[k].bottom()) / 2])
self.current_frame_face_X_e_distance_list = []
# 6.2.2.2 每个捕获人脸的名字坐标 / Positions of faces captured
self.current_frame_face_position_list.append(tuple(
[faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]))
# 6.2.2.3 对于某张人脸, 遍历所有存储的人脸特征
# For every faces detected, compare the faces in the database
for i in range(len(self.face_features_known_list)):
# 如果 q 数据不为空
if str(self.face_features_known_list[i][0]) != '0.0':
e_distance_tmp = self.return_euclidean_distance(
self.current_frame_face_feature_list[k],
self.face_features_known_list[i])
logging.debug(" with person %d, the e-distance: %f", i + 1, e_distance_tmp)
self.current_frame_face_X_e_distance_list.append(e_distance_tmp)
else:
# 空数据 person_X
self.current_frame_face_X_e_distance_list.append(999999999)
# 6.2.2.4 寻找出最小的欧式距离匹配 / Find the one with minimum e distance
similar_person_num = self.current_frame_face_X_e_distance_list.index(
min(self.current_frame_face_X_e_distance_list))
if min(self.current_frame_face_X_e_distance_list) < 0.4:
self.current_frame_face_name_list[k] = self.face_name_known_list[similar_person_num]
logging.debug(" Face recognition result: %s",
self.face_name_known_list[similar_person_num])
else:
logging.debug(" Face recognition result: Unknown person")
# 7. 生成的窗口添加说明文字 / Add note on cv2 window
self.draw_note(img_rd)
# cv2.imwrite("debug/debug_" + str(self.frame_cnt) + ".png", img_rd) # Dump current frame image if needed
# 8. 按下 'q' 键退出 / Press 'q' to exit
if kk == ord('q'):
break
self.update_fps()
cv2.namedWindow("camera", 1)
cv2.imshow("camera", img_rd)
logging.debug("Frame ends\n\n")
def run(self):
# cap = cv2.VideoCapture("video.mp4") # Get video stream from video file
cap = cv2.VideoCapture(0) # Get video stream from camera
self.process(cap)
cap.release()
cv2.destroyAllWindows()
def main():
# logging.basicConfig(level=logging.DEBUG) # Set log level to 'logging.DEBUG' to print debug info of every frame
logging.basicConfig(level=logging.INFO)
Face_Recognizer_con = Face_Recognizer()
Face_Recognizer_con.run()
if __name__ == '__main__':
main()

136
features_extraction_to_csv.py Executable file
View File

@ -0,0 +1,136 @@
# Copyright (C) 2018-2021 coneypo
# SPDX-License-Identifier: MIT
import os
import dlib
import csv
import numpy as np
import logging
import cv2
from PIL import Image
# 要读取人脸图像文件的路径
path_images_from_camera = "data/data_faces"
# Dlib 检测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
face_reco_model = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
def return_128d_features(path_img):
"""返回单张图像的 128D 特征"""
try:
img_pil = Image.open(path_img)
img_np = np.array(img_pil)
img_rd = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
faces = detector(img_rd, 1)
logging.info("%-40s %-20s", "检测到人脸的图像:", path_img)
if len(faces) != 0:
shape = predictor(img_rd, faces[0])
face_descriptor = face_reco_model.compute_face_descriptor(img_rd, shape)
return face_descriptor
else:
logging.warning("未检测到人脸: %s", path_img)
return None
except Exception as e:
logging.error("处理图像时出错 %s: %s", path_img, e)
return None
def return_features_mean_personX(path_face_personX):
"""返回 personX 的 128D 特征均值"""
features_list_personX = []
photos_list = os.listdir(path_face_personX)
if photos_list:
for photo in photos_list:
photo_path = os.path.join(path_face_personX, photo)
logging.info("正在读取图像: %s", photo_path)
features_128d = return_128d_features(photo_path)
if features_128d is not None:
features_list_personX.append(features_128d)
else:
logging.warning("文件夹为空: %s", path_face_personX)
# 计算 128D 特征的均值
if features_list_personX:
features_mean_personX = np.array(features_list_personX).mean(axis=0)
else:
features_mean_personX = np.zeros(128, dtype=np.float64)
return features_mean_personX
def get_person_name_from_folder(folder_name):
"""从文件夹名称获取有意义的姓名"""
# 常见的文件夹前缀
prefixes = ['person_', 'face_', 'user_']
for prefix in prefixes:
if folder_name.startswith(prefix):
name_part = folder_name[len(prefix):]
# 如果剩下的部分是纯数字,使用完整文件夹名
if name_part.isdigit():
return folder_name
else:
return name_part
return folder_name
def main():
logging.basicConfig(level=logging.INFO)
# 检查源文件夹是否存在
if not os.path.exists(path_images_from_camera):
logging.error("人脸图像文件夹不存在: %s", path_images_from_camera)
return
# 获取人脸文件夹列表
person_list = os.listdir(path_images_from_camera)
person_list.sort()
if not person_list:
logging.error("没有人脸文件夹可处理")
return
logging.info("找到 %d 个人脸文件夹: %s", len(person_list), person_list)
# 创建CSV文件
with open("data/features_all.csv", "w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile)
successful_count = 0
for person_folder in person_list:
folder_path = os.path.join(path_images_from_camera, person_folder)
if not os.path.isdir(folder_path):
continue
logging.info("处理文件夹: %s", person_folder)
# 提取特征
features_mean = return_features_mean_personX(folder_path)
# 获取有意义的姓名
person_name = get_person_name_from_folder(person_folder)
logging.info("使用姓名: %s", person_name)
# 构建行数据:姓名 + 128维特征
row_data = [person_name] + features_mean.tolist()
writer.writerow(row_data)
successful_count += 1
logging.info("完成: %s", person_name)
logging.info("-" * 50)
logging.info("成功处理 %d/%d 个人脸文件夹", successful_count, len(person_list))
logging.info("特征数据已保存到: data/features_all.csv")
if __name__ == '__main__':
main()

232
get_faces.py Executable file
View File

@ -0,0 +1,232 @@
# Copyright (C) 2018-2021 coneypo
# SPDX-License-Identifier: MIT
import dlib
import numpy as np
import cv2
import os
import shutil
import time
import logging
# Dlib 正向人脸检测器
detector = dlib.get_frontal_face_detector()
class Face_Register:
def __init__(self):
self.path_photos_from_camera = "data/data_faces/"
self.font = cv2.FONT_ITALIC
self.existing_faces_cnt = 0
self.ss_cnt = 0
self.current_frame_faces_cnt = 0
self.save_flag = 1
self.press_n_flag = 0
self.current_person_name = "" # 新增:当前录入人姓名
# FPS
self.frame_time = 0
self.frame_start_time = 0
self.fps = 0
self.fps_show = 0
self.start_time = time.time()
def pre_work_mkdir(self):
if os.path.isdir(self.path_photos_from_camera):
pass
else:
os.makedirs(self.path_photos_from_camera, exist_ok=True)
def pre_work_del_old_face_folders(self):
folders_rd = os.listdir(self.path_photos_from_camera)
for folder in folders_rd:
shutil.rmtree(os.path.join(self.path_photos_from_camera, folder))
if os.path.isfile("data/features_all.csv"):
os.remove("data/features_all.csv")
def check_existing_faces_cnt(self):
if os.listdir(self.path_photos_from_camera):
person_list = os.listdir(self.path_photos_from_camera)
# 修改:不再提取数字序号,而是统计数量
self.existing_faces_cnt = len(person_list)
else:
self.existing_faces_cnt = 0
def update_fps(self):
now = time.time()
if str(self.start_time).split(".")[0] != str(now).split(".")[0]:
self.fps_show = self.fps
self.start_time = now
self.frame_time = now - self.frame_start_time
self.fps = 1.0 / self.frame_time
self.frame_start_time = now
def draw_note(self, img_rd):
# 添加说明
cv2.putText(img_rd, "Face Register", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "FPS: " + str(self.fps_show.__round__(2)), (20, 100), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "Faces: " + str(self.current_frame_faces_cnt), (20, 140), self.font, 0.8, (0, 255, 0), 1,
cv2.LINE_AA)
# 修改:显示当前录入人姓名
if self.current_person_name:
cv2.putText(img_rd, f"Name: {self.current_person_name}", (20, 180), self.font, 0.8, (255, 255, 0), 1,
cv2.LINE_AA)
cv2.putText(img_rd, "N: Input Name & Create folder", (20, 320), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "S: Save current face", (20, 350), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Q: Quit", (20, 380), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
cv2.putText(img_rd, "D: Delete all data", (20, 410), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
def get_person_name_from_input(self):
"""从用户输入获取姓名"""
print("\n请输入姓名(中文或英文):")
name = input().strip()
return name if name else f"person_{self.existing_faces_cnt + 1}"
def create_person_folder(self, person_name):
"""创建人员文件夹"""
# 清理文件名中的非法字符
safe_name = "".join(c for c in person_name if c.isalnum() or c in (' ', '-', '_')).rstrip()
if not safe_name:
safe_name = f"person_{self.existing_faces_cnt + 1}"
# 创建文件夹路径
folder_name = f"person_{safe_name}"
current_face_dir = os.path.join(self.path_photos_from_camera, folder_name)
os.makedirs(current_face_dir, exist_ok=True)
logging.info("新建人脸文件夹: %s", current_face_dir)
return current_face_dir, safe_name
def process(self, stream):
# 1. 新建储存人脸图像文件目录
self.pre_work_mkdir()
# 2. 检查已有人脸文件
self.check_existing_faces_cnt()
current_face_dir = ""
print("人脸录入说明:")
print("- 按 'N': 输入姓名并创建新人员文件夹")
print("- 按 'S': 保存当前检测到的人脸")
print("- 按 'D': 删除所有已录入数据")
print("- 按 'Q': 退出程序")
while stream.isOpened():
flag, img_rd = stream.read()
if not flag:
break
kk = cv2.waitKey(1)
faces = detector(img_rd, 0)
# 4. 按下 'n' 输入姓名并新建文件夹
if kk == ord('n'):
person_name = self.get_person_name_from_input()
current_face_dir, self.current_person_name = self.create_person_folder(person_name)
self.ss_cnt = 0
self.press_n_flag = 1
print(f"已创建文件夹: {current_face_dir}")
print("请调整位置并按 'S' 保存人脸")
# 5. 按下 'd' 删除所有数据
elif kk == ord('d'):
confirm = input("确定要删除所有数据吗?(y/n): ")
if confirm.lower() == 'y':
self.pre_work_del_old_face_folders()
self.existing_faces_cnt = 0
self.current_person_name = ""
self.press_n_flag = 0
print("所有数据已删除")
continue
# 6. 检测到人脸
if len(faces) != 0:
for k, d in enumerate(faces):
# 计算矩形框大小
height = d.bottom() - d.top()
width = d.right() - d.left()
hh = int(height / 2)
ww = int(width / 2)
# 判断人脸是否在范围内
if (d.right() + ww > 640 or d.bottom() + hh > 480 or
d.left() - ww < 0 or d.top() - hh < 0):
cv2.putText(img_rd, "OUT OF RANGE", (20, 300), self.font, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
color_rectangle = (0, 0, 255)
save_flag = 0
else:
color_rectangle = (255, 255, 255)
save_flag = 1
# 绘制人脸框
cv2.rectangle(img_rd,
(d.left() - ww, d.top() - hh),
(d.right() + ww, d.bottom() + hh),
color_rectangle, 2)
# 创建空白图像用于保存人脸
img_blank = np.zeros((height * 2, width * 2, 3), np.uint8)
if save_flag and kk == ord('s'):
# 检查是否已创建文件夹
if self.press_n_flag:
self.ss_cnt += 1
# 提取人脸区域
for ii in range(height * 2):
for jj in range(width * 2):
img_blank[ii][jj] = img_rd[d.top() - hh + ii][d.left() - ww + jj]
# 保存人脸图像
filename = f"img_face_{self.ss_cnt}.jpg"
filepath = os.path.join(current_face_dir, filename)
cv2.imwrite(filepath, img_blank)
logging.info("保存人脸: %s", filepath)
print(f"已保存第 {self.ss_cnt} 张人脸图片")
else:
logging.warning("请先按 'N' 输入姓名创建文件夹")
self.current_frame_faces_cnt = len(faces)
# 绘制说明文字
self.draw_note(img_rd)
# 按下 'q' 退出
if kk == ord('q'):
break
# 更新 FPS
self.update_fps()
cv2.imshow("Face Register", img_rd)
def run(self):
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("错误: 无法打开摄像头")
return
# 设置摄像头参数
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
self.process(cap)
cap.release()
cv2.destroyAllWindows()
print("程序结束")
def main():
logging.basicConfig(level=logging.INFO)
Face_Register_con = Face_Register()
Face_Register_con.run()
if __name__ == '__main__':
main()

378
get_faces_UI.py Normal file
View File

@ -0,0 +1,378 @@
# Copyright (C) 2018-2021 coneypo
# SPDX-License-Identifier: MIT
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
# Mail: coneypo@foxmail.com
# 人脸录入 Tkinter GUI / Face register GUI with tkinter
import dlib
import numpy as np
import cv2
import os
import shutil
import time
import logging
import tkinter as tk
from tkinter import font as tkFont
from tkinter import messagebox
from PIL import Image, ImageTk
# Dlib 正向人脸检测器 / Use frontal face detector of Dlib
detector = dlib.get_frontal_face_detector()
class Face_Register:
def __init__(self):
self.current_frame_faces_cnt = 0 # 当前帧中人脸计数器 / cnt for counting faces in current frame
self.existing_faces = 0 # 已录入的人脸数 / cnt for counting saved faces
self.ss_cnt = 0 # 录入 person_n 人脸时图片计数器 / cnt for screen shots
self.registered_names = [] # 已录入的人脸名字 / names of registered faces
self.path_photos_from_camera = "data/data_faces_from_camera/"
self.current_face_dir = ""
self.font = cv2.FONT_ITALIC
if os.listdir(self.path_photos_from_camera):
self.existing_faces = len(os.listdir(self.path_photos_from_camera))
# Tkinter GUI
self.win = tk.Tk()
self.win.title("人脸录入")
# PLease modify window size here if needed
self.win.geometry("1300x550")
# GUI left part
self.frame_left_camera = tk.Frame(self.win)
self.label = tk.Label(self.win)
self.label.pack(side=tk.LEFT)
self.frame_left_camera.pack()
# GUI right part
self.frame_right_info = tk.Frame(self.win)
self.label_cnt_face_in_database = tk.Label(self.frame_right_info, text=str(self.existing_faces))
self.label_fps_info = tk.Label(self.frame_right_info, text="")
self.input_name = tk.Entry(self.frame_right_info, width=25)
self.input_name_char = ""
self.label_warning = tk.Label(self.frame_right_info)
self.label_face_cnt = tk.Label(self.frame_right_info, text="Faces in current frame: ")
self.log_all = tk.Label(self.frame_right_info)
self.font_title = tkFont.Font(family='Helvetica', size=20, weight='bold')
self.font_step_title = tkFont.Font(family='Helvetica', size=15, weight='bold')
self.font_warning = tkFont.Font(family='Helvetica', size=15, weight='bold')
# Current frame and face ROI position
self.current_frame = np.ndarray
self.face_ROI_image = np.ndarray
self.face_ROI_width_start = 0
self.face_ROI_height_start = 0
self.face_ROI_width = 0
self.face_ROI_height = 0
self.ww = 0
self.hh = 0
self.out_of_range_flag = False
self.face_folder_created_flag = False
# FPS
self.frame_time = 0
self.frame_start_time = 0
self.fps = 0
self.fps_show = 0
self.start_time = time.time()
self.cap = cv2.VideoCapture(0) # Get video stream from camera
# self.cap = cv2.VideoCapture("test.mp4") # Input local video
# 删除之前存的人脸数据文件夹 / Delete old face folders
def GUI_clear_data(self):
# 删除之前存的人脸数据文件夹, 删除 "/data_faces_from_camera/person_x/"...
folders_rd = os.listdir(self.path_photos_from_camera)
for i in range(len(folders_rd)):
shutil.rmtree(self.path_photos_from_camera + folders_rd[i])
if os.path.isfile("./data/features_all.csv"):
os.remove("./data/features_all.csv")
self.label_cnt_face_in_database['text'] = "0"
self.registered_names.clear()
self.log_all["text"] = "全部图片和`features_all.csv`已全部移除!"
self.log_all["fg"] = "green"
def GUI_get_input_name(self):
self.input_name_char = self.input_name.get()
if self.input_name_char:
if self.input_name_char not in self.registered_names:
self.create_face_folder()
self.registered_names.append(self.input_name_char)
self.label_cnt_face_in_database['text'] = str(self.registered_names.__len__())
else:
self.log_all["text"] = "此名字已被录入,请输入新的名字!"
self.log_all["fg"] = "red"
else:
self.log_all["text"] = "请输入姓名"
self.log_all["fg"] = "red"
def delete_name(self):
self.input_name_char = self.input_name.get()
if self.input_name_char:
if self.input_name_char in self.registered_names:
self.remove_face_dir(self.path_photos_from_camera + "person_" + self.input_name_char)
self.log_all["text"] = "'" + self.input_name_char + "'" + "已移除!"
self.log_all["fg"] = "green"
self.registered_names.remove(self.input_name_char)
self.label_cnt_face_in_database['text'] = str(self.registered_names.__len__())
else:
self.log_all["text"] = "此名字不存在,请输入正确的名字!"
self.log_all["fg"] = "red"
else:
self.log_all["text"] = "请先输入要删除的姓名"
self.log_all["fg"] = "red"
def change_name(self):
self.input_name_char = self.input_name.get()
if self.input_name_char:
if self.input_name_char in self.registered_names:
self.current_face_dir = self.path_photos_from_camera + \
"person_" + \
self.input_name_char
pecturt_list = os.listdir(self.current_face_dir)
self.ss_cnt = len(pecturt_list) # 将人脸计数器置为原来的 / Clear the cnt of screen shots
self.face_folder_created_flag = True # Face folder already created
self.label_cnt_face_in_database['text'] = str(self.registered_names.__len__())
self.log_all["text"] = "可以添加新照片了!"
self.log_all["fg"] = "green"
else:
self.log_all["text"] = "此名字不存在,请输入正确的名字!"
self.log_all["fg"] = "red"
else:
self.log_all["text"] = "请先输入要更改的姓名"
self.log_all["fg"] = "red"
def GUI_info(self):
tk.Label(self.frame_right_info,
text="Face register",
font=self.font_title).grid(row=0, column=0, columnspan=3, sticky=tk.W, padx=2, pady=20)
tk.Label(self.frame_right_info,
text="FPS: ").grid(row=1, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2)
self.label_fps_info.grid(row=1, column=2, sticky=tk.W, padx=5, pady=2)
tk.Label(self.frame_right_info,
text="数据库中已有的人脸: ").grid(row=2, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2)
self.label_cnt_face_in_database.grid(row=2, column=2, columnspan=3, sticky=tk.W, padx=5, pady=2)
tk.Label(self.frame_right_info,
text="当前帧中的人脸: ").grid(row=3, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2)
self.label_face_cnt.grid(row=3, column=2, columnspan=3, sticky=tk.W, padx=5, pady=2)
self.label_warning.grid(row=4, column=0, columnspan=3, sticky=tk.W, padx=5, pady=2)
# Step 1: Clear old data
tk.Label(self.frame_right_info,
font=self.font_step_title,
text="删除之前存的人脸数据文件夹").grid(row=5, column=0, columnspan=2, sticky=tk.W, padx=5, pady=20)
tk.Button(self.frame_right_info,
text='删除全部',
command=self.GUI_clear_data).grid(row=6, column=0, columnspan=3, sticky=tk.W, padx=5, pady=2)
# Step 2: Input name and create folders for face
tk.Label(self.frame_right_info,
font=self.font_step_title,
text="Step 1: 输入姓名").grid(row=7, column=0, columnspan=2, sticky=tk.W, padx=5, pady=20)
tk.Label(self.frame_right_info, text="姓名: ").grid(row=8, column=0, sticky=tk.W, padx=5, pady=0)
self.input_name.grid(row=8, column=1, sticky=tk.W, padx=0, pady=2)
tk.Button(self.frame_right_info,
text='录入',
command=self.GUI_get_input_name).grid(row=8, column=2, padx=5)
tk.Button(self.frame_right_info,
text='更改',
command=self.change_name).grid(row=8, column=3, padx=5)
tk.Button(self.frame_right_info,
text='删除',
command=self.delete_name).grid(row=8, column=4, padx=5)
# Step 3: Save current face in frame
tk.Label(self.frame_right_info,
font=self.font_step_title,
text="Step 2: 保存当前人脸图片").grid(row=9, column=0, columnspan=2, sticky=tk.W, padx=5, pady=20)
tk.Button(self.frame_right_info,
text='保存',
command=self.save_current_face).grid(row=10, column=0, columnspan=3, sticky=tk.W)
# Show log in GUI
self.log_all.grid(row=11, column=0, columnspan=20, sticky=tk.W, padx=5, pady=20)
self.frame_right_info.pack()
# 新建保存人脸图像文件和数据 CSV 文件夹 / Mkdir for saving photos and csv
def pre_work_mkdir(self):
# 新建文件夹 / Create folders to save face images and csv
if os.path.isdir(self.path_photos_from_camera):
pass
else:
os.makedirs(self.path_photos_from_camera)
# 如果有之前录入的人脸, 在之前 person_x 的序号按照 person_x+1 开始录入 / Start from person_x+1
def check_existing_faces(self):
if os.listdir(self.path_photos_from_camera):
# 获取已录入的最后一个人脸序号 / Get the order of latest person
person_list = os.listdir(self.path_photos_from_camera)
for person in person_list:
name = person.split('_')[1]
self.registered_names.append(name)
self.existing_faces = len(person_list)
# 如果第一次存储或者没有之前录入的人脸, 按照 person_1 开始录入 / Start from person_1
else:
self.registered_names.clear()
print("No previous data.")
# 更新 FPS / Update FPS of Video stream
def update_fps(self):
now = time.time()
# 每秒刷新 fps / Refresh fps per second
if str(self.start_time).split(".")[0] != str(now).split(".")[0]:
self.fps_show = self.fps
self.start_time = now
self.frame_time = now - self.frame_start_time
self.fps = 1.0 / self.frame_time
self.frame_start_time = now
formatted_fps = "{:.2f}".format(self.fps)
self.label_fps_info["text"] = str(formatted_fps)
def create_face_folder(self):
# 新建存储人脸的文件夹 / Create the folders for saving faces
self.current_face_dir = self.path_photos_from_camera + \
"person_" + \
self.input_name_char
os.makedirs(self.current_face_dir)
self.log_all["text"] = "\"" + self.current_face_dir + "/\" created!"
self.log_all["fg"] = "green"
logging.info("\n%-40s %s", "新建的人脸文件夹 / Create folders:", self.current_face_dir)
self.ss_cnt = 0 # 将人脸计数器清零 / Clear the cnt of screen shots
self.face_folder_created_flag = True # Face folder already created
def remove_face_dir(self, folder_path):
try:
shutil.rmtree(folder_path)
print(f"Folder '{folder_path}' has been deleted successfully.")
except Exception as e:
print(f"Failed to delete folder '{folder_path}'. Error: {e}")
def save_current_face(self):
if self.face_folder_created_flag:
if self.current_frame_faces_cnt == 1:
if not self.out_of_range_flag:
self.ss_cnt += 1
# 根据人脸大小生成空的图像 / Create blank image according to the size of face detected
self.face_ROI_image = np.zeros((int(self.face_ROI_height * 2), self.face_ROI_width * 2, 3),
np.uint8)
for ii in range(self.face_ROI_height * 2):
for jj in range(self.face_ROI_width * 2):
self.face_ROI_image[ii][jj] = self.current_frame[self.face_ROI_height_start - self.hh + ii][
self.face_ROI_width_start - self.ww + jj]
self.log_all["text"] = "\"" + self.current_face_dir + "/img_face_" + str(
self.ss_cnt) + ".jpg\"" + " 保存成功!"
self.log_all["fg"] = "green"
# 使用Pillow保存图像
img_pil = Image.fromarray(self.face_ROI_image)
img_pil.save(self.current_face_dir + "/img_face_" + str(self.ss_cnt) + ".jpg")
logging.info("%-40s %s/img_face_%s.jpg", "写入本地 / Save into:",
str(self.current_face_dir), str(self.ss_cnt) + ".jpg")
else:
self.log_all["text"] = "人脸不在范围内(人脸框白色才能保存)!"
self.log_all["fg"] = "red"
else:
self.log_all["text"] = "没找到人脸或者找到多个人脸"
self.log_all["fg"] = "red"
else:
self.log_all["text"] = "请先执行step 1"
self.log_all["fg"] = "red"
def get_frame(self):
try:
if self.cap.isOpened():
ret, frame = self.cap.read()
if ret:
return ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
else:
raise Exception("Unable to open the camera")
except Exception as e:
messagebox.showerror("Error", f"没有找到摄像头!!!{e}\n")
print("Error: No video input!!!{e}")
# 获取人脸 / Main process of face detection and saving
def process(self):
ret, self.current_frame = self.get_frame()
faces = detector(self.current_frame, 0)
# Get frame
if ret:
self.update_fps()
self.label_face_cnt["text"] = str(len(faces))
# 检测到人脸 / Face detected
if len(faces) != 0:
# 矩形框 / Show the ROI of faces
for k, d in enumerate(faces):
self.face_ROI_width_start = d.left()
self.face_ROI_height_start = d.top()
# 计算矩形框大小 / Compute the size of rectangle box
self.face_ROI_height = (d.bottom() - d.top())
self.face_ROI_width = (d.right() - d.left())
self.hh = int(self.face_ROI_height / 2)
self.ww = int(self.face_ROI_width / 2)
# 判断人脸矩形框是否超出 480x640 / If the size of ROI > 480x640
if (d.right() + self.ww) > 640 or (d.bottom() + self.hh > 480) or (d.left() - self.ww < 0) or (
d.top() - self.hh < 0):
self.label_warning["text"] = "OUT OF RANGE"
self.label_warning['fg'] = 'red'
self.out_of_range_flag = True
color_rectangle = (255, 0, 0)
else:
self.out_of_range_flag = False
self.label_warning["text"] = ""
color_rectangle = (255, 255, 255)
self.current_frame = cv2.rectangle(self.current_frame,
tuple([d.left() - self.ww, d.top() - self.hh]),
tuple([d.right() + self.ww, d.bottom() + self.hh]),
color_rectangle, 2)
self.current_frame_faces_cnt = len(faces)
# Convert PIL.Image.Image to PIL.Image.PhotoImage
img_Image = Image.fromarray(self.current_frame)
img_PhotoImage = ImageTk.PhotoImage(image=img_Image)
self.label.img_tk = img_PhotoImage
self.label.configure(image=img_PhotoImage)
# Refresh frame
self.win.after(20, self.process)
def run(self):
self.pre_work_mkdir()
self.check_existing_faces()
self.GUI_info()
self.process()
self.win.mainloop()
def main():
logging.basicConfig(level=logging.INFO)
Face_Register_con = Face_Register()
Face_Register_con.run()
if __name__ == '__main__':
main()

View File

@ -1,186 +0,0 @@
# 进行人脸录入 / face register
# 录入多张人脸 / support multi-faces
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
# Mail: coneypo@foxmail.com
# Created at 2018-05-11
# Updated at 2019-02-21
import dlib # 人脸处理的库 Dlib
import numpy as np # 数据处理的库 Numpy
import cv2 # 图像处理的库 OpenCv
import os # 读写文件
import shutil # 读写文件
# Dlib 正向人脸检测器
detector = dlib.get_frontal_face_detector()
# Dlib 68 点特征预测器
predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
# OpenCv 调用摄像头
cap = cv2.VideoCapture(0)
# 设置视频参数
cap.set(3, 480)
# 人脸截图的计数器
cnt_ss = 0
# 存储人脸的文件夹
current_face_dir = 0
# 保存 photos/csv 的路径
path_photos_from_camera = "data/data_faces_from_camera/"
path_csv_from_photos = "data/data_csvs_from_camera/"
# 新建保存人脸图像文件和数据CSV文件夹
def pre_work_mkdir():
# 新建文件夹
if os.path.isdir(path_photos_from_camera):
pass
else:
os.mkdir(path_photos_from_camera)
if os.path.isdir(path_csv_from_photos):
pass
else:
os.mkdir(path_csv_from_photos)
pre_work_mkdir()
##### optional/可选, 默认关闭 #####
# 删除之前存的人脸数据文件夹
def pre_work_deldir():
# 删除之前存的人脸数据文件夹
# 删除 "/data_faces_from_camera/person_x/"...
folders_rd = os.listdir(path_photos_from_camera)
for i in range(len(folders_rd)):
shutil.rmtree(path_photos_from_camera+folders_rd[i])
csv_rd = os.listdir(path_csv_from_photos)
for i in range(len(csv_rd)):
os.remove(path_csv_from_photos+csv_rd[i])
# 这里在每次程序录入之前, 删掉之前存的人脸数据
# 如果这里打开,每次进行人脸录入的时候都会删掉之前的人脸图像文件夹
# pre_work_deldir()
##################################
# 如果有之前录入的人脸
# 在之前 person_x 的序号按照 person_x+1 开始录入
if os.listdir("data/data_faces_from_camera/"):
# 获取已录入的最后一个人脸序号
person_list = os.listdir("data/data_faces_from_camera/")
person_list.sort()
person_num_latest = int(str(person_list[-1]).split("_")[-1])
person_cnt = person_num_latest
# 如果第一次存储或者没有之前录入的人脸, 按照 person_1 开始录入
else:
person_cnt = 0
# 之后用来控制是否保存图像的 flag
save_flag = 1
while cap.isOpened():
# 480 height * 640 width
flag, img_rd = cap.read()
kk = cv2.waitKey(1)
img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY)
# 人脸数 faces
faces = detector(img_gray, 0)
# 待会要写的字体
font = cv2.FONT_HERSHEY_COMPLEX
# 按下 'n' 新建存储人脸的文件夹
if kk == ord('n'):
person_cnt += 1
current_face_dir = path_photos_from_camera + "person_" + str(person_cnt)
os.makedirs(current_face_dir)
print('\n')
print("新建的人脸文件夹: ", current_face_dir)
# 将人脸计数器清零
cnt_ss = 0
# 检测到人脸
if len(faces) != 0:
# 矩形框
for k, d in enumerate(faces):
# 计算矩形大小
# (x,y), (宽度width, 高度height)
pos_start = tuple([d.left(), d.top()])
pos_end = tuple([d.right(), d.bottom()])
# 计算矩形框大小
height = (d.bottom() - d.top())
width = (d.right() - d.left())
hh = int(height/2)
ww = int(width/2)
# 设置颜色 / The color of rectangle of faces detected
color_rectangle = (255, 255, 255)
if (d.right()+ww) > 640 or (d.bottom()+hh > 480) or (d.left()-ww < 0) or (d.top()-hh < 0):
cv2.putText(img_rd, "OUT OF RANGE", (20, 300), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
color_rectangle = (0, 0, 255)
save_flag = 0
else:
color_rectangle = (255, 255, 255)
save_flag = 1
cv2.rectangle(img_rd,
tuple([d.left() - ww, d.top() - hh]),
tuple([d.right() + ww, d.bottom() + hh]),
color_rectangle, 2)
# 根据人脸大小生成空的图像
im_blank = np.zeros((int(height*2), width*2, 3), np.uint8)
if save_flag:
# 按下 's' 保存摄像头中的人脸到本地
if kk == ord('s'):
if os.path.isdir(current_face_dir):
cnt_ss += 1
for ii in range(height*2):
for jj in range(width*2):
im_blank[ii][jj] = img_rd[d.top()-hh + ii][d.left()-ww + jj]
cv2.imwrite(current_face_dir + "/img_face_" + str(cnt_ss) + ".jpg", im_blank)
print("写入本地:", str(current_face_dir) + "/img_face_" + str(cnt_ss) + ".jpg")
else:
print("请在按 'S' 之前先按 'N' 来建文件夹 / Please press 'N' before 'S'")
# 显示人脸数
cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
# 添加说明
cv2.putText(img_rd, "Face Register", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(img_rd, "N: New face folder", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(img_rd, "S: Save current face", (20, 400), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(img_rd, "Q: Quit", (20, 450), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
# 按下 'q' 键退出
if kk == ord('q'):
break
# 窗口显示
# cv2.namedWindow("camera", 0) # 如果需要摄像头窗口大小可调
cv2.imshow("camera", img_rd)
# 释放摄像头
cap.release()
# 删除建立的窗口
cv2.destroyAllWindows()

View File

@ -1,137 +0,0 @@
# 从人脸图像文件中提取人脸特征存入 CSV
# Get features from images and save into features_all.csv
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
# Mail: coneypo@foxmail.com
# Created at 2018-05-11
# Updated at 2019-02-25
# 增加录入多张人脸到 CSV 的功能
# return_128d_features() 获取某张图像的 128D 特征
# write_into_csv() 获取某个路径下所有图像的特征,并写入 CSV
# compute_the_mean() 从 CSV 中读取 128D 特征,并计算特征均值
import cv2
import os
import dlib
from skimage import io
import csv
import numpy as np
import pandas as pd
# 要读取人脸图像文件的路径
path_photos_from_camera = "data/data_faces_from_camera/"
# 储存人脸特征 csv 的路径
path_csv_from_photos = "data/data_csvs_from_camera/"
# Dlib 正向人脸检测器
detector = dlib.get_frontal_face_detector()
# Dlib 人脸预测器
predictor = dlib.shape_predictor("data/data_dlib/shape_predictor_5_face_landmarks.dat")
# Dlib 人脸识别模型
# Face recognition model, the object maps human faces into 128D vectors
facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
# 返回单张图像的 128D 特征
def return_128d_features(path_img):
img = io.imread(path_img)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
faces = detector(img_gray, 1)
print("检测到人脸的图像:", path_img, "\n")
# 因为有可能截下来的人脸再去检测,检测不出来人脸了
# 所以要确保是 检测到人脸的人脸图像 拿去算特征
if len(faces) != 0:
shape = predictor(img_gray, faces[0])
face_descriptor = facerec.compute_face_descriptor(img_gray, shape)
else:
face_descriptor = 0
print("no face")
# print(face_descriptor)
return face_descriptor
# 将文件夹中照片特征提取出来, 写入 CSV
# path_faces_personX: 图像文件夹的路径
# path_csv_from_photos: 要生成的 CSV 路径
def write_into_csv(path_faces_personX, path_csv_from_photos):
photos_list = os.listdir(path_faces_personX)
with open(path_csv_from_photos, "w", newline="") as csvfile:
writer = csv.writer(csvfile)
if photos_list:
for i in range(len(photos_list)):
# 调用return_128d_features()得到128d特征
print("正在读的人脸图像:", path_faces_personX + "/" + photos_list[i])
features_128d = return_128d_features(path_faces_personX + "/" + photos_list[i])
# print(features_128d)
# 遇到没有检测出人脸的图片跳过
if features_128d == 0:
i += 1
else:
writer.writerow(features_128d)
else:
print("Warning: Empty photos in "+path_faces_personX+'/')
writer.writerow("")
# 读取某人所有的人脸图像的数据,写入 person_X.csv
faces = os.listdir(path_photos_from_camera)
faces.sort()
for person in faces:
print("##### " + person + " #####")
print(path_csv_from_photos + person + ".csv")
write_into_csv(path_photos_from_camera + person, path_csv_from_photos + person + ".csv")
print('\n')
# 从 CSV 中读取数据,计算 128D 特征的均值
def compute_the_mean(path_csv_from_photos):
column_names = []
# 128D 特征
for feature_num in range(128):
column_names.append("features_" + str(feature_num + 1))
# 利用 pandas 读取 csv
rd = pd.read_csv(path_csv_from_photos, names=column_names)
if rd.size != 0:
# 存放 128D 特征的均值
feature_mean_list = []
for feature_num in range(128):
tmp_arr = rd["features_" + str(feature_num + 1)]
tmp_arr = np.array(tmp_arr)
# 计算某一个特征的均值
tmp_mean = np.mean(tmp_arr)
feature_mean_list.append(tmp_mean)
else:
feature_mean_list = []
return feature_mean_list
# 存放所有特征均值的 CSV 的路径
path_csv_from_photos_feature_all = "data/features_all.csv"
# 存放人脸特征的 CSV 的路径
path_csv_from_photos = "data/data_csvs_from_camera/"
with open(path_csv_from_photos_feature_all, "w", newline="") as csvfile:
writer = csv.writer(csvfile)
csv_rd = os.listdir(path_csv_from_photos)
csv_rd.sort()
print("##### 得到的特征均值 / The generated average values of features stored in: #####")
for i in range(len(csv_rd)):
feature_mean_list = compute_the_mean(path_csv_from_photos + csv_rd[i])
print(path_csv_from_photos + csv_rd[i])
writer.writerow(feature_mean_list)

View File

@ -1,58 +0,0 @@
# OpenCv 调用摄像头
# 默认调用笔记本摄像头
# Author: coneypo
# Blog: http://www.cnblogs.com/AdaminXie
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
# Mail: coneypo@foxmail.com
import cv2
cap = cv2.VideoCapture(0)
# cap.set(propId, value)
# 设置视频参数: propId - 设置的视频参数, value - 设置的参数值
cap.set(3, 480)
# cap.isOpened() 返回 true/false, 检查摄像头初始化是否成功
print(cap.isOpened())
# cap.read()
"""
返回两个值
先返回一个布尔值, 如果视频读取正确, 则为 True, 如果错误, 则为 False;
也可用来判断是否到视频末尾;
再返回一个值, 为每一帧的图像, 该值是一个三维矩阵;
通用接收方法为:
ret,frame = cap.read();
ret: 布尔值;
frame: 图像的三维矩阵;
这样 ret 存储布尔值, frame 存储图像;
若使用一个变量来接收两个值, 如:
frame = cap.read()
则 frame 为一个元组, 原来使用 frame 处需更改为 frame[1]
"""
while cap.isOpened():
ret_flag, img_camera = cap.read()
cv2.imshow("camera", img_camera)
# 每帧数据延时 1ms, 延时为0, 读取的是静态帧
k = cv2.waitKey(1)
# 按下 's' 保存截图
if k == ord('s'):
cv2.imwrite("test.jpg", img_camera)
# 按下 'q' 退出
if k == ord('q'):
break
# 释放所有摄像头
cap.release()
# 删除建立的所有窗口
cv2.destroyAllWindows()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 KiB

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
dlib==19.17.0
numpy==1.22.0
scikit-image==0.18.3
pandas==1.3.4
opencv-python==4.5.4.58