发帖
3 0 0

安信可小安派BW21-CBV-Kit + 检测人脸并将其像素化显示在OLED屏上

huatenma
注册会员

2

主题

2

回帖

151

积分

注册会员

积分
151
小安派·BW21-CBV-KIt 52 3 3 天前
[i=s] 本帖最后由 huatenma 于 2025-3-29 13:06 编辑 [/i]

实现逻辑思路

  1. 硬件初始化流程

复制

[OLED初始化] -> [WiFi连接] -> [摄像头初始化] -> [RTSP配置] -> [神经网络管道搭建]
  1. 数据流逻辑

复制

摄像头视频流
├─> 主通道(CAM_CHANNEL) ─> RTSP流媒体输出
└─> NN处理通道(NN_CHANNEL) ─> 人脸检测模型 ─> 检测结果回调
  1. 显示逻辑

复制

人脸检测结果 -> 坐标转换 -> OLED显示引擎 -> 屏幕刷新
  1. 关键功能模块
模块 功能说明 关键技术点
OLED驱动模块 管理屏幕初始化、图形绘制和刷新 Adafruit GFX库、双缓冲机制
摄像头管理模块 配置多通道视频流、编码参数设置 Ambarella SDK视频管道管理
神经网络模块 人脸检测模型加载、推理结果处理 SCRFD轻量级人脸检测模型
坐标转换系统 将1920x1080坐标映射到128x64屏幕 线性映射算法
异步回调机制 人脸检测结果与主循环的非阻塞通信 volatile变量保证数据一致性
  1. 性能优化措施
  • 双通道视频处理:主通道用于高清视频流传输,独立通道用于低分辨率神经网络处理
  • 坐标约束机制:使用constrain()函数确保绘制坐标在屏幕范围内
  • 节流刷新机制:80ms刷新间隔平衡流畅度和资源消耗
  • 轻量级绘图:使用基本几何图形降低渲染负载
  1. 异常处理机制
  • 硬件初始化失败检测(摄像头、OLED)
  • WiFi连接重试机制(最多5次重试)
  • 人脸坐标约束处理(防止绘制越界)
  • 网络中断自动恢复(依赖Ambarella SDK底层自动重连)

系统架构示意图

复制

+---------------------+
|    摄像头输入        |
| (1920x1080@30fps)  |
+----------+----------+
           |
           v
+----------+----------+
| 视频分配器          |
| [通道0]  [通道3]   |
+----------+----------+
           |            |
           v            v
+----------+----------+ +-------------------+
| RTSP编码器          | | 神经网络处理器     |
| (H.264视频流)       | | (人脸检测模型)     |
+----------+----------+ +---------+---------+
           |                      |
           v                      v
+----------+----------+   +---------+---------+
| 网络传输模块        |   | 人脸数据回调      |
| (RTSP协议传输)      |   | (坐标转换处理)    |
+---------------------+   +---------+---------+
                                    |
                                    v
                            +-------+-------+
                            | OLED显示引擎  |
                            | (128x64 OLED)|
                            +--------------+
/* AMB82-MINI人脸识别OLED显示完整代码 */
#include "WiFi.h"
#include "StreamIO.h"
#include "VideoStream.h"
#include "RTSP.h"
#include "NNFaceDetection.h"
#include "VideoStreamOverlay.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//---------------------------
// 硬件配置
//---------------------------
// OLED配置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// 摄像头通道配置
#define CAM_CHANNEL    0  // 主视频通道
#define NN_CHANNEL     3  // 神经网络处理通道
#define NN_WIDTH      576 // NN处理宽度
#define NN_HEIGHT     320 // NN处理高度

//---------------------------
// 全局对象
//---------------------------
VideoSetting camConfig(VIDEO_FHD, 30, VIDEO_H264, 0);      // 主摄像头配置(1920x1080)
VideoSetting nnConfig(NN_WIDTH, NN_HEIGHT, 10, VIDEO_RGB, 0); // NN输入配置
NNFaceDetection faceDetector;
RTSP rtspStream;
StreamIO videoPipeline(1, 1);    // 视频流管道
StreamIO nnPipeline(1, 1);       // NN处理管道

//---------------------------
// 网络配置
//---------------------------
char ssid[] = "Maga";
char pass[] = "134567890";
volatile bool wifiConnected = false;

//---------------------------
// 人脸数据共享变量
//---------------------------
volatile struct {
  int x;
  int y;
  int width;
  int height;
  bool detected;
} faceData = {0, 0, 0, 0, false};

//---------------------------
// 系统初始化
//---------------------------
void setup() {
  // 初始化串口
  Serial.begin(115200);
  while (!Serial);

  // 阶段1:OLED初始化
  initOLED();

  // 阶段2:WiFi连接
  connectWiFi();

  // 阶段3:摄像头系统初始化
  initCameraSystem();

  // 阶段4:神经网络管道搭建
  setupNNPipeline();

  // 阶段5:OSD叠加层初始化
  OSD.configVideo(CAM_CHANNEL, camConfig);
  OSD.begin();
}

