Compare commits
2 Commits
DLib_Face_
...
for_patch
| Author | SHA1 | Date | |
|---|---|---|---|
| cf88a17a47 | |||
| cbb8b935ce |
5
.idea/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
8
.idea/Dlib_face_recognition_from_camera.iml
generated
@ -2,9 +2,13 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/data" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (Dlib_face_recognition_from_camera) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="projectConfiguration" value="pytest" />
|
||||
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||
</component>
|
||||
</module>
|
||||
4
.idea/encodings.xml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||
</project>
|
||||
12
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,12 @@
|
||||
<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>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
5
.idea/misc.xml
generated
@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<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" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
1
.idea/vcs.xml
generated
@ -1,7 +1,6 @@
|
||||
<?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>
|
||||
357
.idea/workspace.xml
generated
Normal file
@ -0,0 +1,357 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="e58b655a-3a9b-4001-b4da-39e07ab46629" name="Default Changelist" 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" />
|
||||
</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="FUSProjectUsageTrigger">
|
||||
<session id="2127136746">
|
||||
<usages-collector id="statistics.lifecycle.project">
|
||||
<counts>
|
||||
<entry key="project.closed" value="1" />
|
||||
<entry key="project.open.time.4" value="1" />
|
||||
<entry key="project.open.time.5" value="1" />
|
||||
<entry key="project.opened" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.open">
|
||||
<counts>
|
||||
<entry key="png" value="1" />
|
||||
<entry key="txt" value="1" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.open">
|
||||
<counts>
|
||||
<entry key="Image" value="1" />
|
||||
<entry key="PLAIN_TEXT" value="1" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.edit">
|
||||
<counts>
|
||||
<entry key="rst" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.edit">
|
||||
<counts>
|
||||
<entry key="ReST" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
</session>
|
||||
</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="67">
|
||||
<caret line="12" column="34" selection-start-line="12" selection-start-column="34" selection-end-line="12" selection-end-column="34" />
|
||||
</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="83">
|
||||
<caret line="16" lean-forward="true" selection-start-line="16" selection-end-line="16" />
|
||||
<folding>
|
||||
<element signature="e#491#501#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/face_reco_from_camera.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="127">
|
||||
<caret line="89" column="22" lean-forward="true" selection-start-line="89" selection-start-column="22" selection-end-line="89" selection-end-column="22" />
|
||||
<folding>
|
||||
<element signature="e#230#264#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/requirements.txt">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="76">
|
||||
<caret line="4" column="16" lean-forward="true" selection-start-line="4" selection-start-column="16" selection-end-line="4" selection-end-column="16" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/introduction/face_reco_two_people_in_database.png">
|
||||
<provider selected="true" editor-type-id="images" />
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<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="513">
|
||||
<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="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="IdeDocumentHistory">
|
||||
<option name="CHANGED_PATHS">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/requirements.txt" />
|
||||
<option value="$PROJECT_DIR$/README.rst" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds">
|
||||
<option name="x" value="740" />
|
||||
<option name="y" value="86" />
|
||||
<option name="width" value="1194" />
|
||||
<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>
|
||||
<path>
|
||||
<item name="Dlib_face_recognition_from_camera" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="Dlib_face_recognition_from_camera" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="introduction" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</subPane>
|
||||
</pane>
|
||||
<pane id="Scope" />
|
||||
</panes>
|
||||
</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>
|
||||
<list>
|
||||
<item itemvalue="Python.get_faces_from_camera" />
|
||||
<item itemvalue="Python.get_features_into_CSV" />
|
||||
<item itemvalue="Python.face_reco_from_camera" />
|
||||
</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" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="e58b655a-3a9b-4001-b4da-39e07ab46629" name="Default Changelist" 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="842" y="68" width="1194" height="987" extended-state="0" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.28200692" />
|
||||
<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 anchor="bottom" id="Run" order="2" visible="true" weight="0.4976526" />
|
||||
<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" weight="0.32983682" />
|
||||
<window_info anchor="bottom" id="Terminal" order="8" weight="0.39976552" />
|
||||
<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="VcsContentAnnotationSettings">
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</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" />
|
||||
<entry file="file://$PROJECT_DIR$/data/features_all.csv" />
|
||||
<entry file="file://$PROJECT_DIR$/introduction/face_reco_single_person_custmize_name.png" />
|
||||
<entry file="file://$PROJECT_DIR$/how_to_use_camera.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="513">
|
||||
<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$/get_features_into_CSV.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="83">
|
||||
<caret line="16" lean-forward="true" selection-start-line="16" selection-end-line="16" />
|
||||
<folding>
|
||||
<element signature="e#491#501#0" expanded="true" />
|
||||
</folding>
|
||||
</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="67">
|
||||
<caret line="12" column="34" selection-start-line="12" selection-start-column="34" selection-end-line="12" selection-end-column="34" />
|
||||
</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="127">
|
||||
<caret line="89" column="22" lean-forward="true" selection-start-line="89" selection-start-column="22" selection-end-line="89" selection-end-column="22" />
|
||||
<folding>
|
||||
<element signature="e#230#264#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/requirements.txt">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="76">
|
||||
<caret line="4" column="16" lean-forward="true" selection-start-line="4" selection-start-column="16" selection-end-line="4" selection-end-column="16" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/introduction/face_reco_two_people_in_database.png">
|
||||
<provider selected="true" editor-type-id="images" />
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/README.rst">
|
||||
<provider selected="true" editor-type-id="restructured-text-editor" />
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
||||
21
LICENSE
@ -1,21 +0,0 @@
|
||||
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.
|
||||
140
README.rst
Normal file
@ -0,0 +1,140 @@
|
||||
Face recognition from camera
|
||||
############################
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
Detect and recognize single/multi-faces from camera;
|
||||
|
||||
调用摄像头进行人脸识别,支持多张人脸同时识别;
|
||||
|
||||
|
||||
#. 摄像头人脸录入 / Face register
|
||||
|
||||
.. image:: introduction/get_face_from_camera.png
|
||||
:align: center
|
||||
|
||||
请不要离摄像头过近,人脸超出摄像头范围时会有 "OUT OF RANGE" 提醒 /
|
||||
Please do not too close to the camera, or you can't save faces with "OUT OF RANGE" warning;
|
||||
|
||||
.. image:: introduction/get_face_from_camera_out_of_range.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:
|
||||
|
||||
一张已录入人脸 + 未录入 unknown 人脸:
|
||||
|
||||
.. image:: introduction/face_reco_two_people.png
|
||||
:align: center
|
||||
|
||||
同时识别多张已录入人脸:
|
||||
|
||||
.. image:: introduction/face_reco_two_people_in_database.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.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
此项目中人脸识别的实现流程 / The design of this repo:
|
||||
|
||||
.. image:: introduction/overview.png
|
||||
:align: center
|
||||
|
||||
Steps
|
||||
*****
|
||||
|
||||
#. 下载源码 / Download from website or via GitHub Desktop in windows, or clone repo in Ubuntu
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/coneypo/Dlib_face_recognition_from_camera
|
||||
|
||||
#. 进行 face register / 人脸信息采集录入
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 get_face_from_camera.py
|
||||
|
||||
#. 提取所有录入人脸数据存入 features_all.csv
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 get_features_into_CSV.py
|
||||
|
||||
#. 调用摄像头进行实时人脸识别
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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"
|
||||
|
||||
* 将捕获到的人脸数据和之前存的人脸数据进行对比计算欧式距离, 由此判断是否是同一个人;
|
||||
|
||||
修改显示的人名 / If you want customize the names shown, please refer to this patch and modify the code: https://github.com/coneypo/Dlib_face_recognition_from_camera/commit/58466ce87bf3a42ac5ef855b791bf8c658d408df?diff=unified
|
||||
|
||||
|
||||
.. image:: introduction/face_reco_single_person_customize_name.png
|
||||
:align: center
|
||||
|
||||
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.
|
||||
0
data/data_dlib/dlib_face_recognition_resnet_model_v1.dat
Executable file → Normal file
0
data/data_dlib/shape_predictor_68_face_landmarks.dat
Executable file → Normal file
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 42 KiB |
BIN
data/data_faces_examples/John_Salley/000179_02159509.jpg
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
data/data_faces_examples/John_Salley/000183_02159543.jpg
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
data/data_faces_examples/John_Salley/000186_02159346.jpg
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
data/data_faces_examples/John_Salley/000189_02159361.jpg
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
data/data_faces_examples/John_Salley/000190_02159501.jpg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
data/data_faces_examples/John_Salley/000192_02159531.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
data/data_faces_examples/John_Salley/000194_02159572.jpg
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
data/data_faces_examples/John_Salley/000197_02159322.jpg
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
data/data_faces_examples/John_Salley/000197_02159525.jpg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
data/data_faces_examples/John_Salley/000198_02159470.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
data/data_faces_examples/John_Salley/000200_02159354.jpg
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
data/data_faces_examples/John_Savage/000264_01099001.jpg
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
data/data_faces_examples/John_Savage/000274_01099061.jpg
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
data/data_faces_examples/John_Savage/000277_01099000.jpg
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
data/data_faces_examples/John_Savage/000289_01099139.jpg
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
data/data_faces_examples/John_Savage/000290_01099067.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Savage/000290_01099090.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
data/data_faces_examples/John_Savage/000291_01099023.jpg
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
data/data_faces_examples/John_Savage/000291_01099214.jpg
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
data/data_faces_examples/John_Savage/000293_01099081.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Savage/000296_01099007.jpg
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
data/data_faces_examples/John_Savage/000299_01099008.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
data/data_faces_examples/John_Schneider/000288_00925786.jpg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
data/data_faces_examples/John_Schneider/000302_00925785.jpg
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
data/data_faces_examples/John_Schneider/000307_00925823.jpg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
data/data_faces_examples/John_Schneider/000325_00925954.jpg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
data/data_faces_examples/John_Schneider/000326_00925765.jpg
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
data/data_faces_examples/John_Schneider/000326_00926089.jpg
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
data/data_faces_examples/John_Schneider/000326_00926128.jpg
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
data/data_faces_examples/John_Schneider/000326_00926139.jpg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
data/data_faces_examples/John_Schneider/000329_00925859.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Schneider/000329_00925963.jpg
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
data/data_faces_examples/John_Schneider/000331_00926012.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Shimkus/000373_03228153.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Shimkus/000375_03227651.jpg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
data/data_faces_examples/John_Shimkus/000376_02340068.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
data/data_faces_examples/John_Shimkus/000378_02340151.jpg
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
data/data_faces_examples/John_Shimkus/000378_03227610.jpg
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
data/data_faces_examples/John_Shimkus/000383_03227939.jpg
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
data/data_faces_examples/John_Shimkus/000385_03227766.jpg
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
data/data_faces_examples/John_Shimkus/000388_03227773.jpg
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
data/data_faces_examples/John_Shimkus/000390_03227666.jpg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
data/data_faces_examples/John_Shimkus/000394_02340150.jpg
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
data/data_faces_examples/John_Shimkus/000396_03227722.jpg
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
data/data_faces_examples/John_Simm/000288_00470387.jpg
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
data/data_faces_examples/John_Simm/000297_00470170.jpg
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
data/data_faces_examples/John_Simm/000300_00470148.jpg
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
data/data_faces_examples/John_Simm/000304_00470122.jpg
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
data/data_faces_examples/John_Simm/000305_00470162.jpg
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
data/data_faces_examples/John_Simm/000305_00470717.jpg
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
data/data_faces_examples/John_Simm/000306_00470222.jpg
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
data/data_faces_examples/John_Simm/000306_00470223.jpg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
data/data_faces_examples/John_Simm/000309_00470287.jpg
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
data/data_faces_examples/John_Simm/000310_00470421.jpg
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
data/data_faces_examples/John_Simm/000310_00470511.jpg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
data/data_faces_examples/John_test/000183_02159543.jpg
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
data/data_faces_examples/John_test/000297_00470170.jpg
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
data/data_faces_examples/John_test/000376_02340068.jpg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
@ -1 +0,0 @@
|
||||
马斌昊,-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
|
||||
|
376
face_reco_from_camera.py
Executable file → Normal file
@ -1,266 +1,156 @@
|
||||
# Copyright (C) 2018-2021 coneypo
|
||||
# SPDX-License-Identifier: MIT
|
||||
# 摄像头实时人脸识别
|
||||
# Real-time face recognition
|
||||
|
||||
import dlib
|
||||
import numpy as np
|
||||
import cv2
|
||||
import pandas as pd
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
# Author: coneypo
|
||||
# Blog: http://www.cnblogs.com/AdaminXie
|
||||
# GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera
|
||||
|
||||
# Dlib 正向人脸检测器
|
||||
# Created at 2018-05-11
|
||||
# Updated at 2019-03-23
|
||||
|
||||
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
|
||||
# Refer this tutorial: http://dlib.net/python/index.html#dlib.face_recognition_model_v1
|
||||
facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
|
||||
|
||||
|
||||
# 计算两个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)))
|
||||
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)
|
||||
|
||||
# 用来存放所有录入人脸特征的数组
|
||||
# the array to save the features of faces in the database
|
||||
features_known_arr = []
|
||||
|
||||
# 读取已知人脸数据
|
||||
# print 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 检测器和预测器
|
||||
# The detector and predictor will be used
|
||||
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")
|
||||
|
||||
# 创建 cv2 摄像头对象
|
||||
# cv2.VideoCapture(0) to use the default camera of PC,
|
||||
# and you can use local video name by use cv2.VideoCapture(filename)
|
||||
cap = cv2.VideoCapture(0)
|
||||
|
||||
class Face_Recognizer:
|
||||
def __init__(self):
|
||||
self.face_feature_known_list = []
|
||||
self.face_name_known_list = []
|
||||
# cap.set(propId, value)
|
||||
# 设置视频参数,propId 设置的视频参数,value 设置的参数值
|
||||
cap.set(3, 480)
|
||||
|
||||
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 检查初始化是否成功
|
||||
# when the camera is open
|
||||
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 to write later
|
||||
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
|
||||
# 存储当前摄像头中捕获到的所有人脸的坐标/名字
|
||||
# the list to save the positions and names of current faces captured
|
||||
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 键退出
|
||||
# press 'q' to exit
|
||||
if kk == ord('q'):
|
||||
break
|
||||
else:
|
||||
# 检测到人脸 when face detected
|
||||
if len(faces) != 0:
|
||||
# 获取当前捕获到的图像的所有人脸的特征,存储到 features_cap_arr
|
||||
# get the features captured and save into 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)
|
||||
# 遍历捕获到的图像中所有的人脸
|
||||
# traversal all the faces in the database
|
||||
for k in range(len(faces)):
|
||||
# 让人名跟随在矩形框的下方
|
||||
# 确定人名的位置坐标
|
||||
# 先默认所有人不认识,是 unknown
|
||||
# set the default names of faces with "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
|
||||
|
||||
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]
|
||||
# 每个捕获人脸的名字坐标 the positions of faces captured
|
||||
pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top())/4)]))
|
||||
|
||||
# 对于某张人脸,遍历所有存储的人脸特征
|
||||
# for every faces detected, compare the faces in the database
|
||||
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"
|
||||
|
||||
# 矩形框
|
||||
# draw rectangle
|
||||
for kk, d in enumerate(faces):
|
||||
# 绘制矩形框
|
||||
cv2.rectangle(img_rd,
|
||||
(faces[k].left(), faces[k].top()),
|
||||
(faces[k].right(), faces[k].bottom()),
|
||||
(255, 255, 255), 2)
|
||||
cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)
|
||||
|
||||
self.current_frame_face_cnt = len(faces)
|
||||
# 在人脸框下面写人脸名字
|
||||
# write names under rectangle
|
||||
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)
|
||||
|
||||
# 8. 写名字
|
||||
img_rd = self.draw_name(img_rd)
|
||||
print("Faces in camera now:", name_namelist, "\n")
|
||||
|
||||
# 显示图像
|
||||
cv2.imshow("camera", 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)
|
||||
|
||||
# 9. 更新 FPS
|
||||
self.update_fps()
|
||||
# 窗口显示 show with opencv
|
||||
cv2.imshow("camera", img_rd)
|
||||
|
||||
# 清理资源
|
||||
cv2.destroyAllWindows()
|
||||
print("程序正常退出")
|
||||
# 释放摄像头 release camera
|
||||
cap.release()
|
||||
|
||||
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()
|
||||
# 删除建立的窗口 delete all the windows
|
||||
cv2.destroyAllWindows()
|
||||
@ -1,306 +0,0 @@
|
||||
# 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()
|
||||
@ -1,136 +0,0 @@
|
||||
# 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
@ -1,232 +0,0 @@
|
||||
# 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
@ -1,378 +0,0 @@
|
||||
# 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()
|
||||
197
get_faces_from_camera.py
Normal file
@ -0,0 +1,197 @@
|
||||
# 进行人脸录入 / 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-03-23
|
||||
|
||||
import dlib # 人脸处理的库 Dlib
|
||||
import numpy as np # 数据处理的库 Numpy
|
||||
import cv2 # 图像处理的库 OpenCv
|
||||
|
||||
import os # 读写文件
|
||||
import shutil # 读写文件
|
||||
|
||||
# Dlib 正向人脸检测器 / frontal face detector
|
||||
detector = dlib.get_frontal_face_detector()
|
||||
|
||||
# Dlib 68 点特征预测器 / 68 points features predictor
|
||||
predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
|
||||
|
||||
# OpenCv 调用摄像头 use camera
|
||||
cap = cv2.VideoCapture(0)
|
||||
|
||||
# 设置视频参数 set camera
|
||||
cap.set(3, 480)
|
||||
|
||||
# 人脸截图的计数器 the counter for screen shoot
|
||||
cnt_ss = 0
|
||||
|
||||
# 存储人脸的文件夹 the folder to save faces
|
||||
current_face_dir = ""
|
||||
|
||||
# 保存 photos/csv 的路径 the directory to save photos/csv
|
||||
path_photos_from_camera = "data/data_faces_from_camera/"
|
||||
path_csv_from_photos = "data/data_csvs_from_camera/"
|
||||
|
||||
|
||||
# 新建保存人脸图像文件和数据CSV文件夹
|
||||
# mkdir for saving photos and csv
|
||||
def pre_work_mkdir():
|
||||
|
||||
# 新建文件夹 / make folders to save faces images and csv
|
||||
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/可选, 默认关闭 #####
|
||||
# 删除之前存的人脸数据文件夹
|
||||
# delete the old data of faces
|
||||
def pre_work_del_old_face_folders():
|
||||
# 删除之前存的人脸数据文件夹
|
||||
# 删除 "/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_del_old_face_folders()
|
||||
##################################
|
||||
|
||||
|
||||
# 如果有之前录入的人脸
|
||||
# 在之前 person_x 的序号按照 person_x+1 开始录入
|
||||
# if old face exists, start from 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 开始录入
|
||||
# start from person_1
|
||||
else:
|
||||
person_cnt = 0
|
||||
|
||||
# 之后用来控制是否保存图像的 flag / the flag to control if save
|
||||
save_flag = 1
|
||||
|
||||
# 之后用来检查是否先按 'n' 再按 's' / the flag to check if press 'n' before 's'
|
||||
press_n_flag = 0
|
||||
|
||||
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 to write
|
||||
font = cv2.FONT_HERSHEY_COMPLEX
|
||||
|
||||
# 按下 'n' 新建存储人脸的文件夹 / press 'n' to create the folders for saving faces
|
||||
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("新建的人脸文件夹 / Create folders: ", current_face_dir)
|
||||
|
||||
cnt_ss = 0 # 将人脸计数器清零 / clear the cnt of faces
|
||||
press_n_flag = 1 # 已经按下 'n' / have pressed 'n'
|
||||
|
||||
# 检测到人脸 / if face detected
|
||||
if len(faces) != 0:
|
||||
# 矩形框
|
||||
# show the rectangle box
|
||||
for k, d in enumerate(faces):
|
||||
# 计算矩形大小
|
||||
# we need to compute the width and height of the box
|
||||
# (x,y), (宽度width, 高度height)
|
||||
pos_start = tuple([d.left(), d.top()])
|
||||
pos_end = tuple([d.right(), d.bottom()])
|
||||
|
||||
# 计算矩形框大小 / compute the size of rectangle box
|
||||
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)
|
||||
|
||||
# 根据人脸大小生成空的图像 / create blank image according to the size of face detected
|
||||
im_blank = np.zeros((int(height*2), width*2, 3), np.uint8)
|
||||
|
||||
save_flag = 1;
|
||||
if save_flag:
|
||||
# 按下 's' 保存摄像头中的人脸到本地 / press 's' to save faces into local images
|
||||
if kk == ord('s'):
|
||||
# 检查有没有先按'n'新建文件夹 / check if you have pressed 'n'
|
||||
if press_n_flag:
|
||||
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("写入本地 / Save into:", str(current_face_dir) + "/img_face_" + str(cnt_ss) + ".jpg")
|
||||
else:
|
||||
print("请在按 'S' 之前先按 'N' 来建文件夹 / Please press 'N' before 'S'")
|
||||
|
||||
# 显示人脸数 / show the numbers of faces detected
|
||||
cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
|
||||
|
||||
# 添加说明 / add some statements
|
||||
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' 键退出 / press 'q' to exit
|
||||
if kk == ord('q'):
|
||||
break
|
||||
|
||||
# 如果需要摄像头窗口大小可调 / uncomment this line if you want the camera window is resizeable
|
||||
# cv2.namedWindow("camera", 0)
|
||||
|
||||
cv2.imshow("camera", img_rd)
|
||||
|
||||
# 释放摄像头 / release camera
|
||||
cap.release()
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
137
get_features_into_CSV.py
Normal file
@ -0,0 +1,137 @@
|
||||
# 从人脸图像文件中提取人脸特征存入 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("%-40s %-20s" % ("检测到人脸的图像 / image with faces detected:", 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("%-40s %-20s" % ("正在读的人脸图像 / image to read:", 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 + '/', '\n')
|
||||
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)
|
||||
58
how_to_use_camera.py
Normal file
@ -0,0 +1,58 @@
|
||||
# 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()
|
||||
BIN
introduction/Dlib_Face_recognition_by_coneypo.pptx
Normal file
BIN
introduction/face_reco_single_person.png
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
introduction/face_reco_single_person_customize_name.png
Normal file
|
After Width: | Height: | Size: 457 KiB |
BIN
introduction/face_reco_two_people.png
Normal file
|
After Width: | Height: | Size: 499 KiB |
BIN
introduction/face_reco_two_people_in_database.png
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
introduction/get_face_from_camera.png
Normal file
|
After Width: | Height: | Size: 416 KiB |
BIN
introduction/get_face_from_camera_out_of_range.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
introduction/overview.png
Normal file
|
After Width: | Height: | Size: 445 KiB |
@ -1,5 +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
|
||||
numpy==1.15.1
|
||||
opencv-python==4.0.0.21
|
||||
pandas==0.23.4
|
||||
scikit-image==0.14.0
|
||||