前言
这几天一直在做一个车牌识别的小程序,除了python中numpy作为操作像素的工具,其他部分均为自己实现,也算是检验自己,并锻炼自己的编码能力了。
文章中的算法仅为实现,效率不一定好,希望有大佬可以指点。机器学习算法中只贴出了最主要的一部分,关于一些数学计算函数实现,由于太多就不贴了。
问题
想要实现一个车牌识别的程序,我们要处理的一个最最基本的问题就是要对车牌进行定位,定位出来车牌之后,还要对车牌上的字进行分割,然后再对这些字一个一个的识别。
为了识别这些字,我们需要自己训练模型,然而训练模型的过程中,又需要用到数据,这里提一下,如果真的为了提高准确率,还是自己去做一个数据集,因为当你使用别人的数据集的时候,你对切割出来的字的处理方式如果和别人不一样,就会导致识别准确率特别低。
所以总结一下,在整个项目中,需要解决以下问题:
- 对车牌进行定位
- 对车牌进行分割
- 收集数据,训练模型
- 写一个图形化界面,简化操作
再总结一下就是
- 图像处理
- 模式识别算法(机器学习)
解决问题:车牌定位
我们在对车牌定位的时候,一定要尽可能的利用先验知识,比如说,现在绝大部分车的车牌为蓝底白字,大部分车牌的比例约为4:1,且车牌是有七个字符组成。后面在解决问题的过程中,我们会用到上述先验知识,废话不多说,下面说一下怎么解决车牌定位问题。
第一步,我们肯定是要读取图片,为了体现手撕
二字,所以这一部分我们要自己写,这里之前的博客有记录过不过多描述,直接上代码,这里以BMP位图为例,点击这里详细了解。
def Parse(self,fileName):
f=open(fileName,'rb')
file_type=str(f.read(2),encoding='utf-8')
assert file_type=='BM',"文件类型错误"
file_size_byte = f.read(4) # 这个可以用来读取文件的大小 需要读取4个字节
f.seek(f.tell() + 4) # 跳过中间无用的四个字节
file_ofset_byte = f.read(4) # 读取位图数据的偏移量
f.seek(f.tell() + 4) # 跳过无用的两个字节
file_wide_byte = f.read(4) # 读取宽度字节
file_height_byte = f.read(4) # 读取高度字节
f.seek(f.tell() + 2) ## 跳过中间无用的两个字节
file_bitcount_byte = f.read(4) # 得到每个像素占位大小
#下面就是将读取的字节转换成指定的类型
self.f_size,=struct.unpack('l',file_size_byte)
self.f_ofset,=struct.unpack('l',file_ofset_byte)
self.f_width,=struct.unpack('l',file_wide_byte)
self.f_height,=struct.unpack('l',file_height_byte)
self.f_bitcount,=struct.unpack('i',file_bitcount_byte)
# 判断是否有颜色表
if self.f_ofset==1078:
self.__256Image__(fileName)#处理伪彩色图像
else:
self.__24BImage(fileName) #处理真彩色图像
def __256Image__(self,f_name):
'然后来读取颜色表'
f=open(f_name,'rb')
self.colorTab = np.array([],dtype=int)
f.seek(54) # 跳过文件信息头和位图信息头
for i in range(0, 256):
b = struct.unpack('B', f.read(1))[0]
g = struct.unpack('B', f.read(1))[0]
r = struct.unpack('B', f.read(1))[0]
alpha = struct.unpack('B', f.read(1))[0]
self.colorTab=np.append(self.colorTab,np.array([r,g,b,255]))
self.colorTab=self.colorTab.reshape(256,4)
'下面部分用来读取BMP位图数据区域,将数据存入numpy数组'
# 首先对文件指针进行偏移
f.seek(self.f_ofset)
# 因为图像是8位伪彩色图像,所以一个像素点占一个字节,即8位
img = np.empty(shape=[self.f_height, self.f_width, 4], dtype=int)
cout = 0
for y in range(0, self.f_height):
for x in range(0, self.f_width):
cout = cout + 1
index = struct.unpack('B', f.read(1))[0]
img[self.f_height - y - 1, x] = self.colorTab[index]
while cout % 4 != 0:
f.read(1)
cout = cout + 1
self.Img=img
def __24BImage(self,f_name):
f=open(f_name,'rb')
f.seek(self.f_ofset)
img=np.empty(shape=[self.f_height,self.f_width,3],dtype=int)
cout=0
for y in range(0,self.f_height):
for x in range(0,self.f_width):
BYTES=f.read(3)
x1,x2,x3=struct.unpack('BBB',BYTES)
cout=cout+3
img[self.f_height - y - 1, x]=np.array([x3,x2,x1])
while cout%4!=0:
cout=cout+1
f.read(1)
self.Img=img
当我们把一副图片读取出来,转换成一个个像素之后,剩下的工作就是对这些像素点进行操作了。
来看看我们读取后的图片长什么样子
第二步,将RGB图像转换成HSV图像。
这里之所以这么做,是因为我们利用大部分车牌为蓝底白字的特点,我们想通过蓝色
来找到车牌的大概位置。但是如果你想在RGB颜色空间中准确定位蓝色是很不容易的,但是在HSV颜色空间中就很容易了。如果想要了解HSV颜色空间,请点击这里。对于RGB转换为HSV,以及HSV转换为RGB也是有公式的,利用下面公式我们就可以很轻松的进行编码。
利用python实现上述转换很容易,代码如下:
'RGB转HSV'
def convertHSV(img):
HSV=np.empty(shape=img.shape)
art60 = np.pi / 3
for y in range(0,len(img)):
for x in range(0,len(img[y])):
rgb=img[y,x]
rgb=rgb/255
r=rgb[0]
g=rgb[1]
b=rgb[2]
c_m,c_n=0,255
for color in rgb:
if color>c_m:
c_m=color
if color<c_n:
c_n=color
eta=c_m-c_n
H,S,V=0,0,0
if eta==0:
H=0
elif c_m==r:
H=art60*((g-b)/eta)
elif c_m==g:
H=art60*((b-r)/eta+2)
elif c_m==b:
H=art60 * ((r - g)/eta+4)
if c_m==0:
S=0
else:
S=eta/c_m
V=c_m
HSV[y,x,0]=H
HSV[y, x, 1] = S
HSV[y, x, 2] = V
return HSV
'HSV转RGB'
def HSVtoRGB(img):
temp=np.empty(shape=img.shape,dtype=int)
for y in range(0,len(img)):
for x in range(0,len(img[y])):
H, S, V = img[y, x]
C=V*S
X=C*(1-np.abs((H/1.047198)%2-1))
m=V-C
r,g,b=0,0,0
if H>=0 and H<1.47198:
r,g,b=C,X,0
elif H>=1.47198 and H<2.094395:
r,g,b=X,C,0
elif H>=2.094395 and H<3.141593:
r,g,b=0,C,X
elif H>=3.141593 and H<4.18879:
r,g,b=0,X,C
elif H>=4.18879 and H<5.235988:
r,g,b=X,0,C
elif H>=5.235988 and H<6.283185:
r,g,b=C,0,X
R,G,B=(int((r+m)*255),int((g+m)*255),int((b+m)*255))
temp[y,x,0]=R
temp[y, x, 1]=G
temp[y, x, 2]=B
temp[temp>255]=255
return temp
第三步,我们把RGB图像转换为HSV图像目的就是为了通过颜色进行定位,所以我们需要在HSV颜色空间中对图像进行二值化,而二值化的标准蓝色置为白色,其他颜色都置为黑色,这在RGB空间中是非常难以实现的,然后在HSV空间中却轻而易举。
经过数次实验,我发现可以比较好筛选出蓝色的HSV的范围如下
3.14 < H < 4.53 3.14<H<4.53 3.14<H<4.53
0.4 < S < 0.9 0.4<S<0.9 0.4<S<0.9
0.4 < V < 0.9 0.4<V<0.9 0.4<V<