车牌字符分割

合集下载

车牌的定位与字符分割 报告

车牌的定位与字符分割  报告

车牌的定位与分割实验报告一实验目的针对交通智能系统所拍摄的汽车图片,利用设定的算法流程,完成对汽车车牌部分的定位,分割车牌部分,并完成字符的分割,以便于系统的后续分析及处理。

二实验原理详见《车牌的定位与字符分割》论文。

三概述1一般流程车牌自动识别技术大体可分为四个步骤:图像预处理、车牌定位与分割、车牌字符的分割和车牌字符识别。

而这四个步骤又可归结为两大部分:车牌分割和车牌字符识别。

图1-1为车牌自动识别技术的一般流程图。

2本实验的流程(1)图像预处理:图像去噪(2)车牌的定位:垂直边缘检测(多次)形态学处理的粗定位合并邻近区域结合车牌先验知识的精确定位(3)车牌预处理:车牌直方图均衡化倾斜校正判定(蓝底白字或者黄底黑字)归一化、二值化(4)字符的分割:垂直投影取分割阈值确定各个字符的左右界限(结合字符宽度、间隔等先验知识)分割字符四实验过程4.1图像预处理4.1.1图像去噪一般的去噪方法有:空间域上的均值滤波和中值滤波;频率域上的巴特沃斯滤波器。

图4-1是各滤波器处理椒盐噪声的效果。

a.被椒盐噪声污染的图片 b.均值滤波的效果图 c.中值滤波的效果图 d.BLPF的效果图图4-1 各滤波器处理椒盐噪声的仿真可见,中值滤波对椒盐噪声的处理效果极好,而一般所拍摄的图片上最多的便是孤立的污点,所以此处以中值滤波为主进行去噪。

图4-2是采用中值滤波处理实际汽车图片的效果。

a.原始图像b.灰度图像c.中值滤波后的图像图4-2 中值滤波处理实际汽车图片的效果很显然,经过中值滤波后去除了原图上的部分污点。

4.1.2图像复原由于通常情况下都不知道点扩展函数,所以我们采用基于盲解卷积的图像复原策略。

图4-3~4-7图是函数进行盲解卷积的实验结果,其中图4-3是图像cameraman 的模糊图像。

图4-3 模糊图像在盲解卷积处理中,选择适当大小的矩阵对恢复图像的效果很重要。

PSF的大小比PSF的值更重要,所以首先指定一个有代表性的全1矩阵作为初始PSF。

【车牌识别】-车牌中字符分割代码详解

【车牌识别】-车牌中字符分割代码详解

【车牌识别】-车牌中字符分割代码详解车牌识别项⽬中,关于字符分割的实现:思路: 1. 读取图⽚,使⽤ cv2 。

2. 将 BGR 图像转为灰度图,使⽤ cv2.cvtColor( img,cv2.COLOR_RGB2GRAY) 函数。

3. 车牌原图尺⼨(170, 722) ,使⽤阈值处理灰度图,将像素值⼤于175的像素点的像素设置为 255 ,不⼤于175的像素点的像素设置为0 。

4.观察车牌中字符,可以看到每个字符块中的每列像素值的和都不为 0 ,这⾥做了假设,将左右结构的省份简写的字也看作是由连续相邻的列组成的,如 “ 桂 ” 。

5. 对于经过阈值处理的车牌中的字符进⾏按列求像素值的和,如果⼀列像素值的和为 0,则表明该列不含有字符为空⽩区域。

反之,则该列属于字符中的⼀列。

判断直到⼜出现⼀列像素点的值的和为0,则这这两列中间的列构成⼀个字符,保存到字典character_dict 中,字典的 key 值为第⼏个字符 ( 下标从0开始 ),字典的value值为起始列的下标和终⽌列的下标。

character_dict 是字典,每⼀个元素中的value 是⼀个列表记录了夹住⼀个字符的起始列下标和终⽌列下标。

6. 之后再对字符进⾏填充,填充为170*170⼤⼩的灰度图(第三个字符为⼀个点,不需要处理,跳过即可。

有可能列数不⾜170,这影响不⼤)。

7. 对填充之后的字符进⾏resize,处理成20*20的灰度图,然后对字符分别进⾏存储。

代码实现:1### 对车牌图⽚进⾏处理,分割出车牌中的每⼀个字符并保存2# 在本地读取图⽚的时候,如果路径中包含中⽂,会导致读取失败。

34import cv25import paddle6import numpy as np7import matplotlib.pyplot as plt8#以下两⾏实现了在plt画图时,可以输出中⽂字符9 plt.rcParams['font.sans-serif']=['SimHei']10 plt.rcParams['axes.unicode_minus'] = False111213# cv2.imread() 读进来直接是BGR 格式数据,数值范围在 0~255 。

车牌字符分割算法研究

车牌字符分割算法研究

1 绪论1.1 背景介绍为了实现车牌字符识别,通常要经过车牌位置检测、车牌字符分割和字符识别三个关键步骤。

车牌位置检测是根据车牌字符目标区域的特点,寻找出最符合车牌特征的区域。

