网上查到的资料,实现车牌边缘检测,分割出车牌区域,再进行处理,现在是定位不准,老是不能检测到车牌边缘,现将全部算法贴出,[mw_shl_code=c,true]#include "RECOG.h"
#include "board.h"
#include "lcd.h"
#include "ov7671.h"
#include "delay.h"
#include "text.h"
#include "sys.h"
#include "stdio.h"
#define OV7725_WRST PDout(6) //写指针复位
#define OV7725_WREN PBout(3) //写入FIFO使能
//---------------------------------------------------------------------------------------------------------------------字符定义区
u32 a;
u32 b;
u16 AA,BB;
u16 Min_blue;
u16 Max_blue;//定义车牌蓝 {MOD}区域的横向最大值和最小值
u8 TableChangePoint_240[240];//横向跳变点最大值240个
u8 TableChangePoint_320[320];//纵向跳变点最大值320个
u32 table[240][320]={0};
//u16 color,color_r,color_b,color_g;//灰度化处理变量
u16 color_save;//保存一个像素的值
u8 R_a=20,G_a=45,B_a=20;//阈值(二值化)
//u8 R_a,G_a,B_a;
u8 R,G,B;//颜 {MOD}分量
u8 Max_ChangePoint_240,Min_ChangePoint_240,Max_bChangePoint,Min_bChangePoint;//跳变点纵轴始、末坐标,跳变点横轴始、末坐标,用于确定车牌范围
u8 flag_MaxMinCompare;//完成最大值最小值比较的标志,Max_aChangePoint_reset_1和Max_aChangePoint_reset的标志
float V,S,H;//定义HSV值,用于HSV转换函数
u16 k1,kk1,k2,kk2,k3,kk3,k4,kk4,k5,kk5,k6,kk6,k7,kk7,k8,kk8;//八个字符边界
u8 table_picture[150];//定义保存图片的数组,储存摄像头返回的数据
extern u8 Table[6300];//所有字符集 (10+26)*150 = 5400 字节
extern u8 talble_0[150];//字符3,测试用
extern u8 table_yu[32];//渝字
extern u8 table_min[32];//闽字
extern u8 table_lu[32];//鲁字
extern u8 table_zhe[32];//浙字
extern u8 table_shan[32];//陕字
extern u8 table_cuan[32];//川字
u8 table_char[36]={0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',};
u8 table_cardMeasure[7];//测量的车牌结果
u8 table_card[5][8]={ //保存5个车牌的二维数组
{0,0,0,0,0,0,0,0}, //最后一位保存时间
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
};
#define OV7670_WRST PDout(6) //写指针复位
#define OV7670_WREN PBout(3) //写入FIFO使能
//----------------------------------------------------------------------------------------------------------240方向跳变点显示函数(纵)
void ChangePoint_Show_240()//240方向跳变点显示
{
u16 a,b;
for(a=0;a<240;a++)//显示对应的横向跳变点
{
for(b=0;b<320;b++)
{
TableChangePoint_240[a]=LCD_ReadPoint(a,b);
POINT_COLOR=RED;
LCD_DrawPoint(TableChangePoint_240[a],a);//跳变点显示,红 {MOD}标记
if(TableChangePoint_240[a]>=15) //跳变点个数(阈值)设定 阈值调节3-(1)
{
for(b=35;b<40;b++) //显示达到阈值标准的点
{
POINT_COLOR=GREEN; //暂时无用操作,后期修改
LCD_DrawPoint(b,a);//绿 {MOD}标记
}
}
}
}
//----------------------------------------------------------------------------------------------------------240方向车牌定位函数
void ChangePoint_Analysis_240()//240跳变点分析,即分析出车牌的纵向边界和高度
{
Min_ChangePoint_240=240;Max_ChangePoint_240=0;
for(a=0;a<240;a++)//240扫描 ,获取上下限值 :Min_ChangePoint_240,Max_ChangePoint_240
{
while(TableChangePoint_240[a]<=15) //阈值调节3-(2)
{
a++;
}
Min_ChangePoint_240=a;
while(TableChangePoint_240[a]>15) //阈值调节3-(3)
{
a++;
}
Max_ChangePoint_240=a;
if(Max_ChangePoint_240-Min_ChangePoint_240>=20) a=240;//连续性阈值 //阈值调节2-(1)
}
Min_ChangePoint_240=Min_ChangePoint_240-3;//向上微调3像素
Max_ChangePoint_240=Max_ChangePoint_240+2;//向下微调2像素
for(a=30;a<280;a++)//显示上界限 蓝 {MOD}定位
{
POINT_COLOR=BLUE;
LCD_DrawPoint(a,Max_ChangePoint_240);
}
for(a=30;a<280;a++)//显示下界限
{
POINT_COLOR=BLUE;
LCD_DrawPoint(a,Min_ChangePoint_240);
}
for(a=30;a<280;a++)//显示50,参考50像素位置处,车牌位置不能过红线,否则不能字符的归一化处理
{
POINT_COLOR=RED;
LCD_DrawPoint(a,Min_ChangePoint_240+50);
}
flag_MaxMinCompare=1;
if(Min_ChangePoint_240>Max_ChangePoint_240)//判断合法性1:最小值>最大值
{
flag_MaxMinCompare=0;
}
if(Min_ChangePoint_240==240||Max_ChangePoint_240==0)//判断合法性2:值没有重新赋值
{
flag_MaxMinCompare=0;
}
if(Max_ChangePoint_240-Min_ChangePoint_240<15) //判断合法性3: //阈值调节2-(2)
{
flag_MaxMinCompare=0;
}
}
//-------------------------------车牌区域二值化显示-------------------------------------------------------------------
void Card_101Show()//车牌二值化显示函数
{
u16 a,b,num_color;
u8 R1,G1,B1;
u8 Mid_ChangePoint_240;
u8 max_R,max_G,max_B,min_R,min_G,min_B;
u8 mid_R,mid_G,mid_B;
max_R=0;max_G=0;max_B=0;
min_R=30;min_G=60;min_B=30;
Mid_ChangePoint_240=(Min_ChangePoint_240+Max_ChangePoint_240)/2;
for(b=Min_blue;b<Max_blue;b++)
{
num_color=LCD_ReadPoint(b,Mid_ChangePoint_240);//读取像素点
R1=num_color>>11;
G1=(num_color>>5)&0x3F;
B1=num_color&0x1F;
if( (R1>10) && (G1>25) && (B1>15) && (R1<=30) && (G1<=60) && (B1<=30) )//
{
if(max_R<R1) max_R=R1;//
if(max_G<G1) max_G=G1;
if(max_B<B1) max_B=B1;
if(min_R>R1) min_R=R1;
if(min_G>G1) min_G=G1;
if(min_B>B1) min_B=B1;
}
}
mid_R=(max_R+min_R)/2;
mid_G=(max_G+min_G)/2;
mid_B=(max_B+ min_B)/2;
for(a=Min_ChangePoint_240;a<Max_ChangePoint_240;a++)
{
for(b=Min_blue;b<Max_blue;b++)
{
num_color=LCD_ReadPoint(b,a);//?áè?????£?′ú??ó??ˉ?ù?èóD′yìáéy £?é¨?è·?·¨ò2?éó??ˉ£?ò?ìáéy?ù?è
R1=num_color>>11;
G1=(num_color>>5)&0x3F;
B1=num_color&0x1F;
if((R1>=mid_R) && (G1>=mid_G) && (B1>=mid_B))//?t?μ?ˉ,???D?μ£o25.55.25£???o?êê?D?죨21,47,21£?
{
POINT_COLOR=0xffff;
LCD_DrawPoint(b,a);
}
else
{
POINT_COLOR=0x0000;
LCD_DrawPoint(b,a);
}
}
}
delay_ms(20000);
}
//-------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------颜 {MOD}转换函数,为下面的车牌蓝 {MOD}区域识别做准备
void RGB_HSV(u16 num)//RGB565转HSV 方便用于区分 {MOD}相、饱和度、和VALUE
{
float max,min;
u8 r,g,b;
r=(num>>11)*255/31;g=((num>>5)&0x3f)*255/63;b=(num&0x1f)*255/31;
max=r;min=r;
if(g>=min)max=g;
if(b>=max)max=b;
if(g<=max)min=g;
if(b<=min)min=b;
V=100*max/255;//转换为百分比
S=100*(max-min)/max;//扩大100倍显示
if(max==r) H=(g-b)/(max-min)*60;
if(max==g) H=120+(b-r)/(max-min)*60;
if(max==b) H=240+(r-g)/(max-min)*60;
if(H<0) H=H-360;
}
//--------------------------------------------------------------------------------------------------------320方向蓝 {MOD}区域的车牌定位函数
void ChangePoint_Analysis_Blue()//320蓝 {MOD}区域分析,采用读取像素,得结果Min_blue,Max_blue
{ //目的是分析出车牌的左右边界,即车牌的横向宽度。
u16 a,b,num_color;
u16 min_320,max_320;//各行的最小、最大值
Min_blue=0;Max_blue=320;
min_320=320;max_320=0;
for(a=Min_ChangePoint_240;a<Max_ChangePoint_240;a++)
{
for(b=30;b<320;b++)//不用到320 for(b=30;b<320;b++)
{
num_color=LCD_ReadPoint(b,a);//读取像素,代码优化速度有待提升 扫描方法也可优化,以提升速度
RGB_HSV(num_color);//RGB565转HSV
if( 250>H && H>190 && 60>S && S>15 && 100>V && V>45)//HSV 阈值
{
if(b<min_320)//获得横轴的Min和Max值,即蓝 {MOD}车牌的左右边界
{
min_320=b;
}
if(b>max_320)
{
max_320=b;
}
}
}
}
Min_blue=min_320;//获取各行的最大值//修正一点
Max_blue=max_320-5;//获取各行的最小值//修正一点
for(a=Min_ChangePoint_240;a<Max_ChangePoint_240;a++)//显示左界限
{
POINT_COLOR=RED;
LCD_DrawPoint(Min_blue,a);//LCD_DrawPoint(Min_blue,a,0xf800);
}
for(a=Min_ChangePoint_240;a<Max_ChangePoint_240;a++)//显示右界限
{
POINT_COLOR=RED;
LCD_DrawPoint(Max_blue,a);
}
// delay_ms(6000);
}
//-----------------------------------------------------------------------------------------------------蓝 {MOD}区域的跳变点分析函数
void ChangePoint_Analysis_320()//蓝 {MOD}区域中,320跳变点分析,获得TableChangePoint_320结果
{ //(先二值化,再判断白点个数,=0则是分割线)
u16 a,b,num_color;
u8 R1,G1,B1;
u8 Mid_ChangePoint_240;
u8 max_R,max_G,max_B,min_R,min_G,min_B;
u8 mid_R,mid_G,mid_B;
max_R=0;max_G=0;max_B=0;
min_R=30;min_G=60;min_B=30;
Mid_ChangePoint_240=(Min_ChangePoint_240+Max_ChangePoint_240)/2;
for(b=Min_blue;b<Max_blue;b++)
{
num_color=LCD_ReadPoint(b,Mid_ChangePoint_240);//读取像素,代码优化速度有待提升 ?扫描方法也可优化,以提升速度
R1=num_color>>11;
G1=(num_color>>5)&0x3F;
B1=num_color&0x1F;
R1=color&0xf800;
color >>=5;
G1=color&0x07e0;
B1=color>>11;
color_r = color&0xf800;
color_b = color>>11;
color >>=5;
color_g =color&0x07e0;
if( (R1>10) && (G1>25) && (B1>15) && (R1<=30) && (G1<=60) && (B1<=30) )//二值化,高阈值:25.55.25,较合适阈值(21,47,21)
{
if(max_R<R1) max_R=R1;//获得最大值和最小值
if(max_G<G1) max_G=G1;
if(max_B<B1) max_B=B1;
if(min_R>R1) min_R=R1;
if(min_G>G1) min_G=G1;
if(min_B>B1) min_B=B1;
}
}
mid_R=(max_R+min_R)/2;
mid_G=(max_G+min_G)/2;
mid_B=(max_B+ min_B)/2;
for(b=0;b<320;b++)//各行跳变点计数,数组清零
{
TableChangePoint_320=0;
}
for(a=Min_ChangePoint_240;a<Max_ChangePoint_240;a++)
{
for(b=Min_blue+1;b<Max_blue;b++)
{
num_color=LCD_ReadPoint(b,a);//读取像素,代码优化速度有待提升 ?扫描方法也可优化,以提升速度
// R1=color&0xf800;
// color >>=5;
// G1=color&0x07e0;
// B1=color>>11;
R1=num_color>>11;
G1=(num_color>>5)&0x3F;
B1=num_color&0x1F;
if((R1>=mid_R) && (G1>=mid_G) && (B1>=mid_B))//二值化,高阈值:25.55.25,较合适阈值(21,47,21)
{
POINT_COLOR=WHITE;
LCD_DrawPoint(b,a);
TableChangePoint_320++;//白 {MOD},跳变点+1
}
else
{
POINT_COLOR=BLACK;
LCD_DrawPoint(b,a);
}
}
}
}
//------------------------------------------------------------------------------------------------------------横向跳变点显示函数
void ChangePoint_Show_320()//320方向跳变点显示
{
for(a=0;a<320;a++)//显示对应的横向跳变点
{
POINT_COLOR=RED;
if(TableChangePoint_320[a]==0)
{
LCD_DrawPoint(a,0);//跳变点显示,红 {MOD}标记
}
else
{
LCD_DrawPoint(a,TableChangePoint_320[a]);//跳变点显示,红 {MOD}标记
}
}
}
//-------------------------------------------------------------------------------------------------------------字符分割函数
u8 ZhiFuFenGe()//字符分割,返回分割的字符个数,用于判断合法性
{
u16 b;
u8 i;//统计分割的字符个数,不为9说明分割有误
i=0;//必须置0一下,不然会有错
for(b=Max_blue;b>Min_blue;b--)
{
if(TableChangePoint_320==0)//间隙分割
{
i--;b++;
while(TableChangePoint_320==0)
{
b--;
if(b<=Min_blue) break;
}
}
}
i--;
LCD_ShowNum(30,220,i,2,24);//显示分割的字符个数+1,8是正常值
return i;
}
//-------------------------------------------------------------------------------------------------------------归一化处理函数 统一25*50
void GuiYi(u16 k,u16 kk)//归一化 25*50
{
u16 a,b;
u16 num;//保存读取像素
u8 Mo,Yu;//取整和取模
u8 num2,num3;
u8 Mo_1;//
u8 Min_240,Max_240;//框紧字符后的上下限
if((k-kk)<25)
{
//框紧字符
Min_240=Min_ChangePoint_240+1;
Max_240=Max_ChangePoint_240-1;
while(Min_240++)//框紧后,得到: Min_240
{
for(b=kk-10;b<k;b++)//kk1→k1
{
num=LCD_ReadPoint(b,Min_240);
if(num) break;
}
if(num) break;
}
while(Max_240--)//框紧后,得到: Max_240
{
for(b=kk+1;b<k;b++)//kk1→k1
{
num=LCD_ReadPoint(b,Max_240);
if(num) break;
}
if(num) break;
}
Min_240-=1;
Max_240+=2;
POINT_COLOR=WHITE;
LCD_DrawPoint(kk,Min_240);//显示复制的图片
LCD_DrawPoint( k,Max_240);//显示复制的图片
//显示复制的图片
num3=0;
for(a=Min_240+1;a<Max_240;a++)
{
num2=10;
for(b=kk+1;b<k;b++)//kk1→k1 +1
{
num=LCD_ReadPoint(b,a);
POINT_COLOR=num;
LCD_DrawPoint(271-(k-kk-1)+num2,191+num3);//复制像素值
num2++;
}
num3++;
}
num3=0;
Mo=(24-(k-kk-1))/(k-kk-1);//取模
Yu=(24-(k-kk-1))%(k-kk-1);//取余
if(Yu!=0)
{
Mo_1=24/Yu;//平均Mo_1个像素,插有一个像素,机7+1
}
for(a=Min_240+1;a<Max_240;a++)//宽放大为25像素 =??
{//↓
num2=10;
Yu=(24-(k-kk-1))%(k-kk-1);//取余
for(b=kk+1;b<k;b++)//kk1→k1 +1
{
num=LCD_ReadPoint(b,a);
POINT_COLOR=num;
LCD_DrawPoint(271+num2,191+num3);
num2++;
Mo=(24-(k-kk-1))/(k-kk-1);//取模
while(Mo)
{
POINT_COLOR=num;
LCD_DrawPoint(271+num2,191+num3);
Mo--;
num2++;
}
if(Yu!=0)//横轴拉长
{
if(((num2+1)%Mo_1==0) && (num2!=1))//改插入的地方7+1
{
POINT_COLOR=num;
LCD_DrawPoint(271+num2,191+num3);
Yu--;
num2++;
}
}
}
num3--;
}
POINT_COLOR=GREEN;
LCD_DrawPoint(271,191);//标记点,4个顶角,绿 {MOD}
LCD_DrawPoint(271,240);
LCD_DrawPoint(295,191);
LCD_DrawPoint(295,240);
//纵轴拉长
if((Max_240-Min_240)<50)
{
Mo=(50-(Max_240-Min_240+1))/(Max_240-Min_240+1);//取模
Yu=(50-(Max_240-Min_240+1))%(Max_240-Min_240+1);//取余
Mo_1=50/Yu;
// LCD_ShowNum(30,170,Mo,3);// <调试用>
// LCD_ShowNum(70,170,Yu,3);//
// LCD_ShowNum(100,170,Max_ChangePoint_240-Min_ChangePoint_240,3);//
num2=0;
for(a=0;a<(Max_240-Min_240);a++)//复制图像,考虑范围是否需要进行修正?
{//↓
for(b=271;b<=295;b++)//271开始复制,295才结束
{
num=LCD_ReadPoint(b,a+191);
POINT_COLOR=num;
LCD_DrawPoint(b+25,191+num2);//复制像素值
}
num2++;
while(Mo)
{
for(b=271;b<=295;b++)//271开始复制,295才结束
{
num=LCD_ReadPoint(b,a+191);
POINT_COLOR=num;
LCD_DrawPoint(b+25,191+num2+a);//复制像素值
}
Mo--;
num2++;
}
if(Yu!=0)
{
if((((num2+1) % Mo_1)==0)&& (num2!=1))
{
for(b=271;b<=295;b++)//271开始复制,295才结束
{
num=LCD_ReadPoint(b,a+191);
POINT_COLOR=num;
LCD_DrawPoint(b+25,191+num2);//复制像素值
}
Yu--;
num2++;
}
}
}
}
POINT_COLOR=RED;
LCD_DrawPoint(320,191);//标记点,1个顶角
printf("正常");
}
}
//------------------------------------------------------------------------------------------------------图片->数组table_picture 待修改
void Picture_String()//图片->数组table_picture 有个BUG
{
u16 a,b,num1;
for(a=0;a<150;a++)//归零
{
table_picture[a]=0x00;
}
for(a=0;a<50;a++)//50排
{
for(b=0;b<24;b++)//24行
{
num1=LCD_ReadPoint(b+296,a+191);
if(num1==0xffff)
{
table_picture[b/8+a*3]|=(1<<(7-b%8));
}
}
}
}
//-------------------------------------------------------------------------------------------------------------进行字符匹配
u8 MoShiShiBie_All(u8 begin,u8 end)//字符匹配,模式识别,选择性匹配begin-end
{
u16 num_save;
u8 a,b,e,a_save,st1,st2,s1,s2;
int num1;
for(a=begin;a<end;a--) //36
{
num1=0;
for(b=0;b<110;b++)
{
st1=table_picture;
st2=Table[150*a+b];
for(e=0;e<8;e++)
{
s1=st1&(1<<e);
s2=st2&(1<<e);
if(s1==s2) num1++;
if(s1!=s2) num1--;
}
}
if(num_save<num1)
{
num_save=num1;
a_save=a;
}
// LCD_ShowNum(50,220,a,2,24);//显示匹配的字符是"a" <调试用>
LCD_ShowNum(70,220,num1,4,24);//显示匹配的正确像素数
LCD_ShowNum(120,220,num_save,4,24);//匹配的最大值显示
}
return a_save;
}
//--------------------------------------------------------------------------------------------------------------显示16X16的汉字
void WordShow(u8 num,u16 x,u16 y)//显示汉字16*16
{
u16 a,b,num1;
u8 table1[32];
if(num==1)
{
for(a=0;a<32;a++)
{
table1[a]=table_yu[a];
}
}
if(num==2)
{
for(a=0;a<32;a++)
{
table1[a]=table_min[a];
}
}
if(num==3)
{
for(a=0;a<3;a++)
{
table1[a]=table_lu[a];
}
}
if(num==4)
{
for(a=0;a<32;a++)
{
table1[a]=table_zhe[a];
}
}
if(num==5)
{
for(a=0;a<32;a++)
{
table1[a]=table_shan[a];
}
}
if(num==6)
{
for(a=0;a<32;a++)
{
table1[a]=table_cuan[a];
}
}
for(a=0;a<16;a++)
{
for(b=0;b<16;b++)
{
if(table1[b/8+a*2]&(1<<(7-b%8)))
{
num1=0xffff;
}
else
{
num1=0x0000;
}
POINT_COLOR=num1;
LCD_DrawPoint(b+x,a+y);//画点
}
}
}
//-------------------------------------------------------------------------------------------------------------显示第几组车牌
void Show_Card(u8 i)//显示第几组车牌
{
u16 t0;
//显示汉字
if(table_card[0]!=0)
{
WordShow(table_card[0],9,i*16+50);//
}
if(table_card[1]<10)
{
LCD_ShowNum(25,i*16+50,table_char[table_card[1]],1,24);
}
else
{
LCD_ShowChar(25,i*16+50,table_char[table_card[1]],24,0);
}
LCD_ShowChar(33,i*16+50,'.',24,0); //点
if(table_card[2]<10)
{
LCD_ShowNum(41,i*16+50,table_char[table_card[2]],1,24);
}
else
{
LCD_ShowChar(41,i*16+50,table_char[table_card[2]],24,0);
}
if(table_card[3]<10)
{
LCD_ShowNum(49,i*16+50,table_char[table_card[3]],1,24);
}
else
{
LCD_ShowChar(49,i*16+50,table_char[table_card[3]],24,0);
}
if(table_card[4]<10)
{
LCD_ShowNum(57,i*16+50,table_char[table_card[4]],1,24);
}
else
{
LCD_ShowChar(57,i*16+50,table_char[table_card[4]],24,0);
}
if(table_card[5]<10)
{
LCD_ShowNum(65,i*16+50,table_char[table_card[5]],1,24);
}
else
{
LCD_ShowChar(65,i*16+50,table_char[table_card[5]],24,0);
}
if(table_card[6]<10)
{
LCD_ShowNum(73,i*16+50,table_
F1会不会吃不消啊,矩阵运算什么的
一周热门 更多>