[Python] 發票數字圖片切割

一張張的發票怎麼進行切割呢?

為了進行數字辨識,我們要先切割發票數字,變成每個數字的圖檔,



我們用iphone拍下一張張的發票



















再來要找到數字的範圍後切割數字



1. Import需要的套件


import os
import numpy as np
import matplotlib.pyplot as plt
import cv2
import PIL
from PIL import Image



設定參數
os.chdir('D:\\2019\\numbers\\invoice\\train')


# mypic是指你要裁切第幾張照片,因為怕出錯所以我是一張一張切割,之後可以寫成迴圈
mypic=1
rawimg0 = cv2.imread("origin\\%s.jpg"%(mypic)) #原始照片


# 對照片進行定位後裁切
cropped = rawimg0 [610:1080,250:1465]
# 查看裁切後的照片
#plt.imshow(cropped)









裁切的部分如果不知道照片中數字的定位為何,
可以開啟小畫家把左上右下的像素紀錄下來,然後代入進行裁切,另外測試後選取的範圍盡量不要太大,否則可能後面切割會有點困擾。

















2. 處理圖片(灰階、二值化)


# 圖片灰階
grayscaleimg = cv2.cvtColor(cropped,cv2.COLOR_BGR2GRAY)
#plt.imshow(grayscaleimg,cmap='gray')


# 圖片二值化
ret, binary = cv2.threshold(grayscaleimg, 110, 255, cv2.THRESH_BINARY) # 110這個數字可改
#plt.imshow(binary,cmap='Greys',interpolation='None')
rawimg = binary - binary[0,1] #有這欄 圖的最低就會變成0 圖會變成黑底白字
#plt.imshow(rawimg)













3. 裁切


這個部分會先分成裁切上下多餘的地方,再左到右一個數字一個數字裁切


# counting non-zero value by row , axis y
row_nz = []
for row in rawimg.tolist():
row_nz.append(len(row) - row.count(0))
#plt.plot(row_nz)


idx=np.array(row_nz)>(max(row_nz)/4) #截出上下的範圍
np.where(idx==1)[0][0],np.where(idx==1)[0][-1]
up_y=np.where(idx==1)[0][-1] #上界
down_y=np.where(idx==1)[0][0] #下界
rawimg1=rawimg[down_y:up_y,]
#plt.imshow(rawimg1)













紅線處就是數字的上下邊界,找出上下界之後就可以裁切掉多餘的部分








再來是由左至右把每個數字切割出來


# counting non-zero value by column, x axis
col_nz = []
for col in rawimg1.T.tolist():
    col_nz.append(len(col) - col.count(0))
#plt.plot(col_nz)

idy=np.not_equal(col_nz,0)
record_y=[] #如果有八個數字,裡面應該要有九個格子(一開始找出七個,前後插入變九個)
for i in range(0,(len(np.where(idy==1)[0])-1)):
    # 如果下一個數是0就略過,直到找到下一個數不是0的位置
    if(np.where(idy==1)[0][i+1]-np.where(idy==1)[0][i]==1):
        pass
    else:
        record_y.append(np.where(idy==1)[0][i])

# 插入第一個非0位置跟最後一個非0的位置
record_y.insert(0,np.where(idy==1)[0][0])
record_y.append(np.where(idy==1)[0][-1])














如果有數字的地方位點都會是1,每個直排加總的區間就會是該數字的位置,
所以要找出的就是他的前後位置,總共抓出中間七個數字(上圖紅色三角形處)
外加前後兩個數字(綠色箭頭處),這樣就可以把數字正確做區隔了。



由於圖片處理過程,灰階跟二值化後,數字跟數字中間可能會有雜訊,
這樣會讓找出來的位點可能會超過9個數字,
因此需要設定過濾雜訊的地方,
有些直排可能沒有數字但有雜訊需要被過濾。

類似以下圖片的狀況














所以我們需要做檢查的動作,檢查錯誤篩選的位置


# 檢查數字
rm_id=[]
if len(record_y)>9:
    for j in range(0,len(record_y)-1):
        temp=np.array(col_nz[record_y[j]:record_y[j+1]])
        #如果只是雜訊,就刪掉
        if sum(temp>(max(col_nz)/4))==0:
            rm_id.append(record_y[j+1])

for x in rm_id:
     record_y.remove(x)


最後!!把每張照片按照找出來的邊界裁切成個別數字輸出即可!!
也把最後的圖片格是調成28x28
大功告成!!


for i in range(0,len(record_y)-1):
    a=binary[down_y:up_y,record_y[i]:record_y[i+1]]
    a=cv2.resize(a, (28, 28), interpolation=cv2.INTER_CUBIC)
    img_name='%s-%s.png'%(mypic,i+1)
    cv2.imwrite(img_name,a)
    #plt.imshow(a)


裁切的部分完工!!