python opencv数字识别_基于模板匹配的手写数字识别(python+opencv)

webchat.jpg

智能计算课第一周的实验是做基于模板匹配的手写数字识别,光听见就很感兴趣,于是决定认真做做这个实验,本实验基于python3+opencv的python版本,所用到的知识都比较简单,基本上边学边做,技术含量很低。

实现思路:

大致就是这么个思路但其实纠错模块还没写出来,先搁置一下吧,准备先上手第二个实验。

下面按照流程图讲讲详细步骤吧

手写数字window.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80import tkinter

from PIL import Image

import recognition as rec

import numpy as np

import cv2

img = np.zeros((400,400),np.uint8)

app = tkinter.Tk()

app.resizable(0,0)

app.title('数字识别 v1.0')

app['width']=400

app['height']=400

yseno = tkinter.IntVar(value=0)

X = tkinter.IntVar(value=0)

Y = tkinter.IntVar(value=0)

foreColor = '#000000'

backColor = '#FFFFFF'

image = tkinter.PhotoImage()

canvas = tkinter.Canvas(app,bg='white',width=400,height=400)

canvas.create_image(400,400,image=image)

def (event):

yseno.set(1)

X.set(event.x)

Y.set(event.y)

canvas.bind('',onLeftButtonDown)

def onLeftButtonMove(event):

if yseno.get()==0:

return

canvas.create_line(X.get(),Y.get(),event.x,event.y,fill=foreColor,width=10)

X.set(event.x)

Y.set(event.y)

if (event.x-3>=0) and (event.x+3<=400) and (event.y-3>=0) and (event.y+3<=400):

img[event.y-3:event.y+3,event.x-3:event.x+3]=255

canvas.bind('',onLeftButtonMove)

def onLeftButtonUp(event):

# canvas.create_line(X.get(),Y.get(),event.x,event.y,fill=foreColor)

yseno.set(0)

canvas.bind('',onLeftButtonUp)

def clear():

global img

img = img * 0

for item in canvas.find_all():

canvas.delete(item)

# print(item)

buttonClear = tkinter.Button(app,text='清除',command=clear)

buttonClear.place(x=200,y=370,width=200,height=30)

def recognition():

#保存图片...得到img

global img

# img = img.T

num = rec.recognition(img)

print('识别出的数字为:',num)

# cv2.imshow('img',img)

# cv2.waitKey(0)

# cv2.destroyAllWindows

buttonRec = tkinter.Button(app,text='识别',command=recognition)

buttonRec.place(x=0,y=370,width=200,height=30)

canvas.pack(fill=tkinter.BOTH,expand=tkinter.YES)

app.mainloop()

照着图书馆借来的书,边看边敲的代码,所以比较简单,实现的功能也比较简单,运行结果如图:

window.png

图片标准化

标准化图片我这里分四步:像素取反,灰度化,去噪以及图片分割。

1.像素取反

1

2

3def negation(img):#图片取反

img = cv2.bitwise_not(img)

return img

其实就一句话,也就是用opencv自带的方法 cv2.bitwise_not方法就OK了

2.灰度化(+二值化)

1

2

3

4

5def graying(img):#二值化图像

img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh=cv2.threshold(img,250,255,cv2.THRESH_BINARY)

img = thresh

return thresh

同样使用opencv函数,得到的是黑底(0)白字(255)。

3.去噪

1

2

3

4

5def denoise(img):

kernel = np.ones((5,5),np.uint8)

img = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)

img = cv2.blur(img,(10,10))

return img

用到中值滤波和低通滤波,原意是为了去掉离群点,但经过老师点拨,发现效果其实并不好我去掉的只是数字周围的毛刺点,并不是离群点,如下图

noise.png

就没办法得到理想的效果,最新的想法是利用能量分布去除离群点(不是很懂,找时间研究研究),好像马氏距离也行?嗯,先放放···

4.图片分割

原图是这样的

0-1.bmp然后分割完(中间经过了上述各种处理)变成了这样的

m.bmp

代码如下:

1

2

3

4

5

6

7

8def splitPicture(img):

img = standard(img)

binary,contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

for i in range(len(contours)):

x,y,w,h = cv2.boundingRect(contours[i])

# print([x,y,w,h])

img = img[y:y+h,x:x+w]

return img

