硬件用料
项目背景和原理
电子产品的丰富化,带来一个很大的问题就是青少年儿童的用眼健康问题。很多小朋友在看电视、平板时喜欢凑到屏幕前近距离观看。这很容易导致近视问题。虽然很多电子产品宣称的蓝光护眼,但小学,甚至幼儿园出现越来越多的视力矫正的案例发生。
本项目考虑BW21-CBV-Kit搭载了高清广角摄像头,加上其强大的硬件性能可以轻松胜任人脸检测、人脸识别等任务。利用“近大远小”的视觉特征,使用人脸检测获取人脸区域的大小来判定人脸距离摄像头的远近来进行近距离的提醒,实现预防近视的功能。
正常坐姿下,摄像头获取的图像如下图:

在距离过近时:

从两个图的对比上很明显可以看出,人脸区域的占比直接反映了人脸距离摄像头的远近。
软件实现
软件实现上,本项目参考案例中的人脸检测项目

并使用开发板上的蓝色LED灯作为提示, 在距离过近时直接点亮蓝色LED灯进行提醒。

软件代码如下:
#include "WiFi.h"
#include "StreamIO.h"
#include "VideoStream.h"
#include "RTSP.h"
#include "NNFaceDetection.h"
#include "VideoStreamOverlay.h"
#define LED_BLUE 23
#define CHANNEL 0
#define CHANNELNN 3
// Lower resolution for NN processing
#define NNWIDTH 576
#define NNHEIGHT 320
VideoSetting config(VIDEO_FHD, 30, VIDEO_H264, 0);
VideoSetting configNN(NNWIDTH, NNHEIGHT, 10, VIDEO_RGB, 0);
NNFaceDetection facedet;
RTSP rtsp;
StreamIO videoStreamer(1, 1);
StreamIO videoStreamerNN(1, 1);
char ssid[] = "-----"; // your network SSID (name)
char pass[] = "-----"; // your network password
int status = WL_IDLE_STATUS;
IPAddress ip;
int rtsp_portnum;
void setup()
{
Serial.begin(115200);
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
// wait 2 seconds for connection:
delay(2000);
}
ip = WiFi.localIP();
// Configure camera video channels with video format information
// Adjust the bitrate based on your WiFi network quality
config.setBitrate(2 * 1024 * 1024); // Recommend to use 2Mbps for RTSP streaming to prevent network congestion
Camera.configVideoChannel(CHANNEL, config);
Camera.configVideoChannel(CHANNELNN, configNN);
Camera.videoInit();
// Configure RTSP with corresponding video format information
rtsp.configVideo(config);
rtsp.begin();
rtsp_portnum = rtsp.getPort();
// Configure face detection with corresponding video format information
// Select Neural Network(NN) task and models
facedet.configVideo(configNN);
facedet.setResultCallback(FDPostProcess);
facedet.modelSelect(FACE_DETECTION, NA_MODEL, DEFAULT_SCRFD, NA_MODEL);
facedet.begin();
// Configure StreamIO object to stream data from video channel to RTSP
videoStreamer.registerInput(Camera.getStream(CHANNEL));
videoStreamer.registerOutput(rtsp);
if (videoStreamer.begin() != 0) {
Serial.println("StreamIO link start failed");
}
// Start data stream from video channel
Camera.channelBegin(CHANNEL);
// Configure StreamIO object to stream data from RGB video channel to face detection
videoStreamerNN.registerInput(Camera.getStream(CHANNELNN));
videoStreamerNN.setStackSize();
videoStreamerNN.setTaskPriority();
videoStreamerNN.registerOutput(facedet);
if (videoStreamerNN.begin() != 0) {
Serial.println("StreamIO link start failed");
}
// Start video channel for NN
Camera.channelBegin(CHANNELNN);
// Start OSD drawing on RTSP video channel
OSD.configVideo(CHANNEL, config);
OSD.begin();
pinMode(LED_BLUE , OUTPUT);
}
void loop()
{
// Do nothing
}
// User callback function for post processing of face detection results
void FDPostProcess(std::vector<FaceDetectionResult> results)
{
int count = 0;
uint16_t im_h = config.height();
uint16_t im_w = config.width();
Serial.print("Network URL for RTSP Streaming: ");
Serial.print("rtsp://");
Serial.print(ip);
Serial.print(":");
Serial.println(rtsp_portnum);
Serial.println(" ");
printf("Total number of faces detected = %d\r\n", facedet.getResultCount());
OSD.createBitmap(CHANNEL);
if (facedet.getResultCount() > 0) {
for (int i = 0; i < facedet.getResultCount(); i++) {
FaceDetectionResult item = results[i];
// Result coordinates are floats ranging from 0.00 to 1.00
// Multiply with RTSP resolution to get coordinates in pixels
int xmin = (int)(item.xMin() * im_w);
int xmax = (int)(item.xMax() * im_w);
int ymin = (int)(item.yMin() * im_h);
int ymax = (int)(item.yMax() * im_h);
// Draw boundary box
printf("Face %ld confidence %d:\t%d %d %d %d\n\r", i, item.score(), xmin, xmax, ymin, ymax);
OSD.drawRect(CHANNEL, xmin, ymin, xmax, ymax, 3, OSD_COLOR_WHITE);
if(xmax - xmin > 350)
{
printf("too near!!!!!!");
digitalWrite(LED_BLUE, HIGH);
}
else
{
digitalWrite(LED_BLUE, LOW);
}
// Print identification text above boundary box
char text_str[40];
snprintf(text_str, sizeof(text_str), "%s %d", item.name(), item.score());
OSD.drawText(CHANNEL, xmin, ymin - OSD.getTextHeight(CHANNEL), text_str, OSD_COLOR_CYAN);
// Draw facial feature points
for (int j = 0; j < 5; j++) {
int x = (int)(item.xFeature(j) * im_w);
int y = (int)(item.yFeature(j) * im_h);
OSD.drawPoint(CHANNEL, x, y, 8, OSD_COLOR_RED);
count++;
if (count == MAX_FACE_DET) {
goto OSDUpdate;
}
}
}
}
OSDUpdate:
OSD.update(CHANNEL);
}
编译烧录后:

可以看到已经成功识别到了人脸。
最后来看看效果: