AIOCR:AI文字识别web集成系统@Kylin+RISCV

news2024/11/23 7:01:54

基于kotti_ai的AI文字识别web集成系统

AIOCR项目目标:

在Kylin+RISCV搭建一个kotti_ai构架的网站,提供AI OCR文字识别web服务。

二期目标:在AIOCR的基础上提供chatgpt和文心一言等大模型调用,建立综合大模型应用平台。

功能:

1 AI 文字识别功能即ocr推理

2 web集成功能即web内容管理系统

技术实现

ocr推理部分

飞桨框架安装,参见https://blog.csdn.net/skywalk8163/article/details/136610462

ocr推理,参见:飞桨AI应用@riscv OpenKylin-CSDN博客

web框架部分

Kotti_ai框架调通,参见:安装调试kotti_ai:AI+互联网企业级部署应用软件包@riscv+OpenKylin-CSDN博客

为kotti_ai添加ocr文字识别部分,见下面。

实现难点:

1 飞桨框架在riscv平台的编译安装,碰到问题较多,索性都一一解决了。

2 ocr推理部分,以为会比较顺利的onnx推理没有调通,转而使用飞桨推理

3 kotti_ai安装,因为kotti本身的复杂性,安装时需要反复修改和纠错。

4 ocr与kotti_ai的连通

ocr与kotti_ai的连通

单独调通orc

根据Paddle2ONNX里面的源码infer.py,将ocr部分写成适合调用的函数,比如写入aiocr.py进行测试。

过程略。

将ocr调用功能写入kotti_ai

写并放置aiocr.py文件

将调好ocr调用函数的aiocr.py文件,放置在kotti_ai/kotti_ai目录:

import os
import sys

import cv2
import copy
import numpy as np

# import sys
# sys.path.insert(0, '.')
print("ok1")
from .utils import predict_rec 
from .utils import predict_det 
from .utils import predict_cls

#import .utils.predict_rec as predict_rec
#import .utils.predict_det as predict_det
#import .utils.predict_cls as predict_cls


import argparse


def str2bool(v):
    return v.lower() in ("true", "t", "1")


def init_args():
    parser = argparse.ArgumentParser()
    # params for text detector
    parser.add_argument("--image_path", type=str)
    parser.add_argument("--det_algorithm", type=str, default='DB')
    parser.add_argument("--det_model_dir", type=str)
    parser.add_argument("--det_limit_side_len", type=float, default=960)
    parser.add_argument("--det_limit_type", type=str, default='max')

    # DB params
    parser.add_argument("--det_db_thresh", type=float, default=0.3)
    parser.add_argument("--det_db_box_thresh", type=float, default=0.6)
    parser.add_argument("--det_db_unclip_ratio", type=float, default=1.5)
    parser.add_argument("--max_batch_size", type=int, default=10)
    parser.add_argument("--use_dilation", type=str2bool, default=False)
    parser.add_argument("--det_db_score_mode", type=str, default="fast")

    parser.add_argument("--rec_algorithm", type=str, default='CRNN')
    parser.add_argument("--rec_model_dir", type=str)
    parser.add_argument("--rec_image_shape", type=str, default="3, 32, 320")
    parser.add_argument("--rec_batch_num", type=int, default=6)
    parser.add_argument("--max_text_length", type=int, default=25)
    parser.add_argument(
        "--rec_char_dict_path",
        type=str,
        default="./utils/doc/ppocr_keys_v1.txt")
    parser.add_argument("--use_space_char", type=str2bool, default=True)
    parser.add_argument(
        "--vis_font_path", type=str, default="./utils/doc/fonts/simfang.ttf")
    parser.add_argument("--drop_score", type=float, default=0.5)

    # params for text classifier
    parser.add_argument("--use_angle_cls", type=str2bool, default=True)
    parser.add_argument("--cls_model_dir", type=str)
    parser.add_argument("--cls_image_shape", type=str, default="3, 48, 192")
    parser.add_argument("--label_list", type=list, default=['0', '180'])
    parser.add_argument("--cls_batch_num", type=int, default=6)
    parser.add_argument("--cls_thresh", type=float, default=0.9)

    parser.add_argument("--use_paddle_predict", type=str2bool, default=False)
    parser.add_argument("--seed", type=int, default=42)
    return parser.parse_args(["--seed", "42"])

def preprocess_boxes(dt_boxes, ori_im, FLAGS=None):
    def get_rotate_crop_image(img, points):
        assert len(points) == 4, "shape of points must be 4*2"
        img_crop_width = int(
            max(
                np.linalg.norm(points[0] - points[1]),
                np.linalg.norm(points[2] - points[3])))
        img_crop_height = int(
            max(
                np.linalg.norm(points[0] - points[3]),
                np.linalg.norm(points[1] - points[2])))
        pts_std = np.float32([[0, 0], [img_crop_width, 0],
                              [img_crop_width, img_crop_height],
                              [0, img_crop_height]])
        M = cv2.getPerspectiveTransform(points, pts_std)
        dst_img = cv2.warpPerspective(
            img,
            M, (img_crop_width, img_crop_height),
            borderMode=cv2.BORDER_REPLICATE,
            flags=cv2.INTER_CUBIC)
        dst_img_height, dst_img_width = dst_img.shape[0:2]
        if dst_img_height * 1.0 / dst_img_width >= 1.5:
            dst_img = np.rot90(dst_img)
        return dst_img

    def sorted_boxes(dt_boxes):
        num_boxes = dt_boxes.shape[0]
        sorted_boxes = sorted(dt_boxes, key=lambda x: (x[0][1], x[0][0]))
        _boxes = list(sorted_boxes)

        for i in range(num_boxes - 1):
            if abs(_boxes[i + 1][0][1] - _boxes[i][0][1]) < 10 and \
                    (_boxes[i + 1][0][0] < _boxes[i][0][0]):
                tmp = _boxes[i]
                _boxes[i] = _boxes[i + 1]
                _boxes[i + 1] = tmp
        return _boxes

    img_crop_list = []
    dt_boxes = sorted_boxes(dt_boxes)
    for bno in range(len(dt_boxes)):
        tmp_box = copy.deepcopy(dt_boxes[bno])
        img_crop = get_rotate_crop_image(ori_im, tmp_box)
        img_crop_list.append(img_crop)
    return dt_boxes, img_crop_list


def postprocess(dt_boxes, rec_res, FLAGS=None):
    filter_boxes, filter_rec_res = [], []
    for box, rec_result in zip(dt_boxes, rec_res):
        text, score = rec_result
        if score >= FLAGS.drop_score:
            filter_boxes.append(box)
            filter_rec_res.append(rec_result)

    return filter_boxes, filter_rec_res

def ai_ocr(FLAGS=None, img=None):


    # 本地测试
    img = cv2.imread(FLAGS.image_path)
    # if img == None:
    #     return "None"
    # img = base64.b64encode(img)

    ori_im = img.copy()

    # text detect
    text_detector = predict_det.TextDetector(FLAGS)
    dt_boxes = text_detector(img)
    dt_boxes, img_crop_list = preprocess_boxes(dt_boxes, ori_im, FLAGS=FLAGS)

    # text classifier
    if FLAGS.use_angle_cls:
        text_classifier = predict_cls.TextClassifier(FLAGS)
        img_crop_list, angle_list = text_classifier(img_crop_list)

    # text recognize
    text_recognizer = predict_rec.TextRecognizer(FLAGS)
    rec_res = text_recognizer(img_crop_list)

    _, filter_rec_res = postprocess(dt_boxes, rec_res, FLAGS=FLAGS)
    ocrtext = " "
    for text, score in filter_rec_res:
        ocrtext += text
        print("{}, {:.3f}".format(text, score))
    print("Finish!")
    print(ocrtext)
    return ocrtext

if __name__ == "__main__":
    FLAGS = init_args()
    FLAGS.cls_model_dir="./inference/ch_ppocr_mobile_v2.0_cls_infer"
    FLAGS.rec_model_dir="./inference/ch_PP-OCRv2_rec_infer"
    FLAGS.det_model_dir="./inference/ch_PP-OCRv2_det_infer"
    FLAGS.image_path="./images/wahaha.jpg"
    # FLAGS.image_path="./images/lite_demo.png"
    FLAGS.use_paddle_predict="True"

    ai_ocr(FLAGS=FLAGS)

安装新的kotti_ai包

进入kotti_ai主目录,执行python setup.py develop 安装

使用develop选项,可以使用开发模式,修改源代码都会即时成效,不用再次安装。

测试

进入python,执行from kotti_ai.aiocr import ai_ocr ,如果通过,则证明aiocr.py文件写的正确。

最终测试代码为:

from kotti_ai.aiocr import ai_ocr
from kotti_ai.aiocr import init_args

