前言

天若ocr是我用过最满意的一个文字识别工具,它最核心的功能就是聚合了多个api识别接口。经过我的试验,我发现百度云的ocr是准确率最高的,而且它的免费额度也非常多。通用文字识别一天可免费调用50000次,高精度文字识别一天也有500次的免费额度。此外,还可以开工单申请开通表格文字识别,它也有50次/天的免费额度,个人使用绰绰有余。唯一的缺憾就是没有公式识别,只能寻找其他方案。试过腾讯云和有道云,二者的效果都不能让人满意,只有Mathpix能够应付我的需求,可惜也信用卡才能开通相应的接口。不过这都是题外话了,今天我就来教大家怎么通过python,来实现与天若ocr类似的功能。

思路

天若ocr的使用流程是:按下快捷键——开始截图——截图完成后上传至百度云进行识别——返回结果,那我们就按照这个流程来做。

监听键盘按键

在网上找了一下,能够实现这种功能的库有pyHook,PyWin32pyninput,一个个尝试下来,pyHook过于卡顿,PyWin32有点麻烦,学习成本高,所以最终我选择了pyninput

pyinput文档

它的功能也很简单:监听键盘或鼠标按键,控制键盘或鼠标按键。我们可以用它来监听用户按下截图快捷键win+shift+s,然后进行后续操作。理论上可行,但是操作起来就发现一个大问题:这个库没法监听组合按键!(可能可以,但是我实在找不到如何实现这样的效果)。冥思苦想了一阵,突然发现我思路跑偏了,为什么要监听截图快捷键?万一用户只是用来截个图发微信呢?应该监听一个特定的键(比如F7),如果用户按下这个键,那么代表着用户想要使用文字识别功能,此时再用pyninput控制键盘按下win+shift+s(对,虽然无法监听组合按键,但是可以控制组合按键),然后截图,进行后续流程……

顺着这个思路,写出了相应的代码。

from pynput.keyboard import Key,Controller,Listener

def on_press(key):
    pass    
    # print('{0} pressed'.format(key))

def on_release(key):
    # print('{0} release'.format(key))
    keyboard = Controller()
    if key ==  Key.f7:
        keyboard.press(Key.cmd)
        keyboard.press(Key.shift)
        keyboard.press('s')
        keyboard.release(Key.shift)
        keyboard.release(Key.cmd)
        keyboard.release('s')
    if key == Key.esc:        
        # Stop listener
        return False #停止监视

def main():
    with Listener(on_press=on_press,on_release=on_release) as listener:
        listener.join()

if __name__ == '__main__':
    main()

这样就实现了最基础的功能:按下F7,开始截图。

获取截图并判断

截完图后上传,思路是获取剪贴板中的图片,然后提交给百度api。

获取截图很简单,PIL库中有个模块ImageGrab,它的一个函数就是用来获取剪贴板图片。不过应该再做一个判断,万一剪贴板的内容不是图片呢?可以利用另外一个模块Image,来判断是否为图片。

接下来又是一个难点:我怎么判断应该什么时候获取剪贴板图片并上传呢?

一个直观的想法就是结合pynput库,当用户按下F7的时候进入判定,设一个条件为用户松开鼠标,此时截图肯定已经完成了,就可以上传。但是我实际操作起来,发现并不能实现这一点,不知道是哪里出问题了,只能加一个time()条件,截图开始后几秒后获取剪贴板图片并上传。如果您能解决这一问题,欢迎评论留言给我。

这块代码如下:

import time
time.sleep(2)
pic = ImageGrab.grabclipboard()
if isinstance(pic, Image.Image):
    pic.save('testpic.png')
with open("testpic.png", 'rb') as f:
    image_data = f.read()                
words = ocr(image_data)
for word in words:
    print(word)

上传截图并识别

这块代码就是上面的ocr函数,虽然是最核心的内容,但是实现起来也是最简单的。根据百度云的文档,首先要获取token,然后才能使用api。当然,你可以手动获取token,但是官方称它会30天一变,为了长期使用最好还是自动生成。

另外,根据文档,上传的图片都需要base64编码,所以还需要进行urlencode。返回的结果是json格式,也需要写个循环进行读取。

具体文档可参考

代码如下:

import requests
import base64
import urllib
import json
client_id ='填入你自己的client_id'
client_secret ='填入你自己的client_secret'
#获取token
def get_token():  # 获取你的token
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret
    request = urllib.request.Request(host)
    request.add_header('Content-Type', 'application/json; charset=UTF-8')
    response = urllib.request.urlopen(request)
    token_content = response.read()
    if token_content:
        token_info = json.loads(token_content)
        token_key = token_info['access_token']
    return token_key

def ocr(image_data):  # ocr识别函数
    base64_ima = base64.b64encode(image_data)
    access_token=get_token()
    data = {
            'image': base64_ima
            }
    headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
            }
    url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=" + str(access_token)
    result = requests.post(url, params=headers, data=data).json()
    for word in result['words_result']:
        yield word['words']

如何获取client_id和client_secret?

首先,注册一个百度账号,进入百度云ocr管理界面

点击创建应用

填写相关内容(随便填)

创建完成后,点击查看应用,或在主界面点击管理应用,就可以看到你的client_id(API key)和client_secret(Secret Key)了。

代码

以上代码整理如下:

import requests
import base64

import urllib
import json

from PIL import Image, ImageGrab
from pynput.keyboard import Key,Controller,Listener

import time


client_id ='填入你的API Key'
client_secret ='填入你的Secret Key'
#获取token
def get_token():
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret
    request = urllib.request.Request(host)
    request.add_header('Content-Type', 'application/json; charset=UTF-8')
    response = urllib.request.urlopen(request)
    token_content = response.read()
    if token_content:
        token_info = json.loads(token_content)
        token_key = token_info['access_token']
    return token_key

def ocr(image_data):  # ocr识别函数
    base64_ima = base64.b64encode(image_data)
    access_token=get_token()
    data = {
            'image': base64_ima
            }
    headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
            }
    url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=" + str(access_token)
    result = requests.post(url, params=headers, data=data).json()
    for word in result['words_result']:
        yield word['words']


def on_press(key):
    pass    
    # print('{0} pressed'.format(key))

def on_release(key):
    # print('{0} release'.format(key))
    keyboard = Controller()
    if key ==  Key.f7:
        keyboard.press(Key.cmd)
        keyboard.press(Key.shift)
        keyboard.press('s')
        keyboard.release(Key.shift)
        keyboard.release(Key.cmd)
        keyboard.release('s')
        time.sleep(2)
        pic = ImageGrab.grabclipboard()
        if isinstance(pic, Image.Image):
            pic.save('testpic.png')
        with open("testpic.png", 'rb') as f:
            image_data = f.read()                
        words = ocr(image_data)
        for word in words:
            print(word)
    if key == Key.esc:        
        # Stop listener
        return False #停止监视

def main():
    with Listener(on_press=on_press,on_release=on_release) as listener:
        listener.join()

if __name__ == '__main__':
    main()
Last modification:October 20th, 2019 at 05:13 pm