车牌字符分割就是在车牌图像中找出所有字符的上下左右边界,进而分割出每个车牌字符。

在实际应用中,车牌字符分割的效果对车牌字符识别正确率会产生很大的影响,由于车牌图像亮度不均、尺度变化、透视失真、字符不完整等因素,使图像质量存在较大差异,进而影响图像分割的效果,因此车牌字符分割这一技术仍然具有很大的研究意义。

在实际的监控场景中,车牌图像的透视失真通常是由于拍摄视角的变化或车辆位置的移动,相机光轴偏离车牌平面的法线方向造成的。

由于车牌图像在整幅图像中占有较小的比例,所以车牌图像几何校正主要工作是校正车牌图像的旋转和剪切失真。

旋转投影法和直线拟合法是两种主要的偏斜校正方法。

旋转投影法是为了获取垂直倾斜角,即将车牌图像穷举逐个角度进行剪切变换,然后统计垂直投影数值为0的点数,得到最大值对应的角度。

这种方法受背景区域的干扰比较大。

另一种方法是直线拟合车牌字符的左边界点从而获得垂直倾斜角,该方法为直线拟合法。

该方法并没有逐个角度对车牌图像进行剪切变换,从左边界点拟合出的直线通常不能真正用来代表车牌的垂直倾斜方向,检测出的角度存在较大误差,且字符左侧噪声对角度检测干扰太大,鲁棒性较差。

因此找到一种更准确和迅速的车牌垂直倾斜矫正方法是十分重要的。

通过得到最小的字符投影点坐标方差,得到另一种车牌垂直矫正方法。

首先将车牌字符图像进行水平校正,根据字符的区域的上下边界,将车牌字符进行粗分割。

然后将剪切变换后的字符点进行垂直偷用。

当得到投影点最想左边方差时,便能导出两类剪切角闭合表达是,最后便是确定垂直投影的倾斜角并对此进行校正。

投影法是目前最常用的车牌分割算法之一,其算法简单并且计算复杂度低。

该方法的核心思想是将车牌图像进行水平投影和垂直投影,利用峰谷特征来定位车牌字符的上下左右边界。

车牌识别中的号码分割方法

车牌识别中的号码分割方法

车牌识别中的号码分割方法刘聪074301027车牌拍照自动识别技术是实现车辆身份自动识别的一种途径。

是近年来计算机视觉与模式识别技术在智能交通领域应用的重要研究课题之一。

因为车辆牌照号码和车辆是一一对应的,如同居民身份证和每个公民的关系一样,因此车辆牌照字符自动识别系统可用于一切需要对车辆进行管理的场合,比如公路收费站、停车场、十字路口等交通关卡等处,对于道路交通、园区和停车场车辆管理具有巨大的经济价值和现实意义。

车牌识别方法可以分为3个部分:车牌定位;车牌字符分割;车牌字符识别.这3部分紧密相关,每一步的输出都将作为下一步处理的输入,因此每一步的精确度都直接影响下一步的工作,从而影响车牌识别的最终结果。

首先要输入原始图像,从原始图像中确定车牌的相对位置,,完成对车牌的倾斜度校正;从提取的图像中切分出单个字符;最后输出车牌号码的字符串。

其中车牌字符的正确分割是进行下一步车牌字符识别的基础,目前常用的方法有如下几种:(1)车牌区域纹理特征的方法。

这种方法是利用车牌区域字符和底色具有不同的灰度特征从而进行边框去除和字符分割的方法。

(2)基于数学形态学的方法。

利用形态学的腐蚀和膨胀,将车牌字符区域组成连通域的方法去除边框,再进一步采用字符连通域的形式进行字符切分。

(3)基于Hough变换的字符分割方法。

还有人完全利用Hough变换,寻找车牌字符的上下边界,再结合车牌字符的排列特征进行字符的分割。

在得到一个或几个车牌区域后,先将其分割为单个的字符,然后进行单个字符的识别.传统的方法是使用投影法进行分割.先统计定位出的车牌区域的直方图,然后将统计值等于。

