[Python] 發票數字圖片切割
一張張的發票怎麼進行切割呢?
為了進行數字辨識,我們要先切割發票數字,變成每個數字的圖檔,
我們用iphone拍下一張張的發票
再來要找到數字的範圍後切割數字
設定參數
裁切的部分如果不知道照片中數字的定位為何,
可以開啟小畫家把左上右下的像素紀錄下來,然後代入進行裁切,另外測試後選取的範圍盡量不要太大,否則可能後面切割會有點困擾。
紅線處就是數字的上下邊界,找出上下界之後就可以裁切掉多餘的部分
再來是由左至右把每個數字切割出來
如果有數字的地方位點都會是1,每個直排加總的區間就會是該數字的位置,
所以要找出的就是他的前後位置,總共抓出中間七個數字(上圖紅色三角形處)
外加前後兩個數字(綠色箭頭處),這樣就可以把數字正確做區隔了。
由於圖片處理過程,灰階跟二值化後,數字跟數字中間可能會有雜訊,
這樣會讓找出來的位點可能會超過9個數字,
因此需要設定過濾雜訊的地方,
有些直排可能沒有數字但有雜訊需要被過濾。
類似以下圖片的狀況
所以我們需要做檢查的動作,檢查錯誤篩選的位置
最後!!把每張照片按照找出來的邊界裁切成個別數字輸出即可!!
也把最後的圖片格是調成28x28。
大功告成!!
裁切的部分完工!!
為了進行數字辨識,我們要先切割發票數字,變成每個數字的圖檔,
我們用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)