0x00 项目介绍
围绕MAX78000,本人想要实现对于特定人脸或普通人脸的识别做出不同的操作的小型LED灯。日后可以在此基础上扩展实现区域内供电系统的开关等功能。
0x10 项目设计思路
主要为三大项:人脸识别算法,LED灯,模型训练
人脸识别算法我准备使用ADI SDK内的facial_recognition 项目example,并在此基础上修改使用串口输出当前检测的位置点(本人并没有合适的显示屏,设想使用裱框坐标输出的方式调试输出端。
LED灯我尝试了解密控制米家台灯(使用了很少出货的一块射频芯片,资料极少)失败,尝试解包米家的通信协议(涉及到可能的法律问题)失败。最后用了网购的LED热成像显示灯,具有充电模块与LED灯和匀光板,去除热释电部分后可以使用。
模型训练使用了我的面部数据,与相关的解析数据。使用了内部的gen_db脚本生成模型头文件并且放入内存卡中作为基础数据。
0x20 搜集素材的思路
需要的收集素材主要有:
- SDK
- 训练集
- Eclipse
SDK是指msdk 这里的clone我成功clone后,发现似乎无法正常编译使用,随后在https://analog-devices-msdk.github.io/msdk/USERGUIDE/#installation 找到合适的位置进行自动化安装
完成后可以在开始菜单下生成
这里自动安装了eclipse,可以直接通过eclipse完成工作。
训练集是指MaximIntegratedAI 下的 ai8x-training 、 ai8x-synthesis ,可以直接使用git clone导入,也就是我上图的train_datas文件夹下
这样就可以进行训练了。我还收集了某些训练集,但是实际上可能很难使用上。
0x30 预训练实现过程
首先实现WSL2.
随后进入当前目录,加载pip数据结构并使用python单独的环境
我使用scripts/train_faceid.sh "$@"指令进行训练参数测试。随后片刻事件可以测试完成,期间可能会下载很多测试集,这都是需要仔细寻找的的(前提是网络给力)
最后的结果一般是这样的
0x40 实现部分
0x41 获取当前图像
使用了官方的demo: Examples\MAX78000\CameraIF进行获取,因为当前python环境使用的问题,导致官方demo的python无法使用。所以使用UWP自行开发了一个读取软件,根据IF的串口协议进行读取,实际输出约5帧每秒。
而输出图片似乎有一定的色缺,不过实际使用没有问题。不清楚是否因为本机图片转换格式与实际不同导致。
0x42 训练
根据demo内readme说明,本人截取了自己的正面、四周约20%、一张笑脸的图片,随后使用
source /mnt/g/MaximSDK/train_datas/ai8x-training/venv/bin/activate
python db_gen/generate_face_db.py --db db --db-filename embeddings --include-path include
可以看到最后生成了embedding头文件。
0x43 代码修改
这里提供了关于以官方SDK中demo下提供的git diff状态,可以更加简明查看当前文件修改情况。并去除了本人部分关键信息以保护个人隐私
.settings/language.settings.xml | 2 +-
include/post_process.h | 2 ++
main.c | 32 +++++++++++++++++++++++++++++++-
src/facedetection.c | 2 +-
src/post_process.c | 13 ++++++++++---
5 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index f055255..118e8e5 100644
--- a/.settings/language.settings.xml
+++ b/.settings/language.settings.xml
@@ -5,7 +5,7 @@
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
- <provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-1" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT内置GCC交叉编译器设置" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
+ <provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-1" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
diff --git a/include/post_process.h b/include/post_process.h
index 62364dd..cfce5bc 100644
--- a/include/post_process.h
+++ b/include/post_process.h
@@ -68,3 +68,5 @@ void get_cxcy(float *cxcy, int prior_idx);
void gcxgcy_to_cxcy(float *cxcy, int prior_idx, float *priors_cxcy);
void cxcy_to_xy(float *xy, float *cxcy);
void localize_objects(void);
+uint8_t* get_face_box();
+
diff --git a/main.c b/main.c
index a82e5df..5cdbe2f 100644
--- a/main.c
+++ b/main.c
@@ -61,6 +61,7 @@
#include "faceID.h"
#include "embedding_process.h"
#include "utils.h"
+#include "pb.h"
#define CONSOLE_BAUD 115200
@@ -74,10 +75,24 @@ mxc_uart_regs_t *CommUart;
area_t area = { 50, 290, 180, 30 };
#endif
+
+void check_face_ok_ret()
+{
+ get_face_box();
+
+}
+
+
+#define MXC_GPIO_PORT_OUT MXC_GPIO3
+#define MXC_GPIO_PIN_OUT MXC_GPIO_PIN_1
+
+
// *****************************************************************************
int main(void)
{
int ret = 0;
+ mxc_gpio_cfg_t gpio_out;
+
int slaveAddress;
int id;
int dma_channel;
@@ -181,6 +196,14 @@ int main(void)
#endif
#endif
+ /* Setup output pin. */
+ gpio_out.port = MXC_GPIO_PORT_OUT;
+ gpio_out.mask = MXC_GPIO_PIN_OUT;
+ gpio_out.pad = MXC_GPIO_PAD_NONE;
+ gpio_out.func = MXC_GPIO_FUNC_OUT;
+ gpio_out.vssel = MXC_GPIO_VSSEL_VDDIO;
+ gpio_out.drvstr = MXC_GPIO_DRVSTR_0;
+ MXC_GPIO_Config(&gpio_out);
/* Initilize SD card */
SD_Init();
uint32_t t1 = utils_get_time_ms();
@@ -189,12 +212,18 @@ int main(void)
if (face_detected == 0) {
// run face detection
face_detection();
+
+
undetect_count++;
+ if(undetect_count == 1000)
+ MXC_GPIO_OutClr(gpio_out.port, gpio_out.mask);
} else // face is detected
{
PR_DEBUG("Face Detected");
// run face id
face_id();
+ if(undetect_count >20)
+ MXC_GPIO_OutSet(gpio_out.port, gpio_out.mask);
undetect_count = 0;
if (reload_faceid) {
@@ -204,7 +233,8 @@ int main(void)
// reload weights for next face detection
reload_facedet = 1;
}
- PR_DEBUG("\nTotal detect test: %dms", undetect_count);
+
+ PR_DEBUG("\nTotal detect test: %d", undetect_count);
#ifdef TFT_ENABLE
if (undetect_count > 5) {
MXC_TFT_SetRotation(ROTATE_180);
diff --git a/src/facedetection.c b/src/facedetection.c
index 81eaf39..c949e01 100644
--- a/src/facedetection.c
+++ b/src/facedetection.c
@@ -69,7 +69,7 @@ int face_detection(void)
/* Sleep until camera interrupt */
// MXC_LP_EnterSleepMode();
-#define PRINT_TIME 1
+#define PRINT_TIME 0
#if (PRINT_TIME == 1)
/* Get current time */
uint32_t process_time = utils_get_time_ms();
diff --git a/src/post_process.c b/src/post_process.c
index c976577..f3ee70d 100644
--- a/src/post_process.c
+++ b/src/post_process.c
@@ -410,6 +410,11 @@ void box_sanity_check(float *xy)
if (error)
PR_DEBUG("Corrected: %d\n", error);
}
+uint8_t* get_face_box()
+{
+ return box;
+}
+
void localize_objects(void)
{
@@ -449,14 +454,15 @@ void localize_objects(void)
box[1] = (uint8_t)(IMAGE_SIZE_Y * xy[1]);
box[2] = (uint8_t)(IMAGE_SIZE_X * xy[2]);
box[3] = (uint8_t)(IMAGE_SIZE_Y * xy[3]);
-#if 0
+
PR_DEBUG("class: %d, prior_idx: %d, prior: %d, x1: %.2f, y1: %.2f, x2: %.2f, y2: "
"%.2f \n",
class_idx + 1, prior_idx, global_prior_idx, xy[0], xy[1], xy[2], xy[3]);
-#else
+ float scheck = (box[2] - box[0])* (box[3] - box[1]);
PR_DEBUG("x1:%d y1:%d x2:%d y2:%d\n", box[0], box[1], box[2], box[3]);
PR_DEBUG("width:%d heigth:%d\n", box[2] - box[0], box[3] - box[1]);
-#endif
+ PR_DEBUG("area:%f\n",scheck);
+
face_detected = 1;
draw_obj_rect(xy, IMAGE_SIZE_X, IMAGE_SIZE_Y);
#endif
@@ -482,4 +488,5 @@ void localize_objects(void)
draw_obj_rect(max_xy, IMAGE_SIZE_X, IMAGE_SIZE_Y);
}
#endif
+
}
这段代码主要功能有两点:
- 实现实际的开关GPIO的功能。
- 调控当前输出结构,保证在终端上可以完整看到当前的坐标(方框四周)面积(像素点)
0x44 程序流程
0x45 硬件实现
我有了一个LED灯,拆开看很简单,是一个具有延时功能,使用运放比较红外热释电进行输出,同时使用阻容进行充放电进行延时设计。
修改方案也很简单粗暴:找到运放输入端与电源端,使用电源板转换电池电压进入单板,实现单点输出控制LED,随后就可以拆掉热释电避免干扰。
最后打出来一看就很小,看起来就可以直接放进去,盖上盖子一点也看不出来
旁边就是成品LED灯,可以看到非常小,完全可以藏在外壳里面,事实上也是可以的,飞线就是朴实无华的飞一下,因为飞线的原因,目前不需要排母也可以使用。
但是实际飞线之后本人发现完全无法工作……电源板输出完全正常,3.3和1.8都很稳。随后查看羽毛板原理图发现错误理解了3.3V,应该从5V开始供电,3V3为输出。所以只能退而求其次,将电池的输入修改到开关断开测,随后接入单板。
接好后发现依然无法供电,只能再更换为LED由GPIO推动运放,GND与单板共地。在OFF的时候就可以接入电源实现LED输出(因为电池已经被改到连接负端了),ON且没有USB给羽毛版供电时使用热释电输出(其实正确做法是需要再做一块4.2V转5V的单板但是项目已经没时间给我做这个了)。不过也算是实现了具体的功能。
随后使用透明双面胶将羽毛板与灯匀光板呈约100°排布,以保证当前可以在正面识别到人脸的同时向下面发光(毕竟对着脸才能发光岂不是很傻φ(* ̄0 ̄)