的几列中的一列作为分割的界限.这种分割方法实现起来比较简单,但是适应性很差,在预处理效果不好的情况下,很难有满足条件的列.而为了获取满足传统方法的条件,只有增加预处理的条件.因此,处理完后的图像不可避免地损失一部分有用信息,为进一步的处理造成不必要的困难,还可能引入额外误差.并且,在车牌倾斜的角度比较大或被拍摄车牌上的字符比较密集时,投影的方法从本质上失去了意义.为了避免上述两种弊端,采用一种新方法分割单个字符.该方法既可从一定程度上消除了预处理效果较差带来的影响,又可从根本上解决了倾斜车牌分割的间题.为了说明该方法的效果,使用了一个有较大倾斜角度的车牌做试验,如图3所示.首先,按照上一个步骤中精确定位车牌位置的方法,确定各个连通区域,去掉矩形区域中一些不可能是字符区域部分,例如宽度过大(约大于1/7车牌)或是连通区域中像素的个过少的区域,然后做出剩余各个连通区域的外接矩形,选取高和宽比值最为接近的4个连通区域,如图d所示.通过研究发现,车牌在正常或倾斜的情况下,(这种倾斜的情况包括车牌本身倾斜或由于拍摄的角度造成的倾斜),车牌上各个字符的顶点实际上是在一条直线上,而大部分的数字、字母的结构是上下、左右基本对称的,所以各个数字和字母的中心也基本在一条直线上.因此,依据各连通区域的信息拟合一条直线可以取各连通区域的中点,也可以取各连通区域的最高和最低点拟合这条直线,在多数情况下,能够得到较好的拟合效果.利用拟合出的1条或2条直线,就能够比较准确地确定车牌上字符的上下边界.图c中所示的2条直线就是利用字符的上边缘和下边缘拟合出来的.假设拟合的直线斜率为k,则所有字符的外接矩形与这条直线的相交角度是相同的.从图d中的4个矩形中任意选取一个,将其假定为字符的大小,然后将这个矩形沿斜率为k的直线来回运动,按照车牌字符分布的特点,这个矩形框应该可以包含每一个字符.由此可提取每一个字符,因为如果将汉字看作连通区域,1个汉字则是由几个连通区域组成,而这个矩形框恰好可以将这些连通区域包含在内.并且像1这个高和宽的比值与其他的字符不同的数字,也能借助拟合的直线解决这一问题.本系统并未对倾斜的字符进行矫正,因为经研究发现,车牌上的字符因倾斜会产生一定程度的变形,但是这种变形并非是在二维空间上产生的.因此对其进行矫正仍有一定的困难.所以,对于倾斜字符的识别,可以通过在下一步中加大训练集解决.。

车牌识别系统功能和参数

车牌识别系统功能和参数

车牌识别系统功能和参数车牌识别系统是一种用于自动识别和识别出车辆号牌的技术。

它利用计算机视觉和模式识别的原理和技术,通过图像处理和特征提取等方法,从输入的图像中提取车牌号码并进行识别。

车牌识别系统主要可以分为图像采集、车牌定位、字符分割、字符识别和车牌识别五大模块。

首先,车牌识别系统的功能包括图像采集、车牌定位、字符分割、字符识别和车牌识别等。

通过图像采集模块,可以采集到来自摄像头或其他图像输入设备的车辆图像。

车牌定位模块可以对车辆图像进行处理,找出图像中的车牌位置。

字符分割模块可以将车牌图像中的字符进行分割,从而得到单个字符图像。

字符识别模块使用OCR(光学字符识别)技术,对字符进行识别,并将字符的识别结果输出。

最后,车牌识别模块通过将字符的识别结果进行组合,得到完整的车牌号码,并输出识别结果。

1.图像采集参数:包括图像分辨率、拍摄角度、曝光度、对比度等。

合理的图像采集参数可以保证车牌在图像中的清晰可见性,减少图像中的噪声和干扰。

2.车牌定位参数:包括车牌的位置、大小、高度、宽度等。

通过调整车牌定位参数,可以准确地找到车牌在图像中的位置,排除其他干扰因素。

3.字符分割参数:包括字符之间的间距、字符的大小、字符的高度、宽度等。

合适的字符分割参数可以确保字符之间的距离和大小符合标准,并准确地划分字符。

4.字符识别参数:包括字符模板库、字符识别算法、识别率等。

良好的字符识别参数可以提高字符识别的准确度和速度。

5.车牌识别参数:包括车牌识别算法、车牌号码格式、识别结果输出等。

优化的车牌识别参数可以保证系统对各种车牌号码的识别正确率,快速地输出识别结果。

除了以上几个参数之外,还有一些额外的参数可以用于进一步优化系统的性能,如图像预处理参数、特征提取参数、分类器参数等。

这些参数的选择和调整可以根据实际应用需求和系统性能要求进行调整。

总之,车牌识别系统的功能和参数都是为了实现车牌号码的自动识别和识别而设计的。

利用反馈的车牌字符分割算法

利用反馈的车牌字符分割算法
Байду номын сангаас
Ke w r s y o d
Lc n epa h rc r e m na o I a epe po es g P o c o F e b c D srt c s et n f ma o i s l ec aat g e t in m g r —rcsi r e t n e d a k e t es t n j i i e oi r s r t n c e n a o i
李文举 姜周恩 朱正强 崔晓松
( 辽宁师范大学计算 机与信息技术学 院 辽宁 大连 16 8 ) 10 1


字符 分 割是 车 牌 识 别 系 统 的重 要 步 骤 。 提 出一 种 利 用 反 馈 的 车 牌 字 符 分 割 算 法 。 首 先 , 车 牌 图像 进 行 预 处 理 ; 次 , 对 其
c r c e s Th is e e ha a t r . e fr t l v l ̄ e a k i h e d c a e n c r c e s c u n he wi h o i g e c r c e t e s c nd l v lf e b c s db c s t e f e ba k b s d o ha a t r ’ o nta d t dt f s n l ha a t r,h e o e e e d a k i
u e o o g e me tt n o e c aa t r o h lt ;i al ,w — v lfe b c s e l y d t o d c n e me tt n o e p ae s d f rr u h s g na i ft h r c es n t e pa e f l t o l e e d a k i mp o e o c n u tf e s g n ai ft lt o h n y e i o h

