STM32 单片机驱动 OV7725/OV2640 摄像头颜色识别检测
介绍基于 STM32 单片机配合 OV7725/OV2640 摄像头模块实现颜色识别的技术方案。内容涵盖图像采集基本概念、传感器特性、GPIO 与 DCMI 驱动方式对比,以及 RGB 阈值法和 HSV 空间转换两种核心识别算法原理与代码实现。通过定义颜色范围并进行像素遍历判断,最终在 LCD 上标记目标颜色区域。

介绍基于 STM32 单片机配合 OV7725/OV2640 摄像头模块实现颜色识别的技术方案。内容涵盖图像采集基本概念、传感器特性、GPIO 与 DCMI 驱动方式对比,以及 RGB 阈值法和 HSV 空间转换两种核心识别算法原理与代码实现。通过定义颜色范围并进行像素遍历判断,最终在 LCD 上标记目标颜色区域。

本文介绍如何使用 STM32 单片机对彩色摄像头(OV7725/OV2640)采集的图像数据进行分析处理,实现颜色的识别和检测。

颜色识别(Color Recognition)是一种通过分析图像中像素的颜色信息来识别或区分特定颜色的技术。它通常用于图像处理和计算机视觉领域,通过将图像的每个像素或区域的颜色信息与预设的颜色标准进行比较,从而判断该区域属于何种颜色。


下面是显示屏屏幕的分辨率:










OV7725 是一款 VGA(640x480)分辨率的 CMOS 图像传感器,支持彩色图像采集。
OV7725 支持两种常见的接口标准,便于与不同平台或设备集成:


关于单片机对摄像头的代码驱动,这里不做详细讲解。完整的工程可以到文章末尾的资料连接进行下载。
这里需要注意一些关键的地方:
在 OV7725 摄像头模块的驱动中,GPIO 驱动和 DCMI 驱动分别代表了两种不同的硬件接口方式,它们在摄像头与微控制器(MCU)之间的数据传输和信号控制中扮演不同的角色。
GPIO(General Purpose Input/Output)是一种通用的输入输出接口,通常用于处理信号的传输、控制和接收。对于 OV7725 摄像头来说,GPIO 引脚可以用于与摄像头进行基本的控制和数据交互。
在 OV7725 驱动中,GPIO 驱动主要涉及以下几个方面:控制信号的传输:复位信号(RESET):摄像头复位信号通常通过一个 GPIO 引脚来控制。通过将 GPIO 设置为低电平,可以复位摄像头。电源使能(POWER):某些 MCU 允许通过 GPIO 引脚控制摄像头模块的电源开关。同步信号(VSYNC、HSYNC):用于标记每一帧图像的开始(VSYNC)和每一行像素的开始(HSYNC)。这些信号通常也通过 GPIO 引脚传输。像素数据传输:对于一些简单的配置,OV7725 可以通过并行接口以 8 位数据宽度输出像素数据。这些数据通过 GPIO 引脚进行传输。时钟信号:像素时钟(PCLK):控制像素数据的时序,这个时钟信号也是通过 GPIO 引脚生成并传输的。
**性能限制:**使用 GPIO 接口进行像素数据的传输和控制信号的处理,由于 GPIO 的工作频率相对较低,数据传输速率受限,因此对于高分辨率或高帧率的摄像头应用,GPIO 驱动可能不够高效。**硬件资源消耗:**每个 GPIO 引脚需要消耗 MCU 的资源,因此如果需要大量的 GPIO 引脚(例如 8 位并行数据线、同步信号等),可能会影响到 MCU 的其他功能和资源。
DCMI 是指 Digital Camera Memory Interface,即数字摄像头内存接口。它是 STM32 等微控制器中常见的一种硬件接口,专门用于连接图像传感器(如 OV7725)和处理器(或其他图像处理单元),用于处理图像数据的传输。
DCMI 驱动在摄像头驱动中的作用包括:
| 特性 | GPIO 驱动 | DCMI 驱动 |
|---|---|---|
| 功能 | 主要用于简单的信号控制和低速数据传输 | 高效的图像数据传输接口,支持 DMA |
| 接口方式 | 通过 GPIO 引脚传输数据和信号 | 专用的数字摄像头接口,支持并行和串行数据 |
| 数据传输速率 | 受限,适用于低分辨率和低帧率的图像 | 高速,适用于高分辨率、高帧率图像 |
| CPU 占用 | 较高,因需要通过软件控制信号和数据流 | 较低,通过 DMA 自动传输数据 |
| 应用场景 | 简单摄像头模块,低速图像采集 | 高分辨率、高帧率的视频采集 |
GPIO 驱动通常用于简单的摄像头控制,适合低分辨率、低帧率的图像采集场景,可以通过基本的控制信号(复位、同步信号)和数据传输来与摄像头进行交互。DCMI 驱动则是一种高效、专业的接口,适用于需要高带宽、高速度数据传输的应用,能自动处理复杂的同步信号和数据流,并结合 DMA 提供高效的图像数据采集。
接下来将详细讲解颜色识别的算法和代码工程。下面就是非常重要的一些概念和原理了!

