发帖
6 0 0

【安信可小安派BW21-CBV-Kit】利用人脸检测实现儿童防近视

bzhou830
论坛元老

72

主题

1439

回帖

1万

积分

论坛元老

积分
12054
小安派·BW21-CBV-KIt 102 6 15 小时前

硬件用料

  • BW21-CBV-Kit x 1

项目背景和原理

电子产品的丰富化,带来一个很大的问题就是青少年儿童的用眼健康问题。很多小朋友在看电视、平板时喜欢凑到屏幕前近距离观看。这很容易导致近视问题。虽然很多电子产品宣称的蓝光护眼,但小学,甚至幼儿园出现越来越多的视力矫正的案例发生。

本项目考虑BW21-CBV-Kit搭载了高清广角摄像头,加上其强大的硬件性能可以轻松胜任人脸检测、人脸识别等任务。利用“近大远小”的视觉特征,使用人脸检测获取人脸区域的大小来判定人脸距离摄像头的远近来进行近距离的提醒,实现预防近视的功能。

正常坐姿下,摄像头获取的图像如下图:

Snipaste_2025-03-26_06-13-43.jpg

在距离过近时:

Snipaste_2025-03-26_06-14-08.jpg

从两个图的对比上很明显可以看出,人脸区域的占比直接反映了人脸距离摄像头的远近。

软件实现

软件实现上,本项目参考案例中的人脸检测项目

Snipaste_2025-03-26_06-49-34.jpg

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

image.png

软件代码如下:

#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);
}

编译烧录后:

Snipaste_2025-03-26_06-35-01.jpg

可以看到已经成功识别到了人脸。

最后来看看效果:

──── 0人觉得很赞 ────

使用道具 举报

这个思路很好呀,可以做追踪了。算下偏移量就行了😄
13 小时前
WT_0213 发表于 2025-3-26 08:36
这个思路很好呀,可以做追踪了。算下偏移量就行了😄

以前做小车追踪就是这样干的,哈哈
我还在想你儿子怎么这么大了哈哈
赞一个
13 小时前
爱笑 发表于 2025-3-26 08:39
我还在想你儿子怎么这么大了哈哈

图中那是400个月的儿童
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 28173 个
  • 话题数: 40002 篇