🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩 基于stm32的车牌识别
🥇学长这里给一个题目综合评分(每项满分5分)
使用STM32F103RCT6作为主控,摄像头使用OV7670(带FIFO)。STM32进行了16倍频。识别过程分别为:图像采集,二值化,识别车牌区域,字符分割,字符匹配。
识别过程如下
图像采集
通过OV7670摄像头进行图像采集,采集的图像大小为320*240像素,像素格式为RGB565。每个像素由两字节组成,第一字节的高五位是Red,第一字节的低三位和第二字节的高三位组成Green,第二字节的低五位是Blue。
二值化
二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色),让整个图片呈现出只有黑色和白色的效果。二值化后的图像中灰度值范围是0或者255。这时需要设定一个阈值来对像素点进行设置。
常用二值化方法:
识别车牌区域
根据上一步的二值化,由于车牌区域跳变点多,由此可以得出车牌区域。分别记录车牌区域的上下高度。然后通过RGB-HSV颜色转换,识别出车牌区域的左右边界。
字符分割
我国常见车牌以及排列顺序大部分都是按照如下设计的:汉字、英文字母、点、英文字母、阿拉伯数字、阿拉伯数字、阿拉伯数字、阿拉伯数字。基于这个规律,以及图像采集高度一致,设计了如下的分割方法:
字符匹配
对分割出来的字符进行归一化处理,这里用到图片的扩大算法,扩大之后逐一的去进行字符匹配。字符模板事前通过字模软件转换成二进制数据保存在数组中。最后根据匹配结果相似度最大的做为输出结果。
归一化图像就是要把原来各不相同的字符统一到同一尺寸。因为扫描进来的图像中字符大小存在较大的差异,而相对来说,统一尺寸的字符识别的标准性更强,准确率自然也更高。具体算法如下:先得到原来字符的高度和宽度,与系统已存字模的数据作比较,得出要变换的系数,然后根据得到的系数按照插值的方法映射到原图像中。
摄像头部分
#include "stm32f10x.h"
#include "board.h"
#include "ov7670.h" #define OV7670_REG_NUM 184#define DELAYTIME 2//9倍频时延时“1”,16倍频时延时“2”void OV7670_GPIO_Init(void)
{GPIO_InitTypeDef gpio_init_struct;//结构体RCC_APB2PeriphClockCmd(FIFO_WR_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(FIFO_RRST_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(FIFO_OE_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(FIFO_RCLK_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(FIFO_WRST_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(SCCB_SIC_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(SCCB_SID_RCC, ENABLE);//初始化时钟RCC_APB2PeriphClockCmd(OV7670_RRST_RCC, ENABLE);//初始化时钟gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;gpio_init_struct.GPIO_Pin = FIFO_WR_PIN;GPIO_Init(FIFO_WR_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = FIFO_RRST_PIN;GPIO_Init(FIFO_RRST_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = FIFO_OE_PIN;GPIO_Init(FIFO_OE_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = FIFO_RCLK_PIN;GPIO_Init(FIFO_RCLK_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = FIFO_WRST_PIN;GPIO_Init(FIFO_WRST_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = SCCB_SIC_PIN;GPIO_Init(SCCB_SIC_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);gpio_init_struct.GPIO_Pin = OV7670_RRST_PIN;GPIO_Init(OV7670_RRST_PORT, &gpio_init_struct);RCC_APB2PeriphClockCmd(OV7670_DATA_RCC, ENABLE);//初始化时钟gpio_init_struct.GPIO_Mode = GPIO_Mode_IPU;//输入上拉gpio_init_struct.GPIO_Pin = OV7670_DATA_PIN;//OV7670数据口引脚初始化GPIO_Init(OV7670_DATA_PORT, &gpio_init_struct);GPIO_WriteBit(FIFO_OE_PORT, FIFO_OE_PIN, 0);
}
static void Sccb_Sid_Change_In(void)//引脚切换为输入
{GPIO_InitTypeDef gpio_init_struct;//结构体gpio_init_struct.GPIO_Mode = GPIO_Mode_IPU;//输入上拉gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);
}
static void Sccb_Sid_Change_Out(void)//引脚切换为输出
{GPIO_InitTypeDef gpio_init_struct;//结构体gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);
}
void FIFO_Reset_Read_Addr(void)//FIFO 读数据复位,通过直接操作寄存器来提高速度
{ GPIOC->BRR =1<<2; // RRST=0GPIOC->BRR =1<<4; // RCLK= 0GPIOC->BSRR =1<<4; // RCLK=1GPIOC->BRR =1<<4; // RCLK=0GPIOC->BSRR =1<<2; // RRST=1GPIOC->BSRR =1<<4; // RCLK=1
}
static void Start_Sccb(void)//SCCB设置,类似I2C
{GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);delay(DELAYTIME);
}static void Stop_Sccb(void)//stop命令,SCCB的停止信号
{GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);delay(DELAYTIME);
}
static void No_Ack(void)//noAck,用于连续读取中的最后一个结束周期
{GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);delay(DELAYTIME);GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);delay(DELAYTIME);
}
static u8 Get_Ack(void)
{u8 Error;Sccb_Sid_Change_In(); GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);Error= GPIO_ReadInputDataBit(SCCB_SID_PORT, SCCB_SID_PIN);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);delay(DELAYTIME);Sccb_Sid_Change_Out();//输出GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);return !Error;
}static u8 Sccb_Write_Byte(u8 dat)//写入一个字节的数据到SCCB
{u8 i;for(i=0;i<8;i++){GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, (((dat<>7);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0); delay(DELAYTIME);}GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);return Get_Ack();
}
static u8 Sccb_Read_Byte(void)//一个字节数据读取并且返回
{u8 i,rbyte=0;Sccb_Sid_Change_In();for(i=0;i<8;i++){GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);delay(DELAYTIME);if(GPIO_ReadInputDataBit(SCCB_SID_PORT, SCCB_SID_PIN)) rbyte|=(0x80>>i);delay(DELAYTIME);GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0); delay(DELAYTIME);} Sccb_Sid_Change_Out();//输出GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);return rbyte;
}
主函数部分
#include "stm32f10x.h"
#include "stm32f10x_it.h" #include "key.h"
#include "usart.h"
#include "delay.h"
#include "lcd.h"
#include "ov7670.h"
#include "rcc.h"
#include "ShowChar.h"
#include "discern.h"extern vu8 Red_Vlaue, Green_Value ,Blue_Value;//��ֵint main(void)
{ unsigned int scan_time = 0;STM32_Clock_Init(16); //��ʼ��ʱ��LCD_Init(); Key_Init(); //��ʼ�� KEY1 PA8OV7670_GPIO_Init(); //OV7670���ų�ʼ�������ڴ��ڳ�ʼ��ǰ��//USART1_init(); //��ʼ������ TIM3_Configuration(); //10Khz�ļ���Ƶ�ʣ�������5000Ϊ500ms LCD_Fill(0x6666); while(!Sensor_Init());LCD_Fill(0xF800);delayms(100);scan_time = 2;//��ֵ����ֵ Red_Vlaue = 24;Green_Value = 53;Blue_Value = 24;while(1){if(scan_time <= 1) {CameraDiscern(); //����ʶ��}if(scan_time > 1) {CameraScan(); //����ͷɨ�����LCD_ShowNum(30,220,21 - scan_time, 2); while(GPIO_ReadInputDataBit(KEY1_PORT,KEY1_PIN)==0) //��������{LCD_Fill(0x00); //����Show_Title(); //��ʾ����Show_Card(0); //��ʾ�ڼ��鳵��Show_Card(1);Show_Card(2);Show_Card(3);Show_Card(4);delay_ms(5000);}}if(scan_time == 20) {scan_time = 0;}scan_time++;}
}