工具
系統(tǒng):Windows 10
Python版本:Version 3.6
OpenCV版本:Version 2.4.9
圖片來源:截取自ubuntu上的sudoku數(shù)獨(dú)游戲
具體實(shí)現(xiàn)
本文將用python語言,結(jié)合OpenCV視覺庫來解決來識別數(shù)獨(dú)游戲上的數(shù)字塞关。
上面的數(shù)獨(dú)可以用如下的矩陣表示:
[[0 0 0 7 0 0 4 1 0]
[0 0 3 0 2 0 0 0 6]
[1 0 7 4 0 0 5 2 3]
[4 0 1 6 0 0 0 8 0]
[0 2 9 0 7 0 6 3 0]
[0 7 0 0 0 4 2 0 1]
[7 5 2 0 0 6 3 0 9]
[3 0 0 0 4 0 1 0 0]
[0 1 4 0 0 3 0 0 0]]
要做的工作就是從圖像中識別出這個(gè)矩陣(空白空格用0表示)。具體的步驟如下:
1.讀取圖片驶睦,并獲取灰度圖
2.反色處理匿醒,使得數(shù)字的顏色為白色
3.裁剪圖形,裁去多余的邊界
4.分割圖形廉羔,將原圖分成9*9=81個(gè)更小的圖形
5.識別圖形,將識別的數(shù)字填入numArray
圖像識別部分的功能我用一個(gè)名字叫ImageProcessor的類來實(shí)現(xiàn)憋他,具體代碼如下展示。
from cv2 import *
from numpy import *
from thining import *
class ImageProcessor:
__originalImage=0 #原圖
__grayScaleImage=0 #灰度圖
__binaryImage=0 #二值圖
__invBinaryImage=0 #顏色反轉(zhuǎn)之后的二值圖
__theCutImage=0 #裁剪后的圖像
__tightSize=[] #裁剪后的圖像的尺寸竹挡,寬度和高度
__cutImage=0
__imageList=[] #81個(gè)小格子
numArray=full([9,9],0,dtype=uint8) #9*9的全0矩陣,用來放數(shù)獨(dú)識別的矩陣
__template=[] #模板
# 類初始化函數(shù)
def __init__(self,path):
self.__loadTemplate() #加載圖形模板
self.__originalImage=imread(path) #載入圖像
self.__grayScaleImage=cvtColor(self.__originalImage,COLOR_BGR2GRAY,) #獲得灰度圖像
self.__thresh() #閾值處理梯码,獲得二值圖像
self.__inverseColor() #顏色反轉(zhuǎn)處理
self.__getTightSize() #獲得緊尺寸
self.__getCutImage() #裁剪圖形
self.__splitBoards() #劃分格子,將裁剪后的圖形分成81個(gè)小圖形
self.__fill() #往空矩陣?yán)锩嫣顢?shù)字
self.__resultDisplay() #結(jié)果顯示
#閾值處理
def __thresh(self):
ret,self.__binaryImage=threshold(self.__grayScaleImage,20,255,THRESH_BINARY)
#獲取緊尺寸
def __getTightSize(self):
ox=-1
oy=-1
height=0
width=0
op_confirm=0
rows=self.__invBinaryImage.shape[0]
cols=self.__invBinaryImage.shape[1]
# the following loop help find x0 and y0
for i in range(0,rows-1):
for j in range(0,cols-1):
if op_confirm==0 and self.__invBinaryImage[i,j]:
op_confirm=1
ox = j
oy = i
if 1==op_confirm:
break
# the following loop help find width and height
ep_confirm=0
ex=-1
ey=-1
for i in range(rows-1,0,-1):
for j in range(cols-1,0,-1):
if ep_confirm==0 and self.__invBinaryImage[i,j]:
ep_confirm=1
ex=j
ey=i
if 1==ep_confirm:
break
width=ex-ox
height=ey-oy
# finally assign value to rect
if op_confirm==1 and ep_confirm==1:
self.__tightSize=(ox,oy,height,width)
else:
raise RuntimeError("fail to find tight size of a image")
def showValue(self):
print(self.__tightSize)
print(self.__invBinaryImage[0:3,0:3])
#
def showImage(self):
imshow("original",self.__originalImage)
imshow("gray",self.__grayScaleImage)
imshow("binary",self.__binaryImage)
imshow("invImage",self.__invBinaryImage)
imshow("cut", self.__theCutImage)
# print(self.__grayScaleImage[0:10,0:10])
# 顏色反轉(zhuǎn)
def __inverseColor(self):
tmp=full([self.__binaryImage.shape[0],self.__binaryImage.shape[1]],255,dtype=uint8)
self.__invBinaryImage=tmp-self.__binaryImage
#裁剪圖片
def __getCutImage(self):
self.__theCutImage=self.__invBinaryImage[self.__tightSize[1]:self.__tightSize[3],\
self.__tightSize[0]:self.__tightSize[2]]
#保存圖片
def saveImage(self):
imwrite('D:\original.jpg',self.__originalImage)
imwrite('D:\gray.tif', self.__grayScaleImage)
imwrite(r'D:\inv.tif',self.__invBinaryImage)
imwrite('D:\cut.tif',self.__theCutImage)
# 將圖像分成81份
def __splitBoards(self):
# height of each grid
gh=self.__theCutImage.shape[0]/9
# width of each grid
gw=self.__theCutImage.shape[1]/9
path="D:\\"
for i in range(9):
for j in range(9):
tmp=self.__theCutImage[int(i*gh):int((i+1)*gh),int(j*gw):\
int((j+1)*gw)]
self.__imageList.append(tmp)
# normalize those grids as 54pixels*pixels
self.__normalize()
# need to filter the boarder away
for k in range(81):
for i in range(54):
for j in range(54):
if 10< i<54-10 and 10< j<54-10:
self.__imageList[k][i,j]=self.__imageList[k][i,j]*1
else:
self.__imageList[k][i, j] = self.__imageList[k][i, j] * 0
# test
# save the imageList altered
for i in range(81):
imwrite(path+str(i)+'.tif',self.__imageList[i])
# print("filling process done")
def __normalize(self):
for i in range(len(self.__imageList)):
tmp=cv2.resize(self.__imageList[i],dsize=(54,54))
ret,tmp=threshold(tmp,50,255,THRESH_BINARY)
self.__imageList[i]=tmp
# this function is used to recognize number in the original image
# and fill the __numArray
def __fill(self):
rowsOfNumArray=9
colsOfNumArray=9
for i in range(rowsOfNumArray):
for j in range(colsOfNumArray):
self.numArray[i,j]=self.__recognize(self.__imageList[i*9+j])
def __recognize(self,img):
dst=0
count=0
count0=0
whichOne=0
rows=img.shape[0]
cols=img.shape[1]
#細(xì)化一番
# img=Xihua(img)
for i in range(len(self.__template)):
for x in range(1,rows):
for y in range(1,cols):
count0 = count0 + (int(self.__template[i][x, y]) - int(img[x, y])) * \
(int(self.__template[i][x, y]) - int(img[x, y]))
if i==0:
count=count0
if count0<count:
count=count0
whichOne=i
count0=0
if whichOne==0:
return 0
elif whichOne==1:
return 1
elif whichOne==2:
return 2
elif whichOne==3:
return 3
elif whichOne==4:
return 4
elif whichOne==5:
return 5
elif whichOne==6:
return 6
elif whichOne==7:
return 7
elif whichOne==8:
return 8
elif whichOne==9:
return 9
def __loadTemplate(self):
self.__template.append(imread('TEMPLATE\\_0.tif', 0))
self.__template.append(imread('TEMPLATE\\_1.tif', 0))
self.__template.append(imread('TEMPLATE\\_2.tif', 0))
self.__template.append(imread('TEMPLATE\\_3.tif', 0))
self.__template.append(imread('TEMPLATE\\_4.tif', 0))
self.__template.append(imread('TEMPLATE\\_5.tif', 0))
self.__template.append(imread('TEMPLATE\\_6.tif', 0))
self.__template.append(imread('TEMPLATE\\_7.tif', 0))
self.__template.append(imread('TEMPLATE\\_8.tif', 0))
self.__template.append(imread('TEMPLATE\\_9.tif', 0))
def __resultDisplay(self):
print("識別結(jié)果:")
print(self.numArray)
print('\n')
上面代碼里用到了一個(gè)函數(shù)叫Xihua的函數(shù)框往,其功能是最圖形進(jìn)行細(xì)化,其來自于一個(gè)叫thining的py文件,下面貼出它的代碼:
import cv2
from numpy import *
array=[0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\
1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,\
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\
1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\
1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,0,\
1,1,0,0,1,1,1,0,1,1,0,0,1,0,0,0]
def VThin(image):
h = image.shape[0]
w = image.shape[1]
NEXT = 1
for i in range(h):
for j in range(w):
if NEXT == 0:
NEXT = 1
else:
M = image[i,j-1]+image[i,j]+image[i,j+1] if 0<j<w-1 else 1
if image[i,j] == 0 and M != 0:
a = [0]*9
for k in range(3):
for l in range(3):
if -1<(i-1+k)<h and -1<(j-1+l)<w and image[i-1+k,j-1+l]==255:
a[k*3+l] = 1
sumN = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128
image[i,j] = array[sumN]*255
if array[sumN] == 1:
NEXT = 0
return image
def HThin(image):
h = image.shape[0]
w = image.shape[1]
NEXT = 1
for j in range(w):
for i in range(h):
if NEXT == 0:
NEXT = 1
else:
M = image[i-1,j]+image[i,j]+image[i+1,j] if 0<i<h-1 else 1
if image[i,j] == 0 and M != 0:
a = [0]*9
for k in range(3):
for l in range(3):
if -1<(i-1+k)<h and -1<(j-1+l)<w and image[i-1+k,j-1+l]==255:
a[k*3+l] = 1
sumN = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128
image[i,j] = array[sumN]*255
if array[sumN] == 1:
NEXT = 0
return image
def Xihua(image,num=10):
iThin=image
iThin=full([image.shape[0], image.shape[0]], 255, uint8) - image
for i in range(num):
VThin(iThin)
HThin(iThin)
iThin=full([image.shape[0],image.shape[1]],255,uint8)-iThin
return iThin
def saveTemplate():
path0 = 'TEMPLATE\\'
path1 = 'TEMPLATE\\_'
for i in range(0, 2):
tmp = cv2.imread(path0 + str(i) + '.tif', 0)
tst = Xihua(tmp)
cv2.imwrite(path1 + str(i) + '.tif', tst)
#saveTemplate()
接下來...
建立一個(gè)ImageProcessor對象,看看識別結(jié)果
from ImageProcessor import *
from solution import *
i=ImageProcessor('original.jpg')
由于在ImageProcessor類的init函數(shù)里面我加入了__resultDisplay()函數(shù)闹司,即對識別結(jié)果進(jìn)行了顯示。運(yùn)行程序游桩,結(jié)果表明為:
[[0 0 0 7 0 0 4 1 0]
[0 0 3 0 2 0 0 0 6]
[1 0 7 4 0 0 5 2 3]
[4 0 1 6 0 0 0 8 0]
[0 2 9 0 7 0 6 3 0]
[0 7 0 0 0 4 2 0 1]
[7 5 2 0 0 6 3 0 9]
[3 0 0 0 4 0 1 0 0]
[0 1 4 0 0 3 0 0 0]]
可以發(fā)現(xiàn)識別結(jié)果是正確的。
我發(fā)現(xiàn)用這樣的識別方法借卧,效率是比較低的,待優(yōu)化