AI ninja project [day 5] AI RPA系统--表单篇

再来是办公室表单的处理,
假设有些表单只有图像或是只有纸本,
想要汇入成Excel档案时,
我们就可以使用前面所使用的OCR程序来帮助进行转换。

首先,可能有人会问为甚麽不使用OPEN-CV来解析表格的框线,
然後使用每一格送入进行辨识?

表格不一定都有框线,使用pytessert会有辨识率不佳的情况,
如果有50格的表格,使用google client vision成本就大幅提升了50倍(每月前1000张免费,之後每1000张大约30元台币)。

https://ithelp.ithome.com.tw/upload/images/20210905/20122678kTFp6ivPmK.png

这是我预想中的表格样式,每一条线代表一个字串。

from google.cloud import vision
import io
import os 

credential_path = "cred.json"
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = credential_path

client = vision.ImageAnnotatorClient()

with io.open('table.png', 'rb') as image_file:
    content = image_file.read()

image = vision.Image(content=content)

response = client.text_detection(image=image)
texts = response.text_annotations
print('Texts:')

Xarray=[]
Yarray=[]
TextArray=[]


for text in texts[1:]:
    xy0=(text.bounding_poly.vertices[0].x, text.bounding_poly.vertices[0].y)
    xy1=(text.bounding_poly.vertices[1].x, text.bounding_poly.vertices[1].y)
    xy2=(text.bounding_poly.vertices[2].x, text.bounding_poly.vertices[2].y)
    xy3=(text.bounding_poly.vertices[3].x, text.bounding_poly.vertices[3].y)

    print(f"{xy0}{xy1}{xy2}{xy3} {text.description}")
    
    Yarray.append([text.bounding_poly.vertices[0].y,text.bounding_poly.vertices[2].y])
    Xarray.append([text.bounding_poly.vertices[0].x ,text.bounding_poly.vertices[1].x])
    TextArray.append(text.description)

处理表格,我就没有使用第0个的集合元素,
而是後面的元素文字及座标。

https://ithelp.ithome.com.tw/upload/images/20210905/20122678zztWed2bhO.png

每个文字取两个X及两个Y座标为特徵:

https://ithelp.ithome.com.tw/upload/images/20210905/20122678jIcIQlZKAE.png

https://ithelp.ithome.com.tw/upload/images/20210905/201226780yf0h4exYS.png

采用Kmeans演算法来将字串以Y座标分群,并以silhouette_score找出最将分群数
(假设资料表格列数不超过60,超过的话,可以自行调整60的数值 )

import numpy as np
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
from scipy.signal import find_peaks

Yarray_X=np.array(Yarray)
silhouette_avg = []

for i in range(2,60):
    try:
        kmeans_fit = KMeans(n_clusters = i,max_iter=500).fit(Yarray_X)
        silhouette_avg.append(silhouette_score(Yarray_X, kmeans_fit.labels_)) 
    except:
        pass
        
best_cluster_number,peak_heightsDict=find_peaks(silhouette_avg,height=0.7)

RowNumCategories=best_cluster_number[0]+2
#使用最佳分群数来重新分群
herekmeans = KMeans(n_clusters=RowNumCategories,max_iter=500).fit(Yarray_X) 

RowSerialCount=1
row_dict={}
RowactualArray=[]

#这里是将Y座标得到的分群转换成未来写入excel时的第几个row。
for ele in herekmeans.labels_:
    if ele not in row_dict:
        row_dict[ele]=RowSerialCount
        RowSerialCount+=1

    RowactualArray.append(row_dict[ele])

X座标,栏位上的转换:
(可自行调整栏位20的数值,但是如果超过26,後续转换到excel字母时,需要再自行调整)

X=np.array(Xarray)

TableColumn=20
silhouette_avg = []
for i in range(2,TableColumn):
    kmeans_fit = KMeans(n_clusters = i,max_iter=500).fit(X)
    silhouette_avg.append(silhouette_score(X, kmeans_fit.labels_))

category_count  =  silhouette_avg.index(max(silhouette_avg))+2

print("最佳column分群数::")
print(category_count)

kmeans = KMeans(n_clusters=category_count,max_iter=500).fit(X)   

文字X座标,一样使用最佳分群数来分群

cStarRay=[]
for center in kmeans.cluster_centers_:
    cStar=(center[0]+center[1])/2
    # print(center)
    # print(int(cStar))
    cStarRay.append(int(cStar))

ReSortStarRay=sorted(cStarRay)
Stardict={}
for star in cStarRay:
    StarIndex = cStarRay.index(star)
    ReStarIndex = ReSortStarRay.index(star)
    Stardict[StarIndex]=ReStarIndex

actualXArray=[]
for ele in kmeans.labels_:
    actualXArray.append(Stardict[ele])
    
Column_actualArray  = np.array(actualXArray)    

使用kmeans群心来重新排序X座标栏位

我们将文字内容、栏位分群结果、列数分群结果用zip打包

ToExcelZip=zip(TextArray,Column_actualArray,RowactualArray)

将文字写入EXcel,使用openpyxl套件:

安装

pip install openpyxl

使用

import openpyxl

wb=openpyxl.load_workbook("./template.xlsx")
ws=wb["工作表1"]

counts={}
for i in range(category_count):
    counts[chr(65+i)]=1


for i in enumerate(ToExcelZip):
    # print(i)
    column=chr(65+i[1][1])
    # print(column)
    ws_column=column+str(i[1][2])
    if ws[ws_column].value == None:
        ws[ws_column]=i[1][0]
    else:    
        ws[ws_column]=ws[ws_column].value+" "+i[1][0]
        
wb.save("test_output.xlsx")        

依照分群结果来写入特定Excel栏位。

这是从农委会找到的 主力农家平均每户所得按所得来源分 表格:
https://ithelp.ithome.com.tw/upload/images/20211001/20122678GpAZgGreTB.png

进行截图及执行程序看结果:
https://ithelp.ithome.com.tw/upload/images/20211001/20122678Zt0suUtODx.png


<<:  Varying - fragment shader 之资料

>>:  Day 5:口罩即时库存开放资料

用React刻自己的投资Dashboard Day20 - 首页功能切板

tags: 2021铁人赛 React 先从mobile版型的页面来看,可以分为几个部份,如下图: ...

[Day29]拖稿到最後的逆向工程

我很清楚逆向是我的硬伤,所以拖到最後才写了 Q"Q 虽然逆向工程与恶意软件分析息息相关,但...

[第五天]从0开始的UnityAR手机游戏开发-如何在Vuforia创建可辨识图片

第四天的小试身手解答:将Unity介面改为2By3,Project从Two Column Layou...

CMMC 2.0 之前世今生

美国国防部在 2020 年1月底公布新规范 「网路安全成熟度模型认证Cybersecurity Ma...

[DAY 13] 把Google SpreadSheet 当作题库资料库

接下来是如何储存题目、抓出题目、呈现题目 对一个老师来说,很常把资料都放在excel 中 所以可以选...