常用的车牌识别算法

常用的车牌识别算法

常用的车牌识别算法包括以下几种:
1. 车牌定位算法:用于确定车辆图像中车牌的位置。

这种算法通常会使用图像处理技术,如梯度信息投影统计、小波变换、车牌区域扫描连线算法等,以识别图像中的车牌区域。

2. 字符分割算法:在车牌定位后,需要将车牌中的字符进行分割。

这种算法通常会使用图像处理技术和机器学习算法,如基于深度学习的字符分割算法,以准确地将各个字符分割开来。

3. 字符识别算法:用于识别分割后的字符。

这种算法通常会使用机器学习算法,如卷积神经网络(CNN)或循环神经网络(RNN),以对字符进行分类和识别。

4. 神经网络识别算法:大规模神经网络识别算法是一种深度学习算法,它能够同时处理车牌定位和字符识别两个任务,具有更高的准确性和鲁棒性。

5. 启发式车牌定位算法:综合利用了图像处理技术和机器学习算法,以提高车牌定位的准确性。

这种算法通常会使用一些特征选择方法,如SVM、HOG等,以将车牌区域和非车牌区域进行区分。

6. 角度偏差和光照波动控制算法:在车牌定位和字符识别过程中,车辆的角度偏差和光照波动会影响算法的准确性。

这种算法通常会使用一些图像处理技术,如滤波、归一化等,以减小这些因素的影响。

这些算法在车牌识别过程中相互配合,以实现准确的车牌识别。

车牌识别系统中车牌定位与字符分割的研究

车牌识别系统中车牌定位与字符分割的研究

车牌识别系统中车牌定位与字符分割的研究一、本文概述随着科技的发展和智能交通系统的普及,车牌识别系统已经成为了现代交通管理的重要组成部分。

车牌识别系统的核心在于准确、快速地实现车牌的定位与字符分割。

本文旨在深入探讨车牌识别系统中车牌定位与字符分割的关键技术,并分析其在实际应用中的挑战与解决方案。

本文将对车牌识别系统的基本框架进行概述,介绍车牌定位与字符分割在其中的地位和作用。

接着,本文将详细阐述车牌定位技术的发展历程和现状,包括基于颜色、纹理、形状等特征的定位方法,以及近年来兴起的深度学习技术在车牌定位中的应用。

同时,本文还将对字符分割技术的研究现状进行梳理,包括基于投影分析、边缘检测、形态学处理等方法的字符分割算法。

在此基础上,本文将重点分析车牌定位与字符分割在实际应用中面临的挑战,如复杂背景下的车牌定位不准确、字符粘连或断裂导致的分割失败等问题。

针对这些问题,本文将提出相应的解决方案,如通过改进算法提高定位精度、采用多特征融合的方法提高字符分割的鲁棒性等。

本文将通过实验验证所提方法的有效性,并对实验结果进行分析和讨论。

本文还将展望车牌识别系统的未来发展趋势,探讨新技术在车牌定位与字符分割中的应用前景。

通过本文的研究,旨在为车牌识别系统的优化和改进提供有益的参考和借鉴。

二、车牌定位技术研究车牌定位技术是车牌识别系统的关键环节,它涉及从复杂的背景中准确提取出车牌区域。

随着计算机视觉和图像处理技术的不断发展,车牌定位技术也取得了显著的进步。

早期的车牌定位主要基于车牌的颜色和边缘特征。

由于中国车牌通常为蓝底白字,因此可以通过颜色过滤来初步提取出可能的车牌区域。

随后,利用边缘检测算法(如Canny边缘检测)来进一步细化车牌的轮廓,从而实现车牌的粗定位。

然而,这种方法受光照条件、车牌污损等因素影响较大,定位准确性有待提高。

为了克服颜色和边缘特征方法的局限性,研究人员开始尝试基于纹理和形状特征的车牌定位方法。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

图像处理包括图像二值化、车牌定位、字符分隔、字符识别。

每一步都关系系统成功与否以及好坏。

如果图片二值化不好就不方便车牌定位,如果定位的车牌图片不准确就谈不上字符的切割,字符图片切割不好就难以识别。

这些应该很好理解,可见成员之间需要很好的默契。

而我负责了图像处理中的字符分隔模块,起初我不知道位图形式以及如何读取位图,可见我获取信息的主动性和能力并不好。

非常感谢其他组员提供了读取位图像素数据的相关方法,才能使我能放心去思考切割的算法,而不必去担心如何获取数据的问题。

我使用了一种字符像素横向和纵向扫描的算法,得到字符在横向和纵向的像素分布波形,通常是缓慢的连续变化,车牌越模糊,变化越缓慢。

自然,波峰是字符区,波谷是字符间的空隙区。

它们的分界点并不明显,必然需要找到介于波峰与波谷之间的一个阀值,将波形变成01直方波形。