FLAGS = init_args()
FLAGS.cls_model_dir="./inference/ch_ppocr_mobile_v2.0_cls_infer"
FLAGS.rec_model_dir="./inference/ch_PP-OCRv2_rec_infer"
FLAGS.det_model_dir="./inference/ch_PP-OCRv2_det_infer"
FLAGS.image_path="./images/lite_demo.png"
FLAGS.use_paddle_predict="True"

ai_ocr(FLAGS=FLAGS)

在kotti_ai中添加aiocr网页处理部分

aiocr.py文件中,ai_ocr函数修改成:

def ai_ocr(FLAGS=None, img=None):


    # 本地测试
    if img == None:
        # return "None"
        img = cv2.imread(FLAGS.image_path)
    # img = base64.b64encode(img)

edit.py修改:

            # out = ppshitu(im=im)

            from kotti_ai.aiocr import ai_ocr
            from kotti_ai.aiocr import init_args

            FLAGS = init_args()
            FLAGS.cls_model_dir="./inference/ch_ppocr_mobile_v2.0_cls_infer"
            FLAGS.rec_model_dir="./inference/ch_PP-OCRv2_rec_infer"
            FLAGS.det_model_dir="./inference/ch_PP-OCRv2_det_infer"
            FLAGS.image_path="./images/lite_demo.png"
            FLAGS.use_paddle_predict="True"

            out = ai_ocr(FLAGS=FLAGS, img=im)

            print("====out:", out)
            context.out = str(out)

启动kotti_ai

pserve development.ini

服务端口为5000

登录用户名admin 口令qwerty

上传图片测试文字识别,发现有报错,一一解决。

项目成功运行

经过紧张的调试,项目终于成功运行!

调试

报错AttributeError: 'str' object has no attribute 'decode'

File "/usr/lib/python3.8/site-packages/Kotti-2.0.9-py3.8.egg/kotti/security.py", line 103, in __init__
    password = get_principals().hash_password(password)
  File "/usr/lib/python3.8/site-packages/Kotti-2.0.9-py3.8.egg/kotti/security.py", line 564, in hash_password
    return bcrypt.hashpw(password, salt).decode("utf-8")
AttributeError: 'str' object has no attribute 'decode'

修改代码 即可

sudo vi /usr/lib/python3.8/site-packages/Kotti-2.0.9-py3.8.egg/kotti/security.py

        return bcrypt.hashpw(password, salt)
        # return bcrypt.hashpw(password, salt).decode("utf-8")

添加图片报错

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

 - Expression: "link(context, request)"
 - Filename:   ... /Kotti-2.0.9-py3.8.egg/kotti/templates/edit/el-parent.pt
 - Location:   (line 10: col 8)
 - Source:     ${link(context, request)}
                 ^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "link(context, request)"
 - Filename:   ... ages/Kotti-2.0.9-py3.8.egg/kotti/templates/editor-bar.pt
 - Location:   (line 24: col 14)
 - Source:     ${link(context, request)}
                 ^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "api.render_template('kotti:templates/editor-bar.pt')"
 - Filename:   ... ges/Kotti-2.0.9-py3.8.egg/kotti/templates/view/master.pt
 - Location:   (line 37: col 24)
 - Source:     ... ce="api.render_template('kotti:templates/editor-bar.pt')"></ ...
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "api.macro('kotti:templates/view/master.pt')"
 - Filename:   /home/skywalk/kotti_ai/kotti_ai/templates/image.pt
 - Location:   (line 3: col 23)
 - Source:     ... :use-macro="api.macro('kotti:templates/view/master.pt')">
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Arguments:  view: <NoneType ('None',) at 0x3cfc48>
               renderer_name: kotti:templates/edit/el-parent.pt
               renderer_info: <RendererHelper ('None',) at 0x3e13e06400>
               context: <AImage ('test',) at 0x3e13fd3580>
               request: <Request ('None',) at 0x3e2005a790>
               req: <Request ('None',) at 0x3e2005a790>
               get_csrf_token: <partial ('None',) at 0x3e13f02310>
               api: <TemplateAPI ('None',) at 0x3e13fd30d0>
               link: <LinkRenderer ('None',) at 0x3f8848fc40>
               target_language: <NoneType ('None',) at 0x3cfc48>
               repeat: <RepeatDict ('None',) at 0x3e13fc1c70>

估计是返回值没有str导致的。原来img是个列表,所以aiocr.py文件中修改成img == []

报错找不到模型文件