RGB 阈值法是颜色识别中常见的一种方法。其基本原理是通过设定每个颜色通道(红色 R、绿色 G、蓝色 B)的数值范围来识别图像中的颜色。每个像素的颜色信息可以通过三个通道的数值(通常是 0 到 255 之间的整数)表示。在 RGB 模型中,颜色的表达方式为 (R, G, B),其中每个分量的值控制着对应的颜色成分的强度。
对于颜色识别,我们通过预设的阈值范围来判断图像中的颜色。例如,我们可能会设定如下的阈值来识别红色:
基于这个逻辑,RGB 阈值法能有效地检测图像中符合条件的像素,进而实现颜色的识别。
首先,你需要确保 STM32 和 OV7725 摄像头正确连接,并配置好摄像头接口(通常是通过 DCMI 接口或 GPIO):
使用 STM32 配置 DCMI 驱动程序来捕获图像数据。通过 DCMI 接口,图像数据会通过 DMA 传输到内存。
// 配置 DCMI 和 DMA
void DCMI_DMA_Init(void) {
// 启动 DCMI 和 DMA 配置,配置 DCMI 时钟和像素格式
// 初始化 DMA(例如:使用 DMA2)
// 配置 DMA 传输到内存
// 启动 DCMI 捕获图像
}
例如,如果你想识别红色,可以设置:
通过比较图像中每个像素的 RGB 值与这些阈值进行匹配,就能判断该像素是否属于目标颜色。
// 假设已捕获并存储了图像数据到内存(例如:frame_buffer)
void process_image(uint16_t *frame_buffer, uint32_t width, uint32_t height) {
for (uint32_t y = 0; y < height; y++) {
for (uint32_t x = 0; x < width; x++) {
uint16_t pixel = frame_buffer[y * width + x];
// 提取 RGB 分量(假设图像格式为 RGB565)
uint8_t R = (pixel >> 11) & 0x1F; // 获取红色分量
uint8_t G = (pixel >> 5) & 0x3F; // 获取绿色分量
uint8_t B = pixel & 0x1F; // 获取蓝色分量
// 将 RGB 分量转换为 8 位(如果需要)
R = (R * 255) / 31; // RGB565 转 8-bit
G = (G * 255) / 63;
B = (B * 255) / 31;
// 判断是否属于某个颜色范围(例如识别红色)
if (R > 200 && G < 100 && B < 100) {
// 如果符合红色范围,执行某些操作
// 例如,在此位置标记或执行动作
}
}
}
}
在实际应用中,你可以根据需要调整 RGB 阈值范围。不同的颜色可以使用不同的阈值。以下是识别 红色、绿色 和 蓝色 的示例: **红色:**R: [200, 255] G: [0, 100] B: [0, 100] **绿色:**R: [0, 100] G: [200, 255] B: [0, 100] **蓝色:**R: [0, 100] G: [0, 100] B: [200, 255]
在对每个像素进行 RGB 处理后,检查该像素是否符合某一颜色的阈值。如果符合,可以标记该像素为目标颜色,也可以根据需要进行其他处理,例如计数、绘制框、发送信号等。
void identify_color(uint16_t *frame_buffer, uint32_t width, uint32_t height) {
// 遍历每一行
for (uint32_t y = 0; y < height; y++) {
// 遍历每一列(每个像素)
for (uint32_t x = 0; x < width; x++) {
// 获取当前像素的 RGB565 格式的颜色值
uint16_t pixel = frame_buffer[y * width + x];
// 提取红色分量,右移 11 位并与 0x1F(11111)进行按位与操作,保留最低 5 位
uint8_t R = (pixel >> 11) & 0x1F;
// 提取绿色分量,右移 5 位并与 0x3F(111111)进行按位与操作,保留最低 6 位
uint8_t G = (pixel >> 5) & 0x3F;
// 提取蓝色分量,直接与 0x1F(11111)进行按位与操作,保留最低 5 位
uint8_t B = pixel & 0x1F;
// 将红色、绿色、蓝色分量的值从 5 位或 6 位范围映射到 8 位(0 到 255)
R = (R * 255) / 31;
G = (G * 255) / 63;
B = (B * 255) / 31;
// 判断是否为红色:红色分量大于 200,绿色和蓝色分量小于 100
if (R > 200 && G < 100 && B < 100) {
// 如果是红色,标记为红色(RGB565 格式中的红色:0xF800)
frame_buffer[y * width + x] = 0xF800; // RGB565 红色
}
// 判断是否为绿色:绿色分量大于 200,红色和蓝色分量小于 100
else if (R < && G > && B < ) {
frame_buffer[y * width + x] = ;
}
(R < && G < && B > ) {
frame_buffer[y * width + x] = ;
}
}
}
}
使用 HSV(色相、饱和度、明度)空间来进行颜色识别是图像处理中一种常见且有效的方法。与 RGB(红绿蓝)空间不同,HSV 将颜色分解为三部分:色相(H)、饱和度(S)和明度(V),这使得它更接近于人类的感知方式,也更便于从图像中提取特定颜色。尤其是对于一些光照变化较大的情况,HSV 空间的颜色分离表现更为优秀。
RGB 和 HSV 之间的关系及转换

