华为手环会监测全天心率,大约一般是10分钟一个数据,最短是1分钟1个数据。如果屏幕亮着(最长可设成常亮20分钟),则会一直记录(1分钟1个数据)。
手环心率数据会自动同步到手机的华为健康APP,再自动同步到华为服务器。需要数据导出的话,需要华为网站申请,而且要1周后,非常麻烦,也无法及时得到数据。
华为健康APP,心率那边可以看每天的心率数据,想及时得到数据,最好的办法就是截图+OCR了。
比如截出的图片中的数据是:
图标 70 次/分钟 19:00 >
图标 101 次/分钟 20:00 >
看起来很简单,只需要把图片中的数字识别出来就行了。但千辛万苦,入了N多坑,才终于OCR成功,100%识别。
总结一下:
手机截出的长图,水平是540pix,最多截到2万多PIX垂直长度就不截了,需要分多个图来截。
手机截出的短图,可能水平PIX数会不一样,大于540,程序需要resize成一样的540。因为OCR训练字库是按540PIX的图来的,如果PIX不一样,OCR错误率就很高。
用百度云的“文字识别API”可以,必须采用“精确识别”,效果很好,错误率低,基本100%出来! 其它的如“数字识别”等不行,效果差! 但“精确识别”有长度限制,限制8192pix,长图需要分割,有点麻烦。 百度API有一些免费次数,基本够用。
其它的OCR软件就更不能看了,一是一般长度限制在1万pix,二是错误率高,三是漏内容,有很多内容被忽略了,或“未找到文字”,好悲伤。
因为有tesseract开源OCR软件和python支持,于是自己尝试进行OCR。
tesseract不同语言包,就是用各自的识别字库,比如默认就是英语,用英语的字库进行强行匹配出字符,指定中文就是用中文的字库进行强行匹配出字符。
psm=3,是全页进行分析切割,邻近的文字会当成一个分割大块,因为各个时间邻近,所以所有的时间就都在一起了,心率、时间就分开了,不在同一行,格式乱了。
psm=6,是类似按表格那样处理,心率、时间就能在同一行,需要用这种方式。
发现OCR真的太难了,图片分辨率不能太大也不能太小,不然都错误率高,只有适中的分辨率才能较好地识别,所以不能随便resize,但要找到合适的分辨率size太难了。如果采用训练字库,则训练的字符和待识别的字符要一样pix大,不然错误率高,所以也不能随便resize。
tesseract是先识别出图块,再把各个图块OCR出来一个字符。一个图块一定会OCR出一个字符来,无法忽略掉这个图块,即使图块和字符完全不符,它也会强行去匹配字符而不忽略,所以会OCR出来乱七八糟的字符!
识别过程有N种可能性,同一个图片中的一个汉字在可能会被识别成完整汉字、或者偏旁分开、或者和别的字符一起,所以有无数可能性组合,造成了OCR出来的字符乱七八糟。这也和你采用什么识别语言字库有关系。
试了N多方案,最后发现还是必须要把图片中所有的干扰字(中文、英文、符号等)全部去掉,只留下数字,并且采用训练字库,才可能100%识别,不然干扰实在太多了,一直有错误率。
即使图片中只有数字,采用tesseract自带的语言包字库,也有很多识别错的,所以必须要用jTessBoxEditor自己训练语言包字库,才能100%。 不难,就是把图片里的各个数字0~9截出来进行训练就好了。
所以OCR真的是一个高难度技术活!
然后,又碰到华为健康APP会更新,心率数据显示格式会变了!每行数据的高度没有变,数字的宽高没有变。但水平定位位置变了。程序得调整,还好训练的字库还能用。
当前PYTHON程序如下:
import cv2
import pytesseract
image = cv2.imread("1.jpg",cv2.IMREAD_GRAYSCALE)
if image is not None: #try捕捉不了cv2.imread错误,要用None来判断
height, width = image.shape
if width != 540 :
image = cv2.resize(image, ( 540, int(height/width*540) ) ,interpolation=cv2.INTER_LANCZOS4)
height, width = image.shape
cv2.rectangle(image, (0, 0), (width-1, 155), (255, 255, 255), -1) #清除上方
cv2.rectangle(image, (0, 0), (79, height-1), (255, 255, 255), -1) #清除左方
cv2.rectangle(image, (445, 0), (450, height-1), (255, 255, 255), -1) #清除冒号
cv2.rectangle(image, (480, 0), (width-1, height-1), (255, 255, 255), -1) #清除右方
#查找心率超过100的突出来的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(216, 222): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (144, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字 yy+23会超过总高,没有关系不会出错,不然有的文字比较靠底部,会清不到
#再查找小率小于100的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(202, 209): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (130, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字
cv2.imwrite("output1.png",image)
text = pytesseract.image_to_string(image, config='-l mynum --psm 6 --oem 3')
#print(text)
with open('output.txt', 'w') as file:
file.write(text)
print('1.jpg output.txt ok\n')
#################################################################################################################
image = cv2.imread("2.jpg",cv2.IMREAD_GRAYSCALE)
if image is not None: #try捕捉不了cv2.imread错误,要用None来判断
height, width = image.shape
if width != 540 :
image = cv2.resize(image, ( 540, int(height/width*540) ) ,interpolation=cv2.INTER_LANCZOS4)
height, width = image.shape
cv2.rectangle(image, (0, 0), (width-1, 155), (255, 255, 255), -1) #清除上方
cv2.rectangle(image, (0, 0), (79, height-1), (255, 255, 255), -1) #清除左方
cv2.rectangle(image, (445, 0), (450, height-1), (255, 255, 255), -1) #清除冒号
cv2.rectangle(image, (480, 0), (width-1, height-1), (255, 255, 255), -1) #清除右方
#查找心率超过100的突出来的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(216, 222): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (144, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字 yy+23会超过总高,没有关系不会出错,不然有的文字比较靠底部,会清不到
#再查找小率小于100的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(202, 209): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (130, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字
cv2.imwrite("output2.png",image)
text = pytesseract.image_to_string(image, config='-l mynum --psm 6 --oem 3')
#print(text)
with open('output.txt', 'a') as file:
file.write(text)
print('2.jpg output.txt ok\n')
#################################################################################################################
image = cv2.imread("3.jpg",cv2.IMREAD_GRAYSCALE)
if image is not None: #try捕捉不了cv2.imread错误,要用None来判断
height, width = image.shape
if width != 540 :
image = cv2.resize(image, ( 540, int(height/width*540) ) ,interpolation=cv2.INTER_LANCZOS4)
height, width = image.shape
cv2.rectangle(image, (0, 0), (width-1, 155), (255, 255, 255), -1) #清除上方
cv2.rectangle(image, (0, 0), (79, height-1), (255, 255, 255), -1) #清除左方
cv2.rectangle(image, (445, 0), (450, height-1), (255, 255, 255), -1) #清除冒号
cv2.rectangle(image, (480, 0), (width-1, height-1), (255, 255, 255), -1) #清除右方
#查找心率超过100的突出来的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(216, 222): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (144, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字 yy+23会超过总高,没有关系不会出错,不然有的文字比较靠底部,会清不到
#再查找小率小于100的“钟” #消除这行中文文字
for yy in range(12, height):
tempsum=0
for xx in range(202, 209): #7个像素,平均小于80,就找到了
tempsum=tempsum+image[ yy , xx ]
if tempsum/7 < 80 :
#print(xx,yy)
cv2.rectangle(image, (130, yy-12), (400, yy+23), (255, 255, 255), -1) #清除本行中文文字
cv2.imwrite("output3.png",image)
text = pytesseract.image_to_string(image, config='-l mynum --psm 6 --oem 3')
#print(text)
with open('output.txt', 'a') as file:
file.write(text)
print('3.jpg output.txt ok\n')