将代码里的模型文件的配置,改为框架的相对路径,即:
```
            FLAGS = init_args()
            FLAGS.cls_model_dir="./kotti_ai/inference/ch_ppocr_mobile_v2.0_cls_infer"
            FLAGS.rec_model_dir="./kotti_ai/inference/ch_PP-OCRv2_rec_infer"
            FLAGS.det_model_dir="./kotti_ai/inference/ch_PP-OCRv2_det_infer"
            FLAGS.image_path="./kotti_ai/images/lite_demo.png"
            FLAGS.use_paddle_predict="True"
```

报错找不到rec_char_dict_path txt文件


在代码里将其改为框架的相对路径
parser.add_argument(
        "--rec_char_dict_path",
        type=str,
        default="./kotti_ai/utils/doc/ppocr_keys_v1.txt")

报错__iterator = get('eval', eval)(_lookup_attr(getname('context'), 'out'))

  File "/usr/local/lib/python3.8/dist-packages/chameleon/template.py", line 200, in render
    self._render(
  File "/tmp/tmpv4xn_ul5/image_bbceef85f35e624757730558bb775d8e.py", line 375, in render
    __m(__stream, econtext.copy(), rcontext, __i18n_domain, __i18n_context, target_language)
  File "/tmp/tmp5dtqmvp7/master_21123e986705ec1c4189aca762f798cd.py", line 773, in render_main
    __slot_content(__stream, econtext.copy(), rcontext)
  File "/tmp/tmpv4xn_ul5/image_bbceef85f35e624757730558bb775d8e.py", line 248, in __fill_content
    __iterator = get('eval', eval)(_lookup_attr(getname('context'), 'out'))
  File "<string>", line None
SyntaxError: <no detail available>

    经过查看跟踪信息,发现已经正确文字识别了。是模板文件那个地方报错了template: /home/skywalk/kotti_ai/kotti_ai/templates/image.pt

修改该文件,将原来的循环画表语句去掉,直接显示:

<p><h2>OCR result:</h2>${context.out}</p>

最终网站起来拉!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1532460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL_list文档使用介绍与底层代码实现简介

文章目录 list介绍list的使用构造函数&#xff08;constructor&#xff09;迭代器list capacitylist modify&#xff08;修改&#xff09;其他接口函数list迭代器失效问题 list实现基础框架(节点类&#xff09;基础框架&#xff08;迭代器类&#xff09;基础框架&#xff08;链…

爬虫系列-CSS基础语法

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” CSS全称层叠样式表 &#xff0c;主要用来定义页面内容展示效果的一门语言&#xff0c;HTML&#xff1a;页面骨架&#xff0c;素颜CSS&#xff1a;页面效果美化&#xff1a…

【概念】神马是分布式?

SueWakeup​​​​​ 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习Java框架 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f7; 友情赞助播出&#xff01; 目录 前言 1. 系统架构的演变 2. SOA 与微服务的关系 3. 分布式核心知识…

PCL QT visualizer

PCL QT visualizer 用Qt创建PCL 可视化应用程序。 项目组织 UI界面 Compile & Run cmake -B build -S . -DCMAKE_TOOLCHAIN_FILED:\vcpkg\scripts\buildsystems\vcpkg.cmakecmake --build build --config Releasewindeployqt6.exe .\pcl_visualizer.exe.\build\Release\…

“因聚而生,数智有为”实在智能Agent牵手华为生态合作

近日&#xff0c;2024华为中国合作伙伴大会顺利闭幕&#xff0c;实在智能受邀出席&#xff0c;携TARS大模型及实在Agent&#xff08;智能体&#xff09;数字员工精彩亮相&#xff0c;与华为生态伙伴共同探讨如何帮助客户抓住数智化转型的巨大机遇&#xff0c;加速培育“新质生产…

Python数学建模-2.9Matplotlib库

Matplotlib库是Python中一个非常流行的绘图库&#xff0c;它提供了大量的绘图工具&#xff0c;可以生成各种类型的静态、动态、交互式的图表。Matplotlib的设计初衷是为了与NumPy配合使用&#xff0c;从而提供一个强大的数学绘图工具。 1.Matplotlib的主要特点 丰富的图表类型…

vue2 中使用音频

vue2 中使用音频 在 template 页面 写入 audio 标签 <template><div><audio ref"moreAudio" :src"moreAudioSrc"></audio><audio ref"noAudio" :src"noAudioSrc"></audio></div> </t…

MySQL 索引的分类和优化

​ 优质博文&#xff1a;IT-BLOG-CN 索引是什么 &#xff1a; MySQL 官方对索引的定义&#xff1a;索引&#xff08;Index&#xff09;是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质&#xff1a;索引是数据结构。索引的目的在于提高查询效率。可以简单理解为&#…

css实现的3D立体视觉效果鸡蛋动画特效

这是一个基于纯css实现的3D立体视觉效果鸡蛋动画特效&#xff0c;喜欢的朋友可以拿来使用演示动态效果 css实现的3D立体视觉效果鸡蛋动画特效

音频和视频标签

音频用audio标签 controls表示控制栏 loop循环播放音频 autoplay自动播放&#xff08;浏览器基于隐私一般不支持&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Com…

从零到一构建短链接系统(八)

1.git上传远程仓库&#xff08;现在才想起来&#xff09; git init git add . git commit -m "first commit" git remote add origin OLiyscxm/shortlink git push -u origin "master" 2.开发全局异常拦截器之后就可以简化UserController 拦截器可以…

分布式文件存储与数据缓存(二)| Redis

目录 Redis概述_什么是NoSQLNoSQL的四大分类KV型NoSql&#xff08;代表----Redis&#xff09;列式NoSql&#xff08;代表----HBase&#xff09;文档型NoSql&#xff08;代表----MongoDB&#xff09;搜索型NoSql&#xff08;代表----ElasticSearch&#xff09; 关系型数据库和非…

【Kotlin】扩展属性、扩展函数

1 类的扩展 Kotlin 提供了扩展类或接口的操作&#xff0c;而无需通过类继承或使用装饰器等设计模式&#xff0c;来为某个类添加一些额外的属性或函数&#xff0c;我们只需要通过一个被称为扩展的特殊声明来完成。通过这种机制&#xff0c;我们可以将那些第三方类不具备的功能强…

Css提高——Css3盒子模型border-box

1、盒子模型的种类与区别 CSS3 中可以通过 box-sizing 来指定盒模型&#xff0c;有2个值&#xff1a;即可指定为 content-box、border-box&#xff0c;这样我们 计算盒子大小的方式就发生了改变。 CSS3 盒子模型 可以分成两种情况&#xff1a; 1. box-sizing: content-box 盒…

智慧安全:守护智慧城市的安全屏障

随着信息技术的迅猛发展&#xff0c;智慧城市已成为现代城市发展的重要方向。智慧城市通过集成应用先进的信息通信技术&#xff0c;实现城市管理、服务、运行的智能化&#xff0c;为城市的可持续发展注入了新的活力。然而&#xff0c;在智慧城市的建设过程中&#xff0c;安全问…

华为openEuler系统卸载jdk

华为openEuler系统卸载jdk 1.查看openEuler上已安装的 Java 版本&#xff1a; 在终端中运行以下命令&#xff0c;查看系统中已经安装的 Java 版本。 sudo alternatives --config java这将列出已安装的 Java 版本&#xff0c;你可以看到当前使用的是哪个版本 2.卸载 Java&am…

SpringCloudAlibaba Nacos配置及应用

Nacos搭建及配置 nacos本机服务搭建 windows上搭建单机nacos&#xff1a; Releases alibaba/nacos GitHub 下载安装包 下载本地&#xff0c;解压&#xff0c;直接运行&#xff08;保证安装包的绝度路径只有英文字符&#xff0c;有中文会导致运行失败&#xff09;&#xff…

测试平台——前端框架

一、创建vue项目 npm init vitelatest web_class wylWYLdeMacBook-Air testplatform % npm init vitelatest web_class ✔ Select a framework: › Vue ✔ Select a variant: › JavaScriptScaffolding project in /Users/wyl/workspace/testplatform/web_class...Done. Now…

【云原生 • Kubernetes】认识 k8s、k8s 架构、核心实战

文章目录 Kubernetes基础概念1. 是什么2. 架构2.1 工作方式2.2 组件架构 3. k8s组件创建集群步骤一 基础环境步骤二 安装kubelet、kubeadm、kubectl步骤三 主节点使用kubeadm引导集群步骤四 副节点加入主节点步骤五 部署dashboard Kubernetes核心实战1. 资源创建方式2. Namespa…

Flink RocksDB状态后端优化总结

截至当前&#xff0c;Flink 作业的状态后端仍然只有 Memory、FileSystem 和 RocksDB 三种可选&#xff0c;且 RocksDB 是状态数据量较大&#xff08;GB 到 TB 级别&#xff09;时的唯一选择。RocksDB 的性能发挥非常仰赖调优&#xff0c;如果全部采用默认配置&#xff0c;读写性…