色相(H):代表颜色的种类,取值范围通常为 0 到 360°(即颜色的角度),每个角度对应一种颜色。0° 通常为红色,120° 为绿色,240° 为蓝色,其他颜色则在这三个之间变化。饱和度(S):表示颜色的纯度或强度,取值范围为 0 到 100%。饱和度越高,颜色越纯,越接近原始颜色;饱和度为 0 时,颜色呈灰白色。明度(V):表示颜色的亮度,取值范围为 0 到 100%。明度越高,颜色越明亮;明度为 0 时颜色为黑色。
要在 HSV 空间中进行颜色识别,通常需要将 RGB 颜色空间的像素值转换为 HSV 值。RGB 值的范围通常是 0 到 255,而 HSV 的范围是:**色相 H:0 到 360°**饱和度 S 和明度 V:0 到 100%
下面是 RGB 到 HSV 的转换公式:
1、标准化 RGB 值:将 RGB 的每个分量从 0
255 的范围映射到 01:2、计算最大值和最小值:
计算色差:
3、计算色相 H:
4、计算饱和度 S
5、计算明度 V
基于 HSV 空间,我们可以通过判断色相、饱和度和明度的范围来识别特定的颜色。以下是基于 HSV 空间进行颜色识别的具体步骤:
步骤 1:图像遍历
遍历图像中的每个像素,获取其 RGB 值。步骤 2:RGB 到 HSV 的转换
将每个像素的 RGB 值转换为 HSV 值。使用上述的 RGB 到 HSV 转换公式来实现。步骤 3:定义颜色范围
对于每种你想要识别的颜色(如红色、绿色、蓝色等),定义一个 HSV 范围。例如:红色:H 范围可以是 [0, 10] 或 [350, 360],S 范围可以是 [50, 100],V 范围可以是 [50, 100]。绿色:H 范围可以是 [60, 180],S 范围可以是 [50, 100],V 范围可以是 [50, 100]。蓝色:H 范围可以是 [180, 250],S 范围可以是 [50, 100],V 范围可以是 [50, 100]。步骤 4:进行颜色判断
根据转换得到的 HSV 值,判断是否在目标颜色的范围内。例如,对于红色,如果色相 ( H ) 在 [0, 10] 或 [350, 360] 范围内,饱和度 ( S ) 在 [50, 100] 范围内,明度 ( V ) 在 [50, 100] 范围内,则认为该像素为红色。步骤 5:标记颜色
如果一个像素属于目标颜色(例如红色),可以将其标记或替换为一个特定颜色,或者在后续处理步骤中执行某些操作(如高亮、滤镜等)。
以下是一个简化的基于 HSV 空间进行颜色识别的示例代码。该代码检查图像中的每个像素,识别是否为红色,并将其标记为纯红色(RGB565 格式)。
void RGB_to_HSV(uint8_t R, uint8_t G, uint8_t B, float *H, float *S, float *V) {
float r = R / 255.0f;
float g = G / 255.0f;
float b = B / 255.0f;
float Cmax = fmaxf(fmaxf(r, g), b);
float Cmin = fminf(fminf(r, g), b);
float delta = Cmax - Cmin;
// 计算色相
if (delta == 0) {
*H = 0;
} else if (Cmax == r) {
*H = 60 * fmodf(((g - b) / delta), 6);
} else if (Cmax == g) {
*H = 60 * (((b - r) / delta) + 2);
} else {
*H = 60 * (((r - g) / delta) + 4);
}
if (*H < 0) {
*H += 360;
}
// 计算饱和度
if (Cmax == 0) {
*S = 0;
} else {
*S = (delta / Cmax) * 100;
}
// 计算明度
*V = Cmax * 100;
}
void identify_color_with_HSV(uint16_t *frame_buffer, uint32_t width, uint32_t height) {
for (uint32_t y = ; y < height; y++) {
( x = ; x < width; x++) {
pixel = frame_buffer[y * width + x];
R = (pixel >> ) & ;
G = (pixel >> ) & ;
B = pixel & ;
H, S, V;
RGB_to_HSV(R * / , G * / , B * / , &H, &S, &V);
((H >= && H <= || H >= && H <= ) && S >= && V >= ) {
frame_buffer[y * width + x] = ;
}
}
}
}
颜色滤波器的实现原理基于图像处理技术,通过对图像中的像素进行颜色空间的转换与筛选,提取特定颜色的区域或物体。简单来说,颜色滤波器就是通过设定一个颜色范围,过滤掉其他不需要的颜色,保留感兴趣的颜色区域。常用的颜色空间有 RGB 和 HSV,其中 HSV 空间更适合用于颜色滤波,因为它与人眼的颜色感知更接近。
颜色滤波器通常有以下几个步骤:
在图像处理中,最常见的颜色空间是 RGB(红、绿、蓝)和 HSV(色相、饱和度、明度)。在 RGB 空间中,颜色是由三个通道的值(红、绿、蓝)组成的。HSV 空间则通过 色相 (H)、饱和度 (S) 和 明度 (V) 来描述颜色,更符合人类视觉感知。因此,颜色滤波一般使用 HSV 空间,因为它能够更准确地分离色彩(色相)和亮度(明度、饱和度)。
颜色滤波器的关键是设定一个特定的颜色范围,该范围包含了你要识别的颜色。例如,如果你想检测图像中的 红色,你需要定义红色的 色相 范围(例如 H: 0° 到 10°,或 H: 350° 到 360°),并根据 饱和度 (S) 和 明度 (V) 定义允许的阈值。
对于图像中的每个像素,根据其 HSV 值(或者 RGB 值)来判断它是否在定义的颜色范围内。如果在范围内,则保留该像素;如果不在范围内,则将其设为背景色或忽略。
经过颜色滤波后,图像的背景通常会被去除,只留下符合条件的颜色区域。这通常用于图像分割、目标检测、物体跟踪等应用。
一般来说,颜色滤波器的工作流程可以分为以下几个步骤:
HSV 颜色空间中的颜色滤波器通常只需要定义 色相 (H) 范围,结合 饱和度 (S) 和 明度 (V) 范围来实现更加精确的颜色提取。以下是一个基于 HSV 颜色空间的颜色滤波器实现过程:
首先,根据应用需求设定感兴趣的颜色范围。假设我们要提取 红色,可以设置如下的范围:
对于每个像素,通过计算其 HSV 值(如果图像在 RGB 空间中,则先转换到 HSV 空间):
如果一个像素的 HSV 值在指定的范围内,那么它就是我们需要保留的颜色;否则,可以将其设为背景色或其他颜色。