//---------------------------
// 主循环
//---------------------------
void loop() {
  updateOLEDDisplay();
  delay(80); // 控制刷新率约12.5FPS
}

//---------------------------
// 初始化OLED
//---------------------------
void initOLED() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println("OLED初始化失败!");
    while(1);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.println("System Init...");
  display.display();
}

//---------------------------
// 连接WiFi
//---------------------------
void connectWiFi() {
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("Connecting to ");
  display.println(ssid);
  display.display();

  int retryCount = 0;
  while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
    display.print(".");
    display.display();
    delay(2000);
    if(retryCount++ > 5) {
      display.println("\nConnection Failed!");
      display.display();
      while(1);
    }
  }
  
  display.println("\nConnected!");
  display.print("IP:");
  display.println(WiFi.localIP());
  display.display();
  delay(1000);
}

//---------------------------
// 初始化摄像头系统
//---------------------------
void initCameraSystem() {
  // 配置摄像头通道
  camConfig.setBitrate(2 * 1024 * 1024); // 2Mbps比特率
  Camera.configVideoChannel(CAM_CHANNEL, camConfig);
  Camera.configVideoChannel(NN_CHANNEL, nnConfig);
  
  // 初始化视频系统
  if(Camera.videoInit() != 0) {
    display.println("Camera Init Fail!");
    display.display();
    while(1);
  }

  // 配置RTSP流
  rtspStream.configVideo(camConfig);
  rtspStream.begin();
  
  // 构建视频流管道
  videoPipeline.registerInput(Camera.getStream(CAM_CHANNEL));
  videoPipeline.registerOutput(rtspStream);
  videoPipeline.begin();
  
  Camera.channelBegin(CAM_CHANNEL);
}

//---------------------------
// 配置神经网络管道
//---------------------------
void setupNNPipeline() {
  // 初始化人脸检测器
  faceDetector.configVideo(nnConfig);
  faceDetector.setResultCallback(faceDetectionCallback);
  faceDetector.modelSelect(FACE_DETECTION, NA_MODEL, DEFAULT_SCRFD, NA_MODEL);
  faceDetector.begin();

  // 构建NN处理管道
  nnPipeline.registerInput(Camera.getStream(NN_CHANNEL));
  nnPipeline.registerOutput(faceDetector);
  nnPipeline.begin();
  
  Camera.channelBegin(NN_CHANNEL);
}

//---------------------------
// 人脸检测回调函数
//---------------------------
void faceDetectionCallback(std::vector<FaceDetectionResult> results) {
  if(!results.empty()) {
    FaceDetectionResult firstFace = results[0];
  
    // 原始坐标转换(基于1920x1080)
    int origX = firstFace.xMin() * 1920;
    int origY = firstFace.yMin() * 1080;
    int origW = (firstFace.xMax() - firstFace.xMin()) * 1920;
    int origH = (firstFace.yMax() - firstFace.yMin()) * 1080;

    // 映射到OLED分辨率
    faceData.x = map(origX, 0, 1920, 0, SCREEN_WIDTH);
    faceData.y = map(origY, 0, 1080, 0, SCREEN_HEIGHT);
    faceData.width = map(origW, 0, 1920, 0, SCREEN_WIDTH);
    faceData.height = map(origH, 0, 1080, 0, SCREEN_HEIGHT);
    faceData.detected = true;
  } else {
    faceData.detected = false;
  }
}

//---------------------------
// OLED显示更新
//---------------------------
void updateOLEDDisplay() {
  display.clearDisplay();
  
  if(faceData.detected) {
    // 绘制人脸框
    display.drawRect(
      constrain(faceData.x, 0, SCREEN_WIDTH),
      constrain(faceData.y, 0, SCREEN_HEIGHT),
      constrain(faceData.width, 1, SCREEN_WIDTH-faceData.x),
      constrain(faceData.height, 1, SCREEN_HEIGHT-faceData.y),
      SSD1306_WHITE
    );

    // 绘制简化面部特征
    int eyeY = faceData.y + faceData.height/4;
    int mouthY = faceData.y + faceData.height*3/4;
  
    // 左眼
    display.fillCircle(
      faceData.x + faceData.width/4,
      eyeY,
      2, SSD1306_WHITE
    );
  
    // 右眼
    display.fillCircle(
      faceData.x + faceData.width*3/4,
      eyeY,
      2, SSD1306_WHITE
    );
  
    // 嘴巴
    display.drawLine(
      faceData.x + faceData.width/4,
      mouthY,
      faceData.x + faceData.width*3/4,
      mouthY,
      SSD1306_WHITE
    );
  } else {
    display.setCursor(0,0);
    display.println("No Face Detected");
  }
  
  display.display();
}
──── 0人觉得很赞 ────

使用道具 举报

前天 13:01
[i=s] 本帖最后由 huatenma 于 2025-3-29 13:07 编辑 [/i]

视频上传不了


上传到bilibili,链接贴在这里就行了

视频上传至B站,链接附在帖子里。
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 28214 个
  • 话题数: 40138 篇