cv2.findContours函数是opencv的轮廓检测函数,其中返回值coutours是轮廓点集,再利用cv2.boundingRect函数,得出最小矩形边框,x,y,w,h分别是该矩形左上角坐标及矩形宽高,然后用numpy索引就可以 高效分割图片啦(之所以强调 高效,是因为最开始我用自己的双重for循环做出来的东西跑一趟要30多秒,而现在只要不到0.3秒,所以好好利用库函数总是没错的!)

与模板库对比

模板匹配,顾名思义就是与模板对比,以某种方式得出两张图片的相似程度,从而判断出手写数字是多少。我的第一版用的是欧氏距离匹配,简单来说,两点欧式距离

$d=sqrt{(x1-x2)^2+(y1-y2)^2}$

而对于图像来说,我们可以先算出它的特征值,然后求特征值之间的距离,就可以得到相似程度的距离值。

首先,求特征值。为了减少计算量,我把每张图片分成$5*5$即每张图分为25小块,每一小块的特征值用该区域白色(255)像素点个数除以该块总像素数代表,每张图片有25个特征值,对比两张图片就是依次比较它们的特征值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17def getEigenvalue(img):#传入图片,返回一个特征值列表

img = splitPicture(img)

h,w = img.shape

h0 = int(h/5)

w0 = int(w/5)

# print([h0,w0])

# count = 0

# imgList = []#25张小图片

ratioList = []#小图片白色像素特征值列表

for i in range(5):

for j in range(5):

# imgList.append(img[i+i*h0:i+(i+1)*h0,j+j*w0:j+(j+1)*w0])

ratioList.append(getRatio(img[i+i*h0:i+(i+1)*h0,j+j*w0:j+(j+1)*w0]))

# for k in range(len(imgList)):

# ratioList.append(getRatio(imgList[k]))

# print(ratioList)

return ratioList

然后,得到”距离“。

1

2

3

4

5

6

7

8def getDistance(img1,img2):

list1 = getEigenvalue(img1)

list2 = getEigenvalue(img2)

distance = 0

for i in range(25):

distance = distance + (list1[i]-list2[i])**2

return int(math.sqrt(distance))

#return int(distance)

到这里,思路已经很清晰了,将所要识别的数字图像依次和图片库中的图片对比求“距离”,取“距离值”最小的数字代表的值即为所要识别的数字的值。

剩下的就是依次对比的部分了

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43def getImgNameList():#获取图片库每张图片的路径及其所代表的数字

wolk_path = os.path.split(os.path.realpath(__file__))[0]

# print(os.listdir(wolk_path))

#rootList = os.listdir(wolk_path)

source_path = os.path.join(wolk_path,'source photo')

#print(source_path)

imgdir_path_list=[]

# img_path_list=[]

namemap={}

for i in range(10):

imgdir_path = os.path.join(source_path,str(i))#source下0,1...等目录

#print(imgdir_path)

imgdir_path_list.append(imgdir_path)

# print(os.listdir(imgdir_path_list[0]))

for j in range(10):

dir = len(os.listdir(imgdir_path_list[j]))

#print(os.listdir(imgdir_path_list[j]))

for k in range(dir):

img_path = os.path.join(imgdir_path_list[j],(os.listdir(imgdir_path_list[j]))[k])

# img_path_list.append(img_path)

#print(img_path)

namemap[img_path]=j

# return img_path_list,namemap

return namemap

def recognition(img1):#识别

# img1 = cv2.imread(img1)

# img_path_list,namemap = getImgNameList()

namemap = getImgNameList()

# print(namemap.keys()[0])

similarty = []

try:

# for i in range(len(img_path_list)):

# img2 = cv2.imread(img_path_list[i])

#print(img_path_list[i])

for key in namemap.keys():

# print(key)

img2 = cv2.imread(key)

# cv2.imshow('img',img2)

# cv2.waitKey(0)

# print(key)

similarty.append(getDistance(img1,img2))

# print(similarty[i],'..................')

最后,纠错模块就是人工指出数字是几,然后将图片保存到相应文件夹,因为图片的命名没啥思路,就先偷个懒先放着了,然后就是,想把识别结果也用窗口显示出来,也先搁着了。st=>start: 开始

e=>end: 结束

io1=>inputoutput: 手写数字

op1=>operation: 图片标准化

op2=>operation: 与模板库对比

cond=>condition: 人工鉴别

op3=>operation: 纠错并加入模板库

io2=>inputoutput: 识别正确

io4=>inputoutput: 无法识别

end=>end: 结束

st->io1->op1->op2->cond

cond(yes)->io2->end

cond(no,left)->op3->end{"scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值