这里介绍 STM32 单片机驱动摄像头对颜色识别的关键代码,整体工程看文章末尾资料连接。注意:这里使用 STM32+OV2640
颜色处理的关键代码:
EasyTracer_color.h
#ifndef EASY_TRACERED_H
#define EASY_TRACERED_H
#define IMG_X 0 //图片 x 坐标
#define IMG_Y 0 //图片 y 坐标
#define IMG_W 240 //图片宽度
#define IMG_H 300 //图片高度
#define ALLOW_FAIL_PER 3 //容错率,每 1<<ALLOW_FAIL_PER 个点允许出现一个错误点,容错率越大越容易识别,但错误率越大
#define ITERATE_NUM 7 //迭代次数,迭代次数越多识别越精确,但计算量越大
typedef struct{
unsigned char H_MIN;//目标最小色调
unsigned char H_MAX;//目标最大色调
unsigned char S_MIN;//目标最小饱和度
unsigned char S_MAX;//目标最大饱和度
unsigned char L_MIN;//目标最小亮度
unsigned char L_MAX;//目标最大亮度
unsigned int WIDTH_MIN;//目标最小宽度
unsigned int HIGHT_MIN;//目标最小高度
unsigned int WIDTH_MAX;//目标最大宽度
unsigned int HIGHT_MAX;//目标最大高度
}TARGET_CONDI;
x;
y;
w;
h;
}RESULT;
;
EasyTracer_color.c
#include "EasyTracer_color.h"
#include "LCD.h"
#define min3v(v1, v2, v3) ((v1)>(v2)? ((v2)>(v3)?(v3):(v2)):((v1)>(v3)?(v3):(v1)))//取最大值
#define max3v(v1, v2, v3) ((v1)<(v2)? ((v2)<(v3)?(v3):(v2)):((v1)<(v3)?(v3):(v1)))//取最小值
/* ①数据类型备注(STM32F4):
typedef unsigned char uint8_t; u8
typedef unsigned short int uint16_t; u16 (int 可省) 为 16 位数据类型,占两个字节,范围为 0~65535
typedef unsigned int uint32_t; u32
typedef unsigned __INT64 uint64_t
②static 类型函数:static 函数与普通函数的区别:
用 static 修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是 extern 的,即可以被其它代码文件调用该函数。
在函数的返回类型前加上关键字 static,函数就被定义成为静态函数。普通函数的定义和声明默认情况下是 extern 的,但静态函数只是在声明他的文件当中可见,
不能被其他文件所用。因此定义静态函数有以下优点:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突。
<2> 静态函数不能被其他文件所用。
③const 通常修饰常量类型,其变量/对象的值不能被改变。作用有以下几点:
<1> 修饰对象具有不可变性,便于进行类型检查,对象被修改时编译器可查错,增强了程序的健壮性
<2> 节省空间,避免不必要的内存分配,提高效率
④RGB 和 HSL(也叫 HSB/HSV)是两种色彩空间,即:红,绿,蓝(Red,Green,Blue)和色调,饱和度,亮度(Hue,Saturation,Lightness 或 Brightness 或 Value),
RGB 适用于机器采样,目前的显示器颜色即由这三种基色构成,而 HSL 更符合人类的直观感觉,
比如人一般表达一个颜色会这样说:有点浓的暗红色,而不会说红色占多少,绿色占多少,蓝色占多少;
<1> 亮度仅与图像的最多颜色成分和最少的颜色成分的总量有关。亮度越高,图像越趋于明亮的白色
<2> 饱和度与图像的最多颜色成分和最少的颜色成分的差量有关。饱和度越小,图像越趋于灰度图像。饱和度越大,图像越鲜艳,给人的感觉是彩色的
<3> 色调决定了人对图像的不同的颜色感受。
*/
//RGB 颜色格式结构体
typedef struct {
unsigned char red; // [0,255]
unsigned char green; // [0,255]
unsigned char blue; // [0,255]
}COLOR_RGB;//RGB 格式颜色
//色相 (H)、饱和度 (S)、明度 (L) 颜色格式结构体
typedef struct {
unsigned char hue; // [0,240] 色调
unsigned char saturation; // [0,240] 饱和度
luminance;
}COLOR_HSL;
X_Start;
X_End;
Y_Start;
Y_End;
}SEARCH_AREA;
;
{
C16;
C16 = LCD_ReadPoint(x,y);
Rgb->red = ( )((C16&)>>);
Rgb->green = ( )((C16&)>>);
Rgb->blue = ( )((C16&)<<);
}
{
h,s,l,maxVal,minVal,difVal;
r = Rgb->red;
g = Rgb->green;
b = Rgb->blue;
maxVal = max3v(r, g, b);
minVal = min3v(r, g, b);
difVal = maxVal-minVal;
l = (maxVal+minVal)*//;
(maxVal == minVal)
{
h = ;
s = ;
}
{
(maxVal==r)
{
(g>=b) h = *(g-b)/(difVal);
h = *(g-b)/(difVal) + ;
}
(maxVal==g) h = *(b-r)/(difVal) + ;
(maxVal==b) h = *(r-g)/(difVal) + ;
(l == )
s=(difVal)*/( - (maxVal+minVal));
(l<=) s = (difVal)*/(maxVal+minVal);
s = (difVal)*/( - (maxVal+minVal));
}
Hsl->hue = ( )(((h>)? : ((h<)?:h)));
Hsl->saturation = ( )(((s>)? : ((s<)?:s)));
Hsl->luminance = ( )(((l>)? : ((l<)?:l)));
}
{
( Hsl->hue > Condition->H_MIN && Hsl->hue < Condition->H_MAX &&
Hsl->saturation > Condition->S_MIN && Hsl->saturation < Condition->S_MAX &&
Hsl->luminance > Condition->L_MIN && Hsl->luminance < Condition->L_MAX )
;
;
}
{
SpaceX,SpaceY,i,j,k,FailCount=;
COLOR_RGB Rgb;
COLOR_HSL Hsl;
SpaceX = Condition->WIDTH_MIN/;
SpaceY = Condition->HIGHT_MIN/;
(i=Area->Y_Start;i<Area->Y_End;i+=SpaceY)
{
(j=Area->X_Start;j<Area->X_End;j+=SpaceX)
{
FailCount=;
(k=;k<SpaceX+SpaceY;k++)
{
(k<SpaceX)
ReadColor(j+k,i+SpaceY/,&Rgb);
ReadColor(j+SpaceX/,i+(k-SpaceX),&Rgb);
RGBtoHSL(&Rgb,&Hsl);
(!ColorMatch(&Hsl,Condition))
FailCount++;
(FailCount>((SpaceX+SpaceY)>>ALLOW_FAIL_PER))
;
}
(k==SpaceX+SpaceY)
{
*x = j+SpaceX/;
*y = i+SpaceY/;
;
}
}
}
;
}
{
Xmin,Xmax,Ymin,Ymax,i,FailCount=;
COLOR_RGB Rgb;
COLOR_HSL Hsl;
(i=oldx;i>IMG_X;i--)
{
ReadColor(i,oldy,&Rgb);
RGBtoHSL(&Rgb,&Hsl);
(!ColorMatch(&Hsl,Condition))
FailCount++;
(FailCount>(((Condition->WIDTH_MIN+Condition->WIDTH_MAX)>>)>>ALLOW_FAIL_PER))
;
}
Xmin=i;
FailCount=;
(i=oldx;i<IMG_X+IMG_W;i++)
{
ReadColor(i,oldy,&Rgb);
RGBtoHSL(&Rgb,&Hsl);
(!ColorMatch(&Hsl,Condition))
FailCount++;
(FailCount>(((Condition->WIDTH_MIN+Condition->WIDTH_MAX)>>)>>ALLOW_FAIL_PER))
;
}
Xmax=i;
FailCount=;
(i=oldy;i>IMG_Y;i--)
{
ReadColor(oldx,i,&Rgb);
RGBtoHSL(&Rgb,&Hsl);
(!ColorMatch(&Hsl,Condition))
FailCount++;
(FailCount>(((Condition->HIGHT_MIN+Condition->HIGHT_MAX)>>)>>ALLOW_FAIL_PER))
;
}
Ymin=i;
FailCount=;
(i=oldy;i<IMG_Y+IMG_H;i++)
{
ReadColor(oldx,i,&Rgb);
RGBtoHSL(&Rgb,&Hsl);
(!ColorMatch(&Hsl,Condition))
FailCount++;
(FailCount>(((Condition->HIGHT_MIN+Condition->HIGHT_MAX)>>)>>ALLOW_FAIL_PER))
;
}
Ymax=i;
FailCount=;
Resu->x = (Xmin+Xmax)/;
Resu->y = (Ymin+Ymax)/;
Resu->w = Xmax-Xmin;
Resu->h = Ymax-Ymin;
( ((Xmax-Xmin)>(Condition->WIDTH_MIN)) && ((Ymax-Ymin)>(Condition->HIGHT_MIN)) &&\
((Xmax-Xmin)<(Condition->WIDTH_MAX)) && ((Ymax-Ymin)<(Condition->HIGHT_MAX)) )
;
;
}
{
i;
x0,y0,flag=;
SEARCH_AREA Area={IMG_X,IMG_X+IMG_W,IMG_Y,IMG_Y+IMG_H};
RESULT Result;
(flag==)
{
(SearchCentre(&x0,&y0,Condition,&Area))
flag=;
{
Area.X_Start= IMG_X ; Area.X_End = IMG_X+IMG_W ;
Area.Y_Start= IMG_Y ; Area.Y_End = IMG_Y+IMG_H ;
(SearchCentre(&x0,&y0,Condition,&Area))
{
flag=;
;
}
{
flag=;
;
}
}
}
Result.x = x0;
Result.y = y0;
(i=;i<ITERATE_NUM;i++)
Corrode(Result.x,Result.y,Condition,&Result);
(Corrode(Result.x,Result.y,Condition,&Result))
{
x0=Result.x;
y0=Result.y;
Resu->x=Result.x;
Resu->y=Result.y;
Resu->w=Result.w;
Resu->h=Result.h;
flag=;
Area.X_Start= Result.x - ((Result.w)<<);
Area.X_End = Result.x + ((Result.w)<<);
Area.Y_Start= Result.y - ((Result.h)<<);
Area.Y_End = Result.y + ((Result.h)<<);
;
}
{
flag=;
;
}
}
rgb565.h
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "usmart.h"
#include "usart2.h"
#include "timer.h"
#include "ov2640.h"
#include "dcmi.h"
void rgb565_test(void);
rgb565.c
#include <rgb565.h>
#include "EasyTracer_color.h"
#include "lcd.h"
const u8*EFFECTS_TBL[7]={"Normal","Negative","B&W","Redish","Greenish","Bluish","Antique"}; //7 种特效
/* 4.3 寸,800*480
OV2640_Window_Set 设置图像输出窗口
传感器窗口设置允许用户设置整个传感器区域(1632*1220)的感兴趣部分,也就是在传感器里面开窗,开窗范围从 2*2~1632*1220 都可以设置,不过要求这个窗口必须大于等于随后设置的图像尺寸
OV2640_ImageSize_Set 设置图像尺寸大小,也就是所选格式的输出分辨率
图像尺寸设置,也就是 DSP 输出(最终输出到 LCD 的)图像的最大尺寸,该尺寸要小于等于前面我们传感器窗口设置所设定的窗口尺寸。
OV2640_ImageWin_Set 设置图像开窗大小
图像窗口设置其实和前面的传感器窗口设置类似,只是这个窗口是在我们前面设置的图像尺寸里面,再一次设置窗口大小,该窗口必须小于等于前面设置的图像尺寸。设置后的图像范围,将用于输出到外部。
OV2640_OutSize_Set 设置图像输出大小
图像输出大小设置,控制最终输出到外部的图像尺寸。该设置将图像窗口设置所决定的窗口大小,通过内部 DSP 处理,缩放成我们输出到外部的图像大小。
该设置将会对图像进行缩放处理,如果设置的图像输出大小不等于图像窗口设置图像大小,那么图像就会被缩放处理,只有这两者设置一样大的时候,输出比例才是 1:1 的
lcddev.width=800;
lcddev.height=480;
*/
int x=0,y=0;
TARGET_CONDI Conditionred={215,240,20,240,30,160,15,15,200,200}; //红色 1 API 参数 hsl 的阈值,识别时用的
RESULT Resured;//判定为的目标条件
u8 ov_frame=0;//帧率
void rgb565_test(void) {
OV2640_RGB565_Mode(); //RGB565 模式
My_DCMI_Init(); //DCMI 配置
DCMI_DMA_Init((u32)&LCD->LCD_RAM,1,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);
OV2640_OutSize_Set(,);
DCMI_Start();
() {
delay_ms();
}
}
{
(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)
{
DCMI_Stop();
(Trace(&Conditionred,&Resured)) {
LCD_Fill(Resured.x-Resured.w/,Resured.y-Resured.h/,Resured.x+Resured.w/,Resured.y-Resured.h/+,);
LCD_Fill(Resured.x-Resured.w/,Resured.y-Resured.h/,Resured.x-Resured.w/+,Resured.y+Resured.h/,);
LCD_Fill(Resured.x-Resured.w/,Resured.y+Resured.h/,Resured.x+Resured.w/,Resured.y+Resured.h/+,);
LCD_Fill(Resured.x+Resured.w/,Resured.y-Resured.h/,Resured.x+Resured.w/+,Resured.y+Resured.h/,);
LCD_Fill(Resured.x,Resured.y,Resured.x+,Resured.y+,);
x=Resured.x;
y=Resured.y;
}
ov_frame++;
DCMI_Start();
DCMI_ClearITPendingBit(DCMI_IT_FRAME);
LED1=!LED1;
}
}
{
(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
{
(,ov_frame);ov_frame=;
(,x,y);
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online