美文网首页
【图像处理】数独识图

【图像处理】数独识图

作者: llooRice | 来源:发表于2017-11-23 23:38 被阅读0次

    工具
    系统:Windows 10
    Python版本:Version 3.6
    OpenCV版本:Version 2.4.9
    图片来源:截取自ubuntu上的sudoku数独游戏

    具体实现

    本文将用python语言,结合OpenCV视觉库来解决来识别数独游戏上的数字。

    sudoku

    上面的数独可以用如下的矩阵表示:
    [[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]]

    要做的工作就是从图像中识别出这个矩阵(空白空格用0表示)。具体的步骤如下:
    1.读取图片,并获取灰度图
    2.反色处理,使得数字的颜色为白色
    3.裁剪图形,裁去多余的边界
    4.分割图形,将原图分成9*9=81个更小的图形
    5.识别图形,将识别的数字填入numArray

    图像识别部分的功能我用一个名字叫ImageProcessor的类来实现,具体代码如下展示。

    from cv2 import *
    from numpy import *
    from thining import *
    
    class ImageProcessor:
        __originalImage=0   #原图
        __grayScaleImage=0  #灰度图
        __binaryImage=0     #二值图
        __invBinaryImage=0  #颜色反转之后的二值图
        __theCutImage=0     #裁剪后的图像
        __tightSize=[]      #裁剪后的图像的尺寸,宽度和高度
        __cutImage=0
        __imageList=[]      #81个小格子
    
        numArray=full([9,9],0,dtype=uint8)  #9*9的全0矩阵,用来放数独识别的矩阵
    
        __template=[]       #模板
    
        # 类初始化函数
        def __init__(self,path):
            self.__loadTemplate()   #加载图形模板
            self.__originalImage=imread(path)   #载入图像
            self.__grayScaleImage=cvtColor(self.__originalImage,COLOR_BGR2GRAY,)    #获得灰度图像
            self.__thresh()     #阈值处理,获得二值图像
            self.__inverseColor()   #颜色反转处理
            self.__getTightSize()   #获得紧尺寸
            self.__getCutImage()    #裁剪图形
            self.__splitBoards()    #划分格子,将裁剪后的图形分成81个小图形
            self.__fill()           #往空矩阵里面填数字
            self.__resultDisplay()  #结果显示
    
        #阈值处理
        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])
    
        # 颜色反转
        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]
    
            #细化一番
            # 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("识别结果:")
            print(self.numArray)
            print('\n')
    

    上面代码里用到了一个函数叫Xihua的函数,其功能是最图形进行细化,其来自于一个叫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()
    

    接下来...
    建立一个ImageProcessor对象,看看识别结果

    from ImageProcessor import *
    from solution import *
    i=ImageProcessor('original.jpg')
    

    由于在ImageProcessor类的init函数里面我加入了__resultDisplay()函数,即对识别结果进行了显示。运行程序,结果表明为:
    [[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]]
    可以发现识别结果是正确的。

    我发现用这样的识别方法,效率是比较低的,待优化

    相关文章

      网友评论

          本文标题:【图像处理】数独识图

          本文链接:https://www.haomeiwen.com/subject/zvitbxtx.html