从整体上来讲,就是车牌的一个检测,我仅仅做了数字的检测部分。
首先第一个是基本理论,其实百度车牌识别的话,能收集到很多的方法,具体是怎么一个过程,具体的我不多说。大概就是,定位车牌,首先滤波后,进行边缘检测,边缘为长方形的,比例差不多的那个就是车牌。然后进行分割,矫正,最后才到了数字的检测。
第二个是前期的matlab验证。网上的主要方法是,让数字竖向投影到一条直线上,也就是说,分割后的数字,竖向相加。还有一个是横向投影到一条直线上,就是数字的横向相加。无论哪种方法,最主要的目的是把数字按照自己的数据特性分离开。 方法如下图。
模式识别的最大要点,就是找到每个要检测的物体的差别特性。如果检测数字的时候,竖向投影,和横向投影,我用它的这两个特性依然检测不出数字是几的话,可能就要考虑斜向,或者连续点弯曲位置。用最大差异的特征去识别的话,效果就更好。
我做的模型,或者说模拟检测的时候,只用了2个维度,就是竖向相加和横向相加。
这张图是竖向相加的结果。从1到0顺序。随便选的字体。
这张图是横向相加的结果。从1到0顺序。随便选的字体。
按照这两组matlab的分析图,能看出,两个维度差异比较大,基本可以检测出是什么数字了。可是如果有噪声的影响下,同一个数字能分离开吗??
从图上来看6和9和8,0和8差异并不是很大。在受到多大干扰的情况就检测不出来了呢???
这个是数据1到5的差异图。
这样图是这么来的,在不同的情况下,用了不同的数据样本(没有人为加扰,字体相同)。然后同样做竖向和横向相加,然后看看这些样本的差别。
当然其他的干扰测试还需要很多。6和9,8和0,他们能承受多大的误差。这张图的0,和有干扰图的0,之间的差异性。最后才能决定用什么检测方法。
前面这些都是matlab分析过程,其实要写最终的matlab验证程序的,我发现我偷懒了,没有写,就直接verilog开始写程序了。
最后的检测方案。
横向比较,允许偏差一个数字80个像素点的偏差(当然图的大小有差异,这个数据自然会有)。竖向比较,偏差60吧。如果没有找到,就把最前面的点(竖向相加,最第一个数据点)和最后一个点,去掉,在匹配。如果还没检测出来,把整个数据加10像素点,在匹配。下一步整体减少10个像素点,在匹配。如果检测不出来,报错。
FPGA实现:
parameter idle_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001;
parameter clear_memory_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0010;
parameter receive_image_fifo_full = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0100;
parameter receive_imagel9_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1000;
parameter receive_imageh9_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0000;
parameter selet_vertical_start = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0010_0000;
parameter vertical_add_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0100_0000;
parameter vertical_sum_save = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1000_0000;
parameter find_edge_read_start = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0000_0000;
parameter save_data_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0010_0000_0000;
parameter competer_edge_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0100_0000_0000;
parameter save_edge_value_state1 = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000;
parameter save_edge_value_state2 = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000;
parameter selet_horizontal_start = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000;
parameter horizontal_add_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000;
parameter horizontal_sum_save = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000;
parameter read_save_data50_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000_0000;
parameter test_save_data50_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000_0000;
parameter read_rom_data_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000;
parameter example_data_read_out = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000_0000;
parameter data_compare_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000_0000_0000;
parameter data_abs_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000_0000_0000;
parameter shift_1time_register = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000;
parameter num_big_two_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000_0000_0000;
parameter test_counter_state = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000_0000_0000_0000;
parameter judge_code_test = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000_0000_0000_0000;
parameter read_ramh_data0 = 64'b0000_0000_0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000;
parameter read_ramh_data1 = 64'b0000_0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000_0000_0000_0000;
parameter left_shift_one = 64'b0000_0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000_0000_0000_0000_0000;
parameter data_compare_two = 64'b0000_0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000_0000_0000_0000_0000;
parameter data_abs_two = 64'b0000_0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000_0000;
parameter num_big_two_two = 64'b0000_0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter test_counter_two = 64'b0000_0000_0000_0000_0000_0000_0001_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter data_compare_three = 64'b0000_0000_0000_0000_0000_0000_0010_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter set_send_state_add = 64'b0000_0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter USB_send_emtry_state = 64'b0000_0000_0000_0000_0000_0000_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter USB_send_l9_state = 64'b0000_0000_0000_0000_0001_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter USB_send_h9_state = 64'b0000_0000_0000_0000_0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter data_abs_three = 64'b0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter num_big_two_three = 64'b0000_0000_0000_0000_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter test_counter_three = 64'b0000_0000_0000_0001_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter image_num_time = 64'b0000_0000_0000_0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter save_one_cycle = 64'b0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter shift_1time_three = 64'b0000_0000_0000_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
parameter shift_1time_two = 64'b0000_0000_0001_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
这个是我最后需要用的状态机数量。大概描述一下,状态机运行的顺序。
receive_imageh9_state状态前,都是在接收数字的图像,
好像一个一个状态解释,太多了。
大概顺序是这样的,1,接收到一个图像,5个数字。
2,分离,主要是2个数之间,有段没有数据,所以这个就是标志线。
3,竖向相加,求和。
4,第一次,进行比对。读rom,进行对比,对比如何误差过大,就进入下个数据,如果全没有,进入下个状态。
5,横向相加,求和。
6,第二次,进行比对。
7,看是什么数。如果没有回到第4部分。去掉头和尾,在比对。
8,最后就是整个竖向的和,都减10,和加10.在比对。
9,输出结果。
结果嘛。如果是相对标准的还好。只要换一种字体,就没法检测出来了。所以,如果需要实际用的话,还要有很多的改进,只能说大体思想和大体结构是这样的。
本人能力有限,但是我努力做到,奉献我的技术,希望你们快速成长,希望中国的技术可以腾飞。
如果有好的想法,有些挑战性的话,可以给我留言,有兴趣我也会去学一下的。