做实验的过程中往往需要记录数据,比如笔者有时需要测定上百个DNA样品的浓度,有时又需要去野外实验地记录田间数据,手写记录目前还是最可靠、最便捷、最有效的方法。但手写数据需要录入电脑进行数据分析,录入的过程往往相当繁琐,没有任何技术可言,那么如何让手写数据更快更高效地录入电脑,从而提高我们学习工作的效率呢?那么就让我们一起用python来解决这个问题吧。先看看效果:
图片预处理:
图片转成excel:
利用下面的python代码可以将每一张表格照片都转成了excel中的一个sheet,非常适合批量操作。不过手写数字识别难免会有一定的错误率,实验数据要求很高,因此不要偷懒,还是需要核对一遍的哈。下面我们介绍一下实现的思路和代码
1.实现思路
首先,我们面临的问题就是如何识别手写数字,难道我还要从机器学习开始一步步学习然后才能实现?CSDN上有很多介绍用TensorFlow框架进行手写识别的教程,感兴趣的同学可以深入研究。但此处我们直接调用科大讯飞人工智能开放平台的手写识别API来实现手写识别(
https://www.xfyun.cn/service/wordRecghttps://www.xfyun.cn/service/wordRecg),手写识别的问题就解决了 。然后,我们将识别的结果进行定位,再利用python写入excel表格即可。
2.实现代码
该代码会调用科大讯飞手写识别的webAPI,需要用户名和密码,可以自行前往科大讯飞官网购买相应服务。如何想直接使用笔者打包好的代码,可参考文章配套的付费资源链接哈(https://download.csdn.net/download/weixin_43367441/79581415https://download.csdn.net/download/weixin_43367441/79581415),下载相应的模板进行实验记录,并用笔者提供的程序批量录入数据即可。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import base64
import hashlib
import time
import requests
import json
import xlsxwriter
from PIL import Image
import os
import sys
"""
手写文字识别WebAPI接口调用示例接口文档(必看):https://doc.xfyun.cn/rest_api/%E6%89%8B%E5%86%99%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB.html
图片属性:jpg/png/bmp,最短边至少15px,最长边最大4096px,编码后大小不超过4M,识别文字语种:中英文
webapi OCR服务参考帖子(必看):http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=39111&highlight=OCR
(Very Important)创建完webapi应用添加服务之后一定要设置ip白名单,找到控制台--我的应用--设置ip白名单,如何设置参考:http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=41891
错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
@author iflytek
"""
# OCR手写文字识别接口地址
URL = "http://webapi.xfyun.cn/v1/service/v1/ocr/handwriting"
# 应用APPID(必须为webapi类型应用,并开通手写文字识别服务,参考帖子如何创建一个webapi应用:http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=36481)
APPID = "**yourappid**"
# 接口密钥(webapi类型应用开通手写文字识别后,控制台--我的应用---手写文字识别---相应服务的apikey)
API_KEY = "**yourappkey**"
# 语种设置
language = "cn|en"
# 是否返回文本位置信息
location = "true"
# 图片上传接口地址
picFilePath = sys.argv[1]
#picFilePath = "C:/Users/Tianlong Hu/Desktop/test"
def getFiles(picFilePath):
f = []
for filepath,dirnames,filenames in os.walk(picFilePath):
for filename in filenames:
f = f+[os.path.join(filepath,filename)]
return f
def getHeader():
curTime = str(int(time.time()))
param = "{\"language\":\""+language+"\",\"location\":\""+location+"\"}"
paramBase64 = base64.b64encode(param.encode('utf-8'))
m2 = hashlib.md5()
str1 = API_KEY + curTime + str(paramBase64, 'utf-8')
m2.update(str1.encode('utf-8'))
checkSum = m2.hexdigest()
# 组装http请求头
header = {
'X-CurTime': curTime,
'X-Param': paramBase64,
'X-Appid': APPID,
'X-CheckSum': checkSum,
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
}
return header
def getBody(filepath):
with open(filepath, 'rb') as f:
imgfile = f.read()
data = {'image': str(base64.b64encode(imgfile), 'utf-8')}
return data
#def getImage(picFilePath):
#img_data = cv2.imread(picFilePath, 1)
#height,width = img_data.shape[:2]
#size = (int(height*4050/height), int(width*4050/height))
#img_data = cv2.resize(img_data,size)
#return img_data
def getContent(img_data):
r = requests.post(URL, headers=getHeader(), data=getBody(picFilePath))
#print(r.content)
result_json = json.loads(r.content)
text = [];xloc = [];yloc = [];wheight = []
for i in range(0,len(result_json["data"]["block"][0]["line"])):
text = text+[result_json["data"]["block"][0]["line"][i]["word"]]
xloc = xloc+[(result_json["data"]["block"][0]["line"][i]["location"]["top_left"]["x"]
+ result_json["data"]["block"][0]["line"][i]["location"]["right_bottom"]["x"])/2]
yloc = yloc+[(result_json["data"]["block"][0]["line"][i]["location"]["top_left"]["y"]
+ result_json["data"]["block"][0]["line"][i]["location"]["right_bottom"]["y"])/2]
wheight = wheight + [result_json["data"]["block"][0]["line"][i]["location"]["right_bottom"]["y"]
- result_json["data"]["block"][0]["line"][i]["location"]["top_left"]["y"]]
return text,yloc,wheight
def getTable(picFilePath,wheight):
#二值化
image_data = cv2.imread(picFilePath, 1)
gray = cv2.cvtColor(image_data, cv2.COLOR_BGR2GRAY)
binary = cv2.adaptiveThreshold(~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 35, -5)
#ret,binary = cv2.threshold(~gray, 127, 255, cv2.THRESH_BINARY)
#cv2.imshow("cell", binary)
#cv2.waitKey(0)
rows,cols=binary.shape
scale = 40
#识别横线
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(cols//scale,1))
eroded = cv2.erode(binary,kernel,iterations = 1)
#cv2.imshow("Eroded Image",eroded)
dilatedcol = cv2.dilate(eroded,kernel,iterations = 1)
#cv2.imshow("Dilated Image",dilatedcol)
#cv2.waitKey(0)
#识别竖线
scale = 30
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1,rows//scale))
eroded = cv2.erode(binary,kernel,iterations = 1)
dilatedrow = cv2.dilate(eroded,kernel,iterations = 1)
#cv2.imshow("Dilated Image",dilatedrow)
#cv2.waitKey(0)
#标识交点
bitwiseAnd = cv2.bitwise_and(dilatedcol,dilatedrow)
#cv2.imshow("bitwiseAnd Image",bitwiseAnd)
#cv2.waitKey(0)
#cv2.imwrite('/home/hutianlong/Desktop/test2.png',bitwiseAnd)
#标识表格
#merge = cv2.add(dilatedcol,dilatedrow)
#cv2.imshow("add Image",merge)
#cv2.waitKey(0)
#识别黑白图中的白色点
ys,xs = np.where(bitwiseAnd>0)
mylisty=[]
mylistx=[]
#通过排序,获取跳变的x和y的值,说明是交点,否则交点会有好多像素值,我只取最后一点
myxs=np.sort(xs)
for i in range(len(myxs)-1):
if(myxs[i+1]-myxs[i]>150):
mylistx.append(myxs[i])
mylistx.append(myxs[i])
# print(mylistx)
# print(len(mylistx))
myys=np.sort(ys)
#print(np.sort(ys))
for i in range(len(myys)-1):
if(myys[i+1]-myys[i]>min(wheight)/2):
mylisty.append(myys[i])
mylisty.append(myys[i])
# print(mylisty)
# print(len(mylisty))
return mylistx,mylisty
def outputTable(text,yloc,mylisty):
row_id = []
for i in range(len(text)):
row_id = row_id + [sum((yloc[i] - np.array(mylisty))>0)]
return row_id
# 建立文件
workbook = xlsxwriter.Workbook(sys.argv[2])
#workbook = xlsxwriter.Workbook("C:/Users/Tianlong Hu/Desktop/test.xlsx")
for picFilePath in getFiles(picFilePath):
#2nd step: output content and its location
text,yloc,wheight = getContent(picFilePath)
#3rd step: get table location
mylistx,mylisty = getTable(picFilePath,wheight)
#4th step: output table
row_id = outputTable(text,yloc,mylisty)
# 建立sheet, 可以work.add_worksheet('employee')来指定sheet名,但中文名会报UnicodeDecodeErro的错误
mytext = [[] for i in range(max(row_id))]
for i in range(max(row_id)):
for j in range(len(text)):
if row_id[j] == i+1:
mytext[i] = mytext[i] + text[j]
# 建立sheet, 可以work.add_worksheet('employee')来指定sheet名,但中文名会报UnicodeDecodeErro的错误
worksheet = workbook.add_worksheet()
for i in range(len(mytext)):
for j in range(len(mytext[i])):
worksheet.write(i,j,mytext[i][j]["content"])
workbook.close()
3. 使用方法:
——》打开cmd.exe,进入命令行模式。
——》切换到程序所在文件夹,使用如下命令运行程序:
handwriting_table_ocr_xunfeiai.exe test test.xlsx
——》test为手写数据照片所在文件夹,test.xlsx为输出Excel表格名称。
4. 注意事项:
——》程序在使用过程中需要连接科大讯飞提供的API,请保持网络连接畅通;
——》在拍照过程中确保表格四周均在视野范围内,推荐使用WPS文档扫描文件;
——》照片最大高度控制在4096像素内;
——》照片不要倾斜或旋转。
——》图片格式最好为JPG或PNG
也可直接使用笔者打包好的EXE文件运行哈,见如下链接:手写实验数据批量转Excel的python脚本及EXE文件-教育文档类资源-CSDN文库https://download.csdn.net/download/weixin_43367441/79581415
使用过程中有任何问题,欢迎评论或后台留言,我会尽快回复您。