模板匹配可以说是比较传统的数学方法去做图像识别,以往的模板匹配往往只是让原图像与模板图像做减运算,最后选出最小误差的那张,但在实际识别中,这一方法识别的效果并不是很有效,特别对于两张数字类型并不是很相似的时候,因此在此基础上改经这一普通的减运算,对原图像与模板图像进行距离计算,此处采用欧式距离,计算欧式距离的函数代码如下:
def getTempMinDis(i,j,temp):
minDis = float('inf')
h = temp.shape[0] #图像的高
w = temp.shape[1] #图像的宽
for i1 in range(h):
for i2 in range(w):
if(temp[i1][i2]==255):
continue
dis = math.pow(math.pow(i-i1,2)+math.pow(j-i2,2),0.5) #欧式距离
minDis = min(minDis,dis)
return minDis
def getRawMinDis(raw,temp):
dis = 0
h = raw.shape[0]
w = raw.shape[1]
for i in range(h):
for j in range(w):
if(raw[i][j] == 255):
continue
x = getTempMinDis(i,j,temp)
dis += x
return dis
对文件中的模板与原图像依次做欧式距离计算,选取最小的为识别出的数字。
铺垫这么多,看一下一个简单的数字电表的识别实例,电表原图像如下:
实例中,为了降低识别的繁琐,通过ROI的方式将核心的区域裁剪出来,并做了二值化操作,对切割出来的图像又通过垂直投影的方式切割出单一数字。效果如下:
step1:ROI裁剪
step2:垂直投影分割
step3:模板匹配识别
整套代码如下:
import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog
import os
import math
def get_vvList(list_data):
#取出list中像素存在的区间
vv_list=list()
v_list=list()
for index,i in enumerate(list_data):
# print(i)
if i>4:
v_list.append(index)
else:
if v_list:
vv_list.append(v_list)
#list的clear与[]有区别
v_list=[]
return vv_list
def cut(img):
rows, cols = img.shape
ver_list = [0] * cols
for j in range(cols):
for i in range(rows):
if img.item(i, j) == 0:
ver_list[j] = ver_list[j] + 1
'''
对ver_list中的元素进行筛选,可以去除一些噪点
'''
ver_arr = np.array(ver_list)
ver_arr[np.where(ver_arr < 1)] = 0
ver_list = ver_arr.tolist()
# 绘制垂直投影
img_white = np.ones(shape=(rows, cols), dtype=np.uint8) * 255
for j in range(cols):
pt1 = (j, rows - 1)
pt2 = (j, rows - 1 - ver_list[j])
cv2.line(img_white, pt1, pt2, (0,), 1)
cv2.imshow('vect', img_white)
cv2.waitKey(0)
# 切割单一字符
vv_list = get_vvList(ver_list)
j = 0
img_list = list()
for i in vv_list:
img_ver = img[:, i[0]:i[-1]]
# cv2.imshow('per char', img_ver)
cv2.imwrite(r"./rawChar/"+str(j)+".png",img_ver)
j+=1
img_list.append(img_ver)
return img_list
def getTempMinDis(i,j,temp):
minDis = float('inf')
h = temp.shape[0]
w = temp.shape[1]
for i1 in range(h):
for i2 in range(w):
if(temp[i1][i2]==255):
continue
dis = math.pow(math.pow(i-i1,2)+math.pow(j-i2,2),0.5)
minDis = min(minDis,dis)
return minDis
def getRawMinDis(raw,temp):
dis = 0
h = raw.shape[0]
w = raw.shape[1]
for i in range(h):
for j in range(w):
if(raw[i][j] == 255):
continue
x = getTempMinDis(i,j,temp)
dis += x
return dis
def chooseFile():
default_dir = r"D:\pycharm_project\electricity_project\fnn44p4mj8-1\RAW IMAGES\RAW IMAGES\1)Day Light"
return filedialog.askopenfilename(title=u'选择文件', initialdir=(os.path.expanduser(default_dir)))
root = tk.Tk()
root.withdraw()
file_path = chooseFile()
img = cv2.imread(file_path) # 获取BGR图像,第二个参数默认值为1,可不填
height = img.shape[0] //4 # 将tuple中的元素取出,赋值给height,width,channels
width = img.shape[1] //4
img = cv2.resize(img,(width,height),cv2.INTER_CUBIC)
rect = cv2.selectROI("roi", img, showCrosshair = False, fromCenter = False)
(x,y,w,h) = rect
imgcrop = img[int(y) : int(y+h), int(x):int(x+w)]
cv2.imshow("img_crop",imgcrop)
gray = cv2.cvtColor(imgcrop,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_OTSU+cv2.THRESH_BINARY)
cv2.imshow("thresh",thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
charcut = cut(thresh)
cv2.waitKey(0)
temp_path = "./template/"
file = os.listdir("./template/")
file.sort(key=lambda x: int(x[:-4]))
code = list()
# thresh = 50
for k in range(len(charcut)):
error = list()
raw_char = cv2.resize(charcut[k],(16,32),cv2.INTER_CUBIC)
# gray = cv2.cvtColor(raw_char,cv2.COLOR_BGR2GRAY)
# ret, raw_char = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
for c in range(len(file)):
file_path = temp_path + file[c]
nfile = cv2.imread(file_path)
temp_char = cv2.resize(nfile, (16, 32), cv2.INTER_CUBIC)
gray = cv2.cvtColor(temp_char, cv2.COLOR_BGR2GRAY)
ret, temp_char = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
dis = getRawMinDis(raw_char,temp_char)
error.append(dis)
mindis = min(error)
findc = error.index(mindis)
findc = str(findc)
code.append(findc)
l = len(code)
code[l-2] = '.'
res = ''.join(code)
print("识别结果为:",res)
欢迎入门者学习参考。