在资源受限的嵌入式系统中实现实时图像处理,就像在火柴盒里搭建一座微缩城市——每个字节都弥足珍贵。当STM32F103这类仅有20KB RAM的MCU遇上320x240分辨率的OV7725摄像头时,传统图像处理方案往往束手无策。本文将揭示如何通过HSL颜色空间转换、位级压缩存储和流式处理三大核心技术,在串口带宽限制下实现每秒5帧的颜色追踪系统。
OV7725输出的RGB565格式图像看似节省空间,但对STM32F1来说仍是难以承受之重。320x240的RGB565图像需要150KB存储空间,远超芯片RAM容量。我们的解决方案采用三级压缩策略:
c复制// RGB565转HSL的轻量级实现
typedef struct {
uint8_t H; // 0-240
uint8_t S; // 0-240
uint8_t L; // 0-240
} HSLColor;
void RGB565_to_HSL(uint16_t rgb, HSLColor *hsl) {
uint8_t r = (rgb >> 11) & 0x1F;
uint8_t g = (rgb >> 5) & 0x3F;
uint8_t b = rgb & 0x1F;
// 归一化到0-255范围
r = (r << 3) | (r >> 2);
g = (g << 2) | (g >> 4);
b = (b << 3) | (b >> 2);
// HSL转换核心算法
uint8_t max = MAX3(r, g, b);
uint8_t min = MIN3(r, g, b);
hsl->L = (max + min) / 2;
if(max == min) {
hsl->H = hsl->S = 0;
} else {
uint16_t delta = max - min;
hsl->S = delta * 240 / (hsl->L > 120 ? (510 - max - min) : (max + min));
if(max == r) hsl->H = 40 * (g - b) / delta + (g < b ? 240 : 0);
else if(max == g) hsl->H = 40 * (b - r) / delta + 80;
else hsl->H = 40 * (r - g) / delta + 160;
}
}
关键优化点:
传统二值化存储需要320x240=76,800bit(9.6KB),我们通过位操作压缩到原来的1/8:
| 存储方式 | 内存占用 | 存取复杂度 | 适合场景 |
|---|---|---|---|
| 全帧RGB565 | 150KB | O(1) | 彩显系统 |
| 全帧二值化 | 9.6KB | O(1) | 黑白处理 |
| 位压缩 | 1.2KB | O(log n) | 超低内存 |
实现方案采用二维数组+位掩码技术:
c复制#define WIDTH 320
#define HEIGHT 240
uint8_t bitmap[HEIGHT][WIDTH/8]; // 仅占用1.2KB
void set_pixel(uint16_t x, uint16_t y, uint8_t val) {
if(val) bitmap[y][x/8] |= (0x80 >> (x%8));
else bitmap[y][x/8] &= ~(0x80 >> (x%8));
}
uint8_t get_pixel(uint16_t x, uint16_t y) {
return (bitmap[y][x/8] >> (7 - x%8)) & 0x01;
}
性能对比测试(基于STM32F103C8T6 @72MHz):
| 操作类型 | 传统数组耗时 | 位压缩耗时 | 节省内存 |
|---|---|---|---|
| 单像素写入 | 0.2μs | 0.8μs | 87.5% |
| 全帧读取 | 5ms | 12ms | 87.5% |
| 区域检测 | 15ms | 22ms | 87.5% |
在115200bps波特率下,传输一帧320x240的二值图像需要约6.7秒,完全无法满足实时需求。我们采用动态区域编码技术:
c复制#pragma pack(push, 1)
typedef struct {
uint8_t header; // 0xAA
uint16_t center_x;
uint16_t center_y;
uint16_t width;
uint16_t height;
uint8_t checksum;
} ObjectFrame;
#pragma pack(pop)
void send_object_frame(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
ObjectFrame frame = {
.header = 0xAA,
.center_x = htons(x),
.center_y = htons(y),
.width = htons(w),
.height = htons(h),
.checksum = 0
};
uint8_t *p = (uint8_t*)&frame;
for(int i=0; i<sizeof(frame)-1; i++)
frame.checksum ^= p[i];
HAL_UART_Transmit(&huart1, (uint8_t*)&frame, sizeof(frame), 100);
}
传输效率对比:
| 传输方式 | 数据量 | 传输时间 | 帧率 |
|---|---|---|---|
| 原始图像 | 76,800bit | 667ms | 1.5fps |
| 全帧压缩 | 9,600bit | 83ms | 12fps |
| 动态区域 | 80bit | 0.7ms | 1428fps |
code复制OV7725摄像头硬件接口:
VSYNC -- PA8 (定时器输入捕获)
HREF -- PA9
PCLK -- PA10
D[0:7] -- PE0-PE7
SCCB -- PB6(SCL), PB7(SDA)
串口调试接口:
USART1_TX -- PA9
USART1_RX -- PA10
图像采集阶段:
实时处理阶段:
mermaid复制graph TD
A[原始图像] --> B[RGB565转HSL]
B --> C{颜色匹配?}
C -->|是| D[设置位图]
C -->|否| E[清除位图]
D --> F[腐蚀算法]
E --> F
F --> G[计算质心]
输出优化阶段:
中断服务例程优化:
c复制void TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
static uint32_t last_cnt;
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint32_t cnt = __HAL_TIM_GET_COMPARE(htim, TIM_CHANNEL_1);
line_interval = cnt - last_cnt;
last_cnt = cnt;
// 提前处理上一行数据
if(current_line > 0) {
process_line(current_line - 1);
}
current_line++;
}
}
关键参数配置:
| 参数项 | 推荐值 | 调整影响 |
|---|---|---|
| HSL阈值范围 | H±15, S>100, L>50 | 范围越大误判越多 |
| 腐蚀迭代次数 | 3-5次 | 次数越多延迟越大 |
| 串口发送间隔 | ≥50ms | 间隔越小帧率越高 |
在STM32F103C8T6上的实测表现: