zoukankan      html  css  js  c++  java
  • Mathpix-OCR识别手写矩阵并计算PageRank

    源码仓库

    https://github.com/Cpaulyz/BigDataAnalysis/tree/master/Assignment3

    工作流程

    尝试

    本次作业的难点在于OCR,对于OCR开源库/API/工具的使用

    经过初步分析,认为本次OCR的目标有两个难点

    1. 手写
    2. 数学公式

    因此,本人先后进行了以下尝试

    • python的TesseractOCR库,无法识别
    • 讯飞OCR API,无法识别
    • 百度OCR API,无法识别
    • 腾讯OCR API,无法识别
    • Mathpix,可识别

    image-20200923135501568

    因此,最终选取Mathpix作为OCR部分的工具

    思路

    Mathpix的图像识别特殊之处在于,它将识别数学公式并将其转换为latex

    因此,工作思路如下图所示:

    image-20200923140136160

    1 OCR

    OCR部分采用的是Mathpix提供的服务(https://mathpix.com/)

    但由于Mathpix需要收费,且需要信用卡注册,这边取巧使用了国内的一个翻版网站提供的服务(https://www.latexlive.com/##)

    通过监控其HTTP请求,可以看到其图像识别的请求API是对mathpix服务的封装,因此可以认为是一致的

    image-20200923140600841

    响应内容为latex文本

    image-20200923140638567

    考虑到如果是1万张图片呢的情况,我们必须使用python脚本对其请求进行封装,以实现自动化和可修改性

    import base64
    import json
    import requests
    
    def encode_base64(file):
        with open(file, 'rb') as f:
            img_data = f.read()
            base64_data = base64.b64encode(img_data)
            # 如果想要在浏览器上访问base64格式图片,需要在前面加上:data:image/jpeg;base64,
            base64_str = str(base64_data, 'utf-8')
            return base64_str
    
    
    # 传入本地图片路径,发起请求,返回latex文本
    def get_latex_from_file(img_path: str):
        base64code = encode_base64(img_path)
        url = "https://www.latexlive.com:5001/api/mathpix/posttomathpix"
        # headers = {'Content-Type': 'application/json'}
        payload = {"src": "data:image/png;base64," + base64code}
        r = requests.post(url, json=payload)
        print(r.text)
        content = json.loads(r.text).get('latex_styled')
        print(content)
        return content
    

    返回结果如下

    N=left[egin{array}{cccc}
    0 & frac{1}{2} & 0 & frac{1}{2} \
    frac{1}{3} & 0 & 0 & frac{1}{2} \
    frac{1}{3} & frac{1}{2} & 0 & 0 \
    frac{1}{3} & 0 & 1 & 0
    end{array}
    ight]
    

    2 LatexParser

    OCR识别后的值是一个latex格式的文本,因此我们需要对其进行语法解析

    假设OCR识别出的内容是可信且没有问题的,我们需要做的是提取矩阵部分,将其保存到一个二维数组中

    主要实现思路是使用正则表达式进行提取

    # 对外暴露的接口
    def latex_to_array(latex):
        content = get_array_str(latex)
        return matrix_to_array(content)
    
    
    # 提取egin{array}到end{array}中间的部分
    def get_array_str(latex):
        # re_array =
        pattern = re.compile(r'\begin{array}{.*}[wW]*\end{array}', re.DOTALL)
        # pattern = re.compile(r'[.|
    ]*',re.DOTALL)
        array = pattern.findall(latex)[0]
        content = re.sub(r'(\begin{array}{.*}(
    )*)|((
    )*\end{array})', "", array)
        # print(content)
        return content
    
    
    # 将latex中的数组部分转为二维数组
    def matrix_to_array(content: str):
        res = []
        rows = content.split('\\
    ')
        for row in rows:
            res.append(row_to_array(row))
    
        # print(res)
        return res
    
    
    # 将latex中的一行转为list
    def row_to_array(row: str):
        res = []
        expr_array = row.split('&')
        for expr in expr_array:
            res.append(expr_to_num(expr))
        return res
    
    
    # 将latex中的一个表达式转为数字
    def expr_to_num(expr: str):
        expr = expr.strip()
        if expr.isdigit():
            return expr
        else:
            matchObj = re.match(r'\frac{(d+)}{(d+)}', expr)
            fenzi = int(matchObj.group(1))
            fenmu = int(matchObj.group(2))
            return fenzi / fenmu
    
    

    以本题为例,提取后的结果如下

    [['0', 0.5, '0', 0.5], 
    [0.3333333333333333, '0', '0', 0.5], 
    [0.3333333333333333, 0.5, '0', '0'], 
    [0.3333333333333333, '0', '1', '0']]
    

    3 PageRank

    pagerank部分使用了迭代的方式进行计算,有两个可变参数迭代次数和阻尼系数,实现如下

    初始pagerank值r=[1/N]N

    计算公式为

    image-20200923141526929

    代码实现如下:

    # array: 转移矩阵(二维数组)
    # time:迭代次数
    # d: 阻尼系数
    def calculate(array, time=10, d=0.85):
        length = len(array)
        value = [1 / length] * length  # page rank value list
        for i in range(time):
            new_value = []  # new page rank value list
            for row_index in range(length):
                row = array[row_index]
                tmp = (1 - d) / length  # tmp is new value item
                for col_index in range(len(row)):
                    tmp += d * float(row[col_index]) * value[col_index]
                new_value.append(tmp)
            print('iter', i + 1, new_value)
            value = new_value
    

    技术选型

    使用python进行实现

    迭代结果

    若不考虑阻尼系数,十次迭代的计算结果如下

    init [0.25, 0.25, 0.25, 0.25]
    iter 1 [0.25, 0.20833333333333331, 0.20833333333333331, 0.3333333333333333]
    iter 2 [0.2708333333333333, 0.25, 0.1875, 0.29166666666666663]
    iter 3 [0.2708333333333333, 0.23611111111111108, 0.21527777777777776, 0.2777777777777778]
    iter 4 [0.2569444444444444, 0.22916666666666666, 0.20833333333333331, 0.3055555555555555]
    iter 5 [0.2673611111111111, 0.2384259259259259, 0.20023148148148145, 0.29398148148148145]
    iter 6 [0.26620370370370366, 0.2361111111111111, 0.20833333333333331, 0.2893518518518518]
    iter 7 [0.26273148148148145, 0.23341049382716045, 0.2067901234567901, 0.29706790123456783]
    iter 8 [0.2652391975308641, 0.23611111111111105, 0.20428240740740738, 0.29436728395061723]
    iter 9 [0.2652391975308641, 0.23559670781893, 0.20646862139917688, 0.29269547325102874]
    iter 10 [0.2641460905349794, 0.23476080246913572, 0.20621141975308638, 0.29488168724279823]
    

    若考虑阻尼系数,假设d=0.85,十次迭代结果如下

    init [0.25, 0.25, 0.25, 0.25]
    iter 1 [0.25, 0.21458333333333335, 0.21458333333333335, 0.3208333333333333]
    iter 2 [0.2650520833333333, 0.24468749999999997, 0.19953125, 0.2907291666666667]
    iter 3 [0.2650520833333333, 0.2361579861111111, 0.21659027777777776, 0.28219965277777775]
    iter 4 [0.2578019965277778, 0.2325329427083333, 0.21296523437499998, 0.29669982638888887]
    iter 5 [0.26242392686631943, 0.23664132523148146, 0.20937039966724535, 0.2915643482349537]
    iter 6 [0.26198741122323493, 0.2357682939453125, 0.2124260091688368, 0.2898182856626157]
    iter 7 [0.26087429633336945, 0.23490253791986154, 0.21193129144000772, 0.2922918743067612]
    iter 8 [0.26155762519631465, 0.23563843054149486, 0.2112479625770625, 0.2915559816851279]
    iter 9 [0.26155762519631465, 0.23551928602180183, 0.21175432678575778, 0.2911687619961256]
    iter 10 [0.2613424204076192, 0.23535471765397586, 0.21170369036488826, 0.2915991715735166]
    

    附:运行截图

    image-20200923142245276

    拓展

    考虑:如果是1万张图片呢?

    # 传入文件路径列表
    def page_rank(paths):
        for img_path in paths:
            latex = get_latex_from_file('test.png')
            arr = latex_to_array(latex)
            # calculate(arr, 10, 1)  # 不考虑阻尼系数
            calculate(arr, 10)  # 考虑阻尼系数
    

    只需要传入图片文件路径列表即可

  • 相关阅读:
    jquery 全选反选 .prop('checked',!$(this).is(':checked'));
    C#字典 Dictionary<Tkey,Tvalue> 之线程安全问题 ConcurrentDictionary<Tkey,Tvalue> 多线程字典
    javascript 获取iframe里页面中元素值的方法 关于contentWindow和contentDocumen
    WPF 之 UI 异步交互
    layui 解决从子页面传值回父页面方法
    layui 解决文本框只输入数字方法
    layui 合计出现多位小数的解决方法
    Jquery.Cookie 使用
    Git 上传代码步骤
    2分钟教你如何彻底删除Win10中 Windows.old 文件夹
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14128893.html
Copyright © 2011-2022 走看看