那么阀值自然是个关键,如果定得不准,就可能切不出所有字符,这是我之前遇到的问题,那时我固定了阀值,使它介于平均波峰值和平均波谷值之间的某个固定点,但这通常只能切割出模糊图片的部分字符,因为有些波峰和波谷并没有被切分开来。

于是我采用了另一种策略,即使用动态扫描,从最小的波谷扫到最大的波峰,并不断计算切得的波峰数量(实际就是字符数量)。

然后判断这个切割数是否符合实际车牌上的字符数量,如果符合,可以停止扫描,切割位置可以明确定在波峰和波谷的变化点上。

当然,我进行了各种优化,比如更多判断来排除各种车牌边框等干扰。

在DOS窗口上经过反复的数据显示测试,终于得到了非常不错的字————————————————————————————————————————————(1)利用字符像素XY方向扫描;(2)分析波形;(3)动态指定阀值;(4)获得01分布;(5)判断波形变化次数;(6)去干扰;(7)获得切割位置;时间有限,有不完善之处可以去本人博客提问:/flashforyou#pragma once#include <cstring>#include <cmath> //数学函数库#include <cstdio>#include <cstdlib>#include <cmalloc>#include "stdafx.h"#include <complex>#define WIDTHBYTES(bits) (((bits)+31)/32*4)/////////////////////////////////////typedef unsigned char BYTE;typedef unsigned short WORD;typedef unsigned long DWORD;typedef long LONG;///////////////////////////////////////***位图文件头信息结构定义//其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息)typedef struct tagBITMAPFILEHEADER {DWORD bfSize; //文件大小WORD bfReserved1; //保留字,不考虑WORD bfReserved2; //保留字,同上DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和} BITMAPFILEHEADER;///////////////////////////////////////***信息头BITMAPINFOHEADER结构,其定义如下:typedef struct tagBITMAPINFOHEADER{//public:DWORD biSize; //指定此结构体的长度,为40LONG biWidth; //位图宽LONG biHeight; //位图高WORD biPlanes; //平面数,为1WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32 DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩DWORD biSizeImage; //实际位图数据占用的字节数LONG biXPelsPerMeter; //X方向分辨率LONG biYPelsPerMeter; //Y方向分辨率DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的} BITMAPINFOHEADER;/////////////////////////////////////////***调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。

24位和32位是不需要调色板的。

//(似乎是调色板结构体个数等于使用的颜色数。

)typedef struct tagRGBQUAD {//public:BYTE rgbBlue; //该颜色的蓝色分量BYTE rgbGreen; //该颜色的绿色分量BYTE rgbRed; //该颜色的红色分量BYTE rgbReserved; //保留值} RGBQUAD;//////////////////////////////////////typedef struct tagHSI {//public:double hsiH; //该颜色的Hdouble hsiS; //该颜色的Sdouble hsiI; //该颜色的Idouble hsiTh; //该颜色的Th} HSIInfo;//////////////////////////////////////typedef struct tagCUT {//public:int no; //切块编号int up; //切块的上坐标int down; //切块的下坐标int left; //切块的左坐标int right; //切块的右坐标} CUT;//////////////////////////////////////////////////***RGB输出函数//参数说明:<--调色板对象-->void showRgbQuan(tagRGBQUAD* pRGB){//printf("(%-3d,%-3d,%-3d) ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);if(pRGB->rgbRed>0)printf(" ");elseprintf("[X]");}//////////////////////////////////////////////////***设定位图文件头信息//参数说明:<>void MakeBmpHead(BITMAPFILEHEADER &pBmpHead,DWORD biSizeImage){pBmpHead.bfSize=biSizeImage+62;pBmpHead.bfReserved1=0;pBmpHead.bfReserved2=0;pBmpHead.bfOffBits=62;}/////////////////////////////////////////////////***设定位图信息头信息//参数说明:<位图信息头,为图宽,为图高>void MakeBmpInforHead(tagBITMAPINFOHEADER &pBmpInforHead,LONG width,LONG height){ pBmpInforHead.biSize=40;pBmpInforHead.biWidth=width;pBmpInforHead.biHeight=height;pBmpInforHead.biPlanes=1;pBmpInforHead.biBitCount=1;pBmpInforHead.biCompression=0;pBmpInforHead.biSizeImage=WIDTHBYTES(width) * height;pBmpInforHead.biXPelsPerMeter=3780;pBmpInforHead.biYPelsPerMeter=3780;pBmpInforHead.biClrUsed=0;pBmpInforHead.biClrImportant=0;}////////////////////////////////////////////////***设定调色板信息//参数说明:<调色板对象>void MakeColorPlant(tagRGBQUAD *pRgb,long nPlantNum) {for(int i=0;i<nPlantNum;i++){pRgb[i].rgbRed=i*(255);pRgb[i].rgbGreen=i*(255);pRgb[i].rgbBlue=i*(255);pRgb[i].rgbReserved=i;}}/////////////////////////////////////////////////***创建BMP文件//参数说明:<bmp文件名,调色板对象,宽度,高度>void MakeBmpHeader(char* strFile,tagRGBQUAD* dataOfBmp,LONG widthBmp,CUT cut){ FILE* pf;BITMAPFILEHEADER bitHead;BITMAPINFOHEADER bitInfoHead;//////////////int up=cut.up;int down=cut.down;int left=cut.left;int right=cut.right;LONG widthCut=LONG(right-left+1);LONG heightCut=LONG(up-down+1);int no=cut.no;char* charNo;if(no<10){charNo=new char[3];charNo[0]='_';charNo[1]=(char)(48+no);charNo[2]='\0';}else if(no<100){charNo=new char[4];charNo[0]='_';charNo[1]=(char)(48+no-no%10);charNo[2]=(char)(48+no%10);charNo[3]='\0';}else{printf("数字大,不考虑!");return;}//构造生成图片的文件名char* strnFile= new char[100];for(int i = 0; strFile[i] != '\0'; i++){if(i==99){printf("Name of File too long!");return;}strnFile[i]=strFile[i];if(strFile[i] == '.'){strnFile[i] = '\0';break;}}strcat(charNo,".bmp");strcat(strnFile,charNo);pf = fopen(strnFile,"wb");//打开文件//文件头信息设定WORD fileType=0x4D42;fwrite(&fileType,1,sizeof(WORD),pf); //===========================RMakeBmpInforHead(bitInfoHead,widthCut,heightCut);MakeBmpHead(bitHead,bitInfoHead.biSizeImage);fwrite(&bitHead,1,sizeof(tagBITMAPFILEHEADER),pf); //====================Rfwrite(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pf);//=======================R//调色板信息设定long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount))); // Mix color Plant Number;tagRGBQUAD *pRgb = (tagRGBQUAD *)malloc(nPlantNum*sizeof(tagRGBQUAD));memset(pRgb,0,nPlantNum*sizeof(tagRGBQUAD)); //都设置为0MakeColorPlant(pRgb,nPlantNum);fwrite(pRgb,4,nPlantNum,pf); //===================R//写入像素信息BYTE *pColorData = (BYTE *)malloc(bitInfoHead.biSizeImage);memset(pColorData,0,bitInfoHead.biSizeImage*sizeof(BYTE));int l_width = WIDTHBYTES(widthCut); //4字节整倍化for(int i = down; i < up+1; i++ )for(int j = left; j < right+1; j++ ){int k = (i-down)*l_width + (j-left)/8; //k:取得该像素颜色数据在实际数据数组中的序号int index = i*widthBmp+j; //index:取得该像素在实际像素信息数组中的序号BYTE dataAns = (dataOfBmp[index].rgbBlue+dataOfBmp[index].rgbGreen+dataOfBmp[index].rgbRed)/3/128;//25 5/128或0/128switch((j-left)%8){case 0:pColorData[k] |= (dataAns<<7);break;case 1:pColorData[k] |= (dataAns<<6);break;case 2:pColorData[k] |= (dataAns<<5);break;case 3:pColorData[k] |= (dataAns<<4);break;case 4:pColorData[k] |= (dataAns<<3);break;case 5:pColorData[k] |= (dataAns<<2);break;case 6:pColorData[k] |= (dataAns<<1);break;case 7:pColorData[k] |= dataAns;break;}}fwrite(pColorData,1,bitInfoHead.biSizeImage,pf);//====================R printf("Build sucessful!\n");if(fclose(pf)==0){printf("closed!!!\n");}free(pColorData);free(pRgb);return ;}/////////////////////////////////////////////////////***处理BMP文件//参数说明:<车牌bmp文件名,切割字符数>void DealFile(char* strFile,int numOfCut){//puts(strFile);if(numOfCut<1)return;//防止输入负数或者0,因为无意义;BITMAPFILEHEADER bitHead;BITMAPINFOHEADER bitInfoHead;FILE* pfile;pfile = fopen(strFile,"rb"); //打开文件if(pfile!=NULL){printf("file bkwood.bmp open success.\n");//读取位图文件头信息//////////////////WORD fileType;fread(&fileType,1,sizeof(WORD),pfile);if(fileType != 0x4D42){printf("file is not .bmp file!");return ;}fread(&bitHead,1,sizeof(tagBITMAPFILEHEADER),pfile);////////////////////////////////////////读取位图信息头信息///////////////////fread(&bitInfoHead,1,sizeof(tagBITMAPINFOHEADER),pfile);//////////////////////////////////////}else{printf("file open fail!\n");return ;}tagRGBQUAD *pRgb ;if(bitInfoHead.biBitCount != 8){printf("非256色BMP位图!\n"); //输出像素信息return;}//读取调色盘结信息long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount))); // Mix color Plant Number;pRgb=(tagRGBQUAD *)malloc(nPlantNum*sizeof(tagRGBQUAD));memset(pRgb,0,nPlantNum*sizeof(tagRGBQUAD));fread(pRgb,4,nPlantNum,pfile);printf("Color Plate Number: %d\n",nPlantNum);printf("\n");int width = bitInfoHead.biWidth;int height = bitInfoHead.biHeight;//分配内存空间把源图存入内存int l_width = WIDTHBYTES(width* bitInfoHead.biBitCount); //计算位图的实际宽度并确保它为4的倍数BYTE *pColorData=(BYTE *)malloc(height*l_width);memset(pColorData,0,height*l_width);long nData = height*l_width;//把位图数据信息读到数组里fread(pColorData,1,nData,pfile);//将位图数据转化为RGB数据tagRGBQUAD* dataOfBmp;dataOfBmp = (tagRGBQUAD *)malloc(width*height*sizeof(tagRGBQUAD));//用于保存各像素对应的RGB数据memset(dataOfBmp,0,width*height*sizeof(tagRGBQUAD)); //将内存空间dataOfBmp的前0个字节值设置为width*height*sizeof(tagRGBARAD);////处理8位图////////////////////////////int k,index=0;for(int i=0;i<height;i++)for(int j=0;j<width;j++){BYTE mixIndex= 0;k = i*l_width + j;mixIndex = pColorData[k];dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;index++;}////////////////////////////////////////printf("%d个像素数据信息:\n",width*height); //输出像素信息///读完了信息,马上关闭,与fopen对应fclose(pfile);//int projectionOnX[500];int *projectionOnX = new int[width]; //用来存储投影量的整型数组int *projectionOnY = new int[height]; //用来存储投影量的整型数组for(int i=0;i<width;++i){projectionOnX[i]=0;}for(int i=0;i<height;++i){projectionOnY[i]=0;}//////////////////////////////////粗略显示图片for(int i=height-1;i>=0;--i){if(i%6==0)printf("\n");for(int j=0;j<width;++j){if(i%6==0 && j%6==0){int k = i*width + j;//int num=dataOfBmp[k].rgbRed;showRgbQuan(&dataOfBmp[k]);//if(dataOfBmp[k].rgbRed>0) projectionOnX[j]+=1;}}printf("\n\n<自上而下横向扫描>:\n"); printf("<水平方向投影分布>\n\n"); //水平X轴上投影投影值记录/////////////////////////////////for(int i=height-1;i>=0;--i){//if(i%6==0)printf("\n");for(int j=0;j<width;++j){int k = i*width + j;//int num=dataOfBmp[k].rgbRed;if(dataOfBmp[k].rgbRed>0){projectionOnX[j]+=1;projectionOnY[i]+=1;}//end if}//end for}//end for////////////////////////////////for(int i=height-1;i>=0;--i){//if(i%6==0)printf("\n");for(int j=0;j<width;++j){int k = i*width + j;//int num=dataOfBmp[k].rgbRed;if(dataOfBmp[k].rgbRed>0){if(i==(height-1) || i==0){if(projectionOnX[j]>(height*0.8)){ projectionOnX[j]=0;}}if(j==(width-1) || j==0){if(projectionOnY[i]>(width*0.8)){projectionOnY[i]=0;}}}//end if}//end for}//end for//求平均投影值//获得一个01分布数组double rateChar=0.75;int minNum=int((1-rateChar)*width); int *min=new int[minNum];for (int m=0;m<minNum;++m){min[m]=height;}int averageMin=0;int maxNum=int(rateChar*width);int *max=new int[maxNum];for (int m=0;m<maxNum;++m){max[m]=0;}int maxAll=0;int averageMax=0;//for(int i=0;i<width;++i)projectionOnX[i]=averageMax;for(int m=0;m<width;++m){for (int i=0; i<minNum-1; ++i){// printf("[%d]",s);if(projectionOnX[m] < min[i]) //判断插入的条件{for(int j=minNum-2; j>=i; --j){ //插入地址的元素依次向后移动一位,min[j+1]=min[j];}min[i]=projectionOnX[m];break; //已经判断到插入位置并移位,不必再判断}}for (int i=0; i<maxNum-1; ++i){if(projectionOnX[m] > max[i]) //判断插入的条件{for(int j=maxNum-2; j>=i; --j){ //插入地址的元素依次向后移动一位,max[j+1]=max[j];}max[i]=projectionOnX[m];break; //已经判断到插入位置并移位,不必再判断}}}for(int m=0;m<minNum;++m){minAll+=min[m];}averageMin=int(maxAll/maxNum);for(int m=0;m<maxNum;++m){maxAll+=max[m];}averageMax=int(maxAll/maxNum);printf("[maxNum=%d][minNum=%d][maxAll=%d][minAll=%d][maxAverage=%d]-[averageMin= %d]",maxNum,minNum,maxAll,minAll,averageMax,averageMin);//for(int c=averageMin;c<averageMax;++c){///////////////////////////////////////////~~90%for(int c=min[0];c<max[0];++c){//min to max//100%int *arrOneZero=new int[width];for(int m=0;m<width;++m){arrOneZero[m]=(projectionOnX[m]>c)?1:0;}//遍历projectionOnX[],寻找空白峰值作为切割位置int cutNum=20; //切割位置的数量CUT *cutData=new CUT[cutNum];int mark=0; //指示切割位置记录下标int countOne=0; //投影值为0的列数int preNum=arrOneZero[0]; //用以记录上一列的投影值int *cutPos=new int[cutNum];for(int i=0;i<cutNum;i++)cutPos[i]=-1; //如果坐标为-1,可以认为不在数组中for(int m=0;m<width;++m){int newNum=arrOneZero[m];if(newNum==preNum){if(newNum==1){countOne+=1;}}else{if(countOne>3) //width/10)//黑色部分,也就是字体投影应该大于车牌宽度的10%{//切割记录cutData[mark].left=m-countOne-1;cutData[mark].right=m;mark+=1;//进行插入,并保持排序,以下是计算优先程度,粗略的情况下可有可无for (int i=0; i<cutNum-1; ++i){int index=cutPos[i];int num=projectionOnX[m];// printf("[%d]",lastMid);if(index=-1 || num <= projectionOnX[index] ) //判断插入的条件,=-1可以认为极小,可以插入{for(int j=cutNum-2; j>=i; --j){ //插入地址的元素依次向后移动一位,cutPos[j+1]=cutPos[j];}cutPos[i]=m;break; //已经判断到插入位置并移位,不必再判断}}//end forfor (int i=0; i<cutNum-1; ++i){int index=cutPos[i];if(index==-1)break;int num=projectionOnX[m-countOne-1];// printf("[%d]",s);if(index==-1 || num <= projectionOnX[index] )//判断插入的条件{for(int j=cutNum-2; j>=i; --j){ //插入地址的元素依次向后移动一位,cutPos[j+1]=cutPos[j];}cutPos[i]=m-countOne-1;break;//已经判断到插入位置并移位,不必再判断}}}//end ifcountOne=1;}//end elsepreNum=newNum; //用完了当前投影,注意保存为下一投影值 }//end forif(mark<numOfCut){continue;}////////////////////////////////////////显示投影波形图和切割位置for(int m=0;m<width;++m){//if(projectionOnX[m]>height*0.2){for(int n=0;n<projectionOnX[m];n+=2){printf("#");}printf("[第%d列]",m);int cut=0;for(int k=0;k<cutNum;++k){if(m==cutPos[k]){cut+=1;printf("<----可能切割[%d]-%d",k,arrOneZero[m]);if(cut==2)break;}}printf("\n");}/////////////////////////////////////printf("\n\n<自左向右竖向扫描>:\n");printf("<垂直方向投影分布>\n\n");int allOnY=0;int averageY=0;int *arrY=new int[height];for(int m=0;m<height;++m){arrY[m]=0;allOnY+=projectionOnY[m];}averageY=allOnY/height;int cutY[2]={0,height-1};int countY=0;for(int m=0;m<height;++m){arrY[m]=projectionOnY[m]>(averageY*3/4)?1:0; }//for(int i=0;i<height;++i)projectionOnY[i]=0;int preY=arrY[0];for(int m=0;m<height;++m){int newY=arrY[m];if(newY==preY){if(arrY[m]==1){countY+=1;}}else{if(countY>int(height*0.3)){cutY[0]=m-countY-1;////////////BUGER2010.8 cutY[1]=m;/////////////////////break;}countY=1;}preY=newY;}////////////显示for(int m=0;m<height;++m){for(int n=0;n<projectionOnY[m];n+=2){printf("#");}printf("[第%d行]",m);int cut=0;for(int k=0;k<2;++k){if(m==cutY[k]){cut+=1;printf("<----可能切割[%d]",k);if(cut==2)break;}}printf("\n");}///////////////////////////////////////////int unposible=0;int widthAverage=(cutData[numOfCut-1].right-cutData[0].left)/numOfCut;for(int m=0;m<mark;++m){cutData[m].no=m;cutData[m].up=cutY[1];cutData[m].down=cutY[0];int w=cutData[m].right-cutData[m].left;if(w>widthAverage){ //有人太穷,有人太富unposible+=(numOfCut-1);if(m==0){cutData[m].left=cutData[m].right-int(widthAverage*3/4);}if(m==(numOfCut-1)){cutData[m].right=cutData[m].left+int(widthAverage*3/4);}//break;}if(w<(widthAverage/3)){unposible+=1;}}if(unposible>numOfCut){continue;}printf("\n\n\n\n<切割位置显示:>\n\n");for(int m=0;m<mark;++m){MakeBmpHeader(strFile,dataOfBmp,width,cutData[m]);printf("切割[%d]--< 上%d,下%d,左%d,右%d >\n",m,cutData[m].up,cutData[m].down,cutData[m].left,cutData[m].right);}/////////printf("\n\n\n\n");/////break;}//////////////////////////////end for form min to maxfclose(pfile);if(bitInfoHead.biBitCount<24)free(pRgb);free(dataOfBmp);free(pColorData);printf("New File...\n");return ;}//***主应用程序int _tmain(int argc, _TCHAR* argv[]){char strfile[50];while(true){printf("Please input the .bmp file name:\n"); scanf("%s",strfile);DealFile(strfile,7);}return 0;}。

相关文档
最新文档