安卓拍照扫描APP解决方案——基于深度学习的文本方向检测与校正

简介

在OCR(光学字符识别)系统中,为了提高OCR系统的性能,确保准确识别文本内容。图像预处理是一个关键的组成部分。其中,一个重要的任务是矫正文本方向。例如,在进行文字识别时,不仅需要有效地提取和识别文字,还应确保文本以正确的方向呈现,以提高准确性。这意味着在识别文本之前,必须对图像进行预处理,以使文本在水平或垂直方向上对齐。在传统数字图像处理中常用投影分析、Hough变换、方向梯度直方图(HOG)等,来检测并调整文本的方向。
但在实现过程中,发现传统的数字图像处理撸棒性并不是很高,所以选用了基于深度学习的方法,实现的步骤是使用先对文档进行边缘检测,关于边缘检测,可以看我之前的博客。然后对剪切出来的文档使用DBNet进行文本检测,之后对检测的行做文字方向检测。

安卓实现效果视频:

文本方向检测与校正

文本检测

常用的基于深度学习的文字检测方法一般可以分为基于回归的、基于分割的两大类,DBNet把两者进行结合的方法。

常用的基于回归的方法有:

  1. CTPN(Connectionist Text Proposal Network): CTPN是一种基于回归的文本检测方法,主要通过在图像中生成文本线的候选区域,并通过回归来精细调整这些区域。

  2. Textbox系列: Textbox是一系列基于回归的算法,主要关注在生成文本框的同时,对文本的旋转和形变进行建模,以适应各种文本形状。

  3. EAST(Efficient and Accurate Scene Text Detector): EAST是一种基于回归的文本检测方法,采用全卷积网络,通过预测文本框的四个角点坐标实现文本检测。

  4. CRAFT(Character Region Awareness for Text Detection): CRAFT是一种采用像素值回归的方法,通过在字符级别上实现像素级别的回归,能够有效地处理曲线形状的文本。

  5. SA-Text(Structure-Aware Text Detector): SA-Text是另一种基于像素值回归的方法,通过捕获文本结构信息,能够对小文本和曲线文本进行有效检测。

**基于分割的方法和结合回归和分割的方法 **:

  1. PSENet(Shape Robust Text Detection with Progressive Scale Expansion Network): PSENet是一种基于分割的文本检测方法,通过逐步扩展文本区域的尺度来实现文本实例的检测。

  2. DBNet(Dilated Bi-directional Network): DBNet是一种将回归和分割结合的文本检测方法,采用了膨胀卷积和双向上下文信息,使其能够在不同尺度上捕获文本信息,同时通过联合训练提高检测性能。

DBNet

DBNet算法在传统的基于0,1黑白像素阈值进行二值化的基础上,提出了threshold map陪练probability map生成DB(Differentiable Binarization,可微二值化)函数,从而优化反向传播梯度更新的图像文本检测方法
DBNet的最大创新点。在基于分割的文本检测网络中,最终的二值化map都是使用的固定阈值来获取,并且阈值不同对性能影响较大。在DBNet,对每一个像素点进行自适应二值化,二值化阈值由网络学习得到,彻底将二值化这一步骤加入到网络里一起训练,这样最终的输出图对于阈值就会非常鲁棒。
在这里插入图片描述
更多关于算法原理,可以转到DBNet的git:https://github.com/WenmuZhou/DBNet.pytorch?tab=readme-ov-file 。

检测效果:
在这里插入图片描述
在这里插入图片描述

文本方向分类

在文档拍摄过程中,由于拍摄设备旋转,生成的图片可能存在不同方向。要对这些方向进行分类,这里采用了基于PaddleClas的超轻量图像分类方案(PULC)算法。该算法旨在快速构建轻量级、高精度、可实际应用的文字图像方向分类模型。
关于文字方向分类具体优化与如何训练自己的数据可以参考Paddle的官方文档:https://github.com/PaddlePaddle/PaddleClas/blob/release/2.5/docs/zh_CN/models/PULC/PULC_text_image_orientation.md

安卓实现

我的开发环境是Android Studio 北极狐,真机是华为mate 30 pro,系统是HarmonyOS 4.0.0, NDK 是21.1.6352462这个版本,可实现CPU与GPU、NPU推理,推理速度与精度可以按真机去匹配。使用的推理库是onnxruntime。

实现代码

#pragma once
#include "../onnxocr/DbNet.h"
#include "../onnxocr/AngleNet.h"
#include "../onnxocr/OcrUtils.h"namespace SCAN
{class TextDirection{public:TextDirection();~TextDirection();int read_model(std::string _db_model_path = "ch_PP-OCRv3_det_infer.onnx",std::string _angle_model_path = "ch_ppocr_mobile_v2.0_cls_infer.onnx",int _thread_num = 4, int _gpu_index = 0);void set_thread_num(int _thread_num);void set_gpu_index(int _gpu_index);int direction(cv::Mat& cv_src, cv::Mat& cv_dst);private:ONNXOCR::DbNet db_net;ONNXOCR::AngleNet angle_net;int thread_num;int gpu_index;const int angle_w = 192;const int angle_h = 48;public:int padding = 10;int maxSideLen = 1024;float boxScoreThresh = 0.4f;float boxThresh = 0.2f;float unClipRatio = 1.6f;std::string db_model_path;std::string angle_model_path;};
}
#include "TextDirection.h"namespace SCAN
{TextDirection::TextDirection(){}TextDirection::~TextDirection(){}int TextDirection::read_model(std::string _db_model_path, std::string _angle_model_path, int _thread_num, int _gpu_index){db_model_path = _db_model_path;angle_model_path = _angle_model_path;thread_num = _thread_num;gpu_index = _gpu_index;db_net.set_thread_num(thread_num);angle_net.set_thread_num(thread_num);db_net.set_gpu_index(gpu_index);angle_net.set_gpu_index(-1);db_net.read_model(db_model_path);angle_net.read_model(angle_model_path);return 0;}void TextDirection::set_gpu_index(int _gpu_index){gpu_index = _gpu_index;db_net.set_gpu_index(gpu_index);angle_net.set_gpu_index(-1);}void TextDirection::set_thread_num(int _thread_num){thread_num = _thread_num;db_net.set_thread_num(thread_num);angle_net.set_thread_num(thread_num);}cv::Mat make_padding(cv::Mat& src, const int padding){if (padding <= 0) return src;cv::Scalar paddingScalar = { 255, 255, 255 };cv::Mat paddingSrc;cv::copyMakeBorder(src, paddingSrc, padding, padding, padding, padding, cv::BORDER_ISOLATED, paddingScalar);return paddingSrc;}/// -1 - 180度/// 0  - 90度/// 1  - 270度cv::Mat rotateMat(cv::Mat& cv_src, int angle_index){cv::Mat cv_copy = cv_src.clone();cv::Mat cv_dst;if (angle_index == -1){flip(cv_copy, cv_dst, angle_index);return cv_dst;}transpose(cv_copy, cv_copy);flip(cv_copy, cv_dst, angle_index);return cv_dst;}int TextDirection::direction(cv::Mat& cv_src, cv::Mat& cv_dst){cv::Mat originSrc = cv_src;int originMaxSide = (std::max)(originSrc.cols, originSrc.rows);int resize;if (maxSideLen <= 0 || maxSideLen > originMaxSide){resize = originMaxSide;}else{resize = maxSideLen;}resize += 2 * padding;cv::Rect paddingRect(padding, padding, originSrc.cols, originSrc.rows);cv::Mat cv_padding = make_padding(originSrc, padding);ScaleParam scale = ONNXOCR::getScaleParam(cv_padding, resize);std::vector<TextBox> textBoxes = db_net.get_text_boxes(cv_padding, scale, boxScoreThresh, boxThresh, unClipRatio);std::vector<int> angle_index = { 0, 0, 0, 0};for (size_t i = 0; i < textBoxes.size(); ++i){cv::Mat cv_part = ONNXOCR::get_crop_image(cv_padding, textBoxes[i].boxPoint);if (float(cv_part.rows) >= float(cv_part.cols) * 1.5){cv::Mat cv_copy = cv::Mat(cv_part.rows, cv_part.cols, cv_part.depth());cv::transpose(cv_part, cv_copy);cv::flip(cv_copy, cv_copy, 0);cv::Mat cv_angle;cv::resize(cv_copy, cv_angle, cv::Size(angle_w, angle_h));Angle angle = angle_net.get_angle(cv_angle);if (angle.index == 0){angle_index[0] ++;}else if(angle.index == 1){angle_index[1] ++;}}else{cv::Mat cv_angle;cv::resize(cv_part, cv_angle, cv::Size(angle_w, angle_h));Angle angle = angle_net.get_angle(cv_angle);if (angle.index == 0){angle_index[2] ++;}else if(angle.index == 1){angle_index[3] ++;}}}auto maxElement = std::max_element(angle_index.begin(), angle_index.end());int maxIndex = std::distance(angle_index.begin(), maxElement);switch (maxIndex){case 0:cv_dst = rotateMat(cv_src, 0);break;case 1:cv_dst = rotateMat(cv_src, 1);break;case 2:cv_dst = cv_src.clone();break;case 3:cv_dst = rotateMat(cv_src, -1);break;default:break;}return maxIndex;}
} 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Js的String的replace(和replaceAll(

EcmaJavascriptJs的String的 replace( 和 replaceAll( 方法 String.prototype.replaceString.prototype.replaceAll 相同点 都是String.prototype的函数都是用于字符串替换都是两个参数第一个参数都可以是正则或字符串第二参数都可以是字符串或者回调函数, 回调会传入一个参…

32、[ShallowFBCSPNet、EEG-ITNet、EEGResNet、EEGInception]4种模型处理脑机接口-MOABB数据库+代码+结果

脑机接口基准之母—MOABB数据库介绍&#xff1a; 18、MOABB&#xff1a;BCI创新模型基准测试的群虫之心-CSDN博客 Dataset&#xff1a; BNCI 2014-001 Motor Imagery dataset. (BCI IV2a)&#xff1a; https://paperswithcode.com/dataset/bci-competition-4-version-iia …

AI小蜜批量写作助手:多级指令,插件,GPTs满足不同写作需求

为什么会开发这个脚本&#xff1f; 爆文项目的核心是矩阵怼量 具体怎么做这里介绍很清楚了&#xff1a; AI爆文撸流量主保姆级教程3.0脚本写作教程&#xff08;解放双手&#xff09; 我在刚做爆文项目时候&#xff0c;都是手动操作&#xff0c;复制指令&#xff0c;组合指令…

2023 | 美团技术团队热门技术文章汇总

新年好&#xff01;时光飞逝&#xff0c;我们告别了难忘的2023&#xff0c;迎来了充满希望的2024。再次感谢大家的一路相伴~~ 今天&#xff0c;我们整理了2023年公众号阅读量靠前的10篇技术文章&#xff0c;欢迎大家品阅。祝愿大家在新的一年里&#xff0c;幸福平安&#xff0…

[足式机器人]Part2 Dr. CAN学习笔记-动态系统建模与分析 Ch02-6频率响应与滤波器

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-动态系统建模与分析 Ch02-6频率响应与滤波器 1st order system 一阶系统 低通滤波器——Loss Pass Filter

C#上位机与欧姆龙PLC的通信10----开发专用的通讯工具软件(WPF版)

1、介绍 上节开发了一个winform版的通讯测试工具&#xff0c;这节再搞个wpf版的&#xff0c;wpf是什么&#xff1f;请自行百度&#xff0c;也可以看前面的博客&#xff0c;WPF真入门教程&#xff0c;wpf的界面效果是比winform漂亮&#xff0c;因为wpf使用了web项目中的css样式…

国图公考:2024年上半年中小学教师资格考试(笔试)报考须知

(一)信息填报时间&#xff1a;2024年1月12日9:00至1月15日16&#xff1a;00 (二)信息确认时间&#xff1a;2024年1月13日9:00至1月16日16&#xff1a;00 (三)网上缴费时间&#xff1a;2024年1月13日9:00至1月17日24&#xff1a;00

新年福利|这款价值数万的报表工具永久免费了

随着数据资产的价值逐渐凸显&#xff0c;越来越多的企业会希望采用报表工具来处理数据分析&#xff0c;了解业务经营状况&#xff0c;从而辅助经营决策。不过&#xff0c;企业在选型报表工具的时候经常会遇到以下几个问题&#xff1a; 各个报表工具有很多功能和特性&#xff0c…

硬盘基本知识(磁头、磁道、扇区、柱面)

概述 盘片&#xff08;platter&#xff09; 磁头&#xff08;head&#xff09; 磁道&#xff08;track&#xff09; 扇区&#xff08;sector&#xff09; 柱面&#xff08;cylinder&#xff09; 盘片 片面 和 磁头 硬盘中一般会有多个盘片组成&#xff0c;每个盘片包含两个面…

大数据毕设分享 flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

商品小程序(6.商品详情)

目录 一、获取商品详情数据二、渲染商品详情页的UI结构1、渲染轮播图区域2、实现轮播图预览效果3、渲染商品信息区域4、渲染商品详情信息5、解决商品价格闪烁的问题 三、渲染详情页底部的商品导航区域1、渲染商品导航区域的UI结构2、点击跳转到购物车页面 本章主要完成商品详情…

大数据框架ElasticSearch学习网站,让你的技能瞬间升级!

介绍&#xff1a;Elasticsearch是一个分布式、免费和开放的搜索和分析引擎&#xff0c;它适用于所有类型的数据&#xff0c;包括文本Elasticsearch是一个分布式、免费和开放的搜索和分析引擎&#xff0c;它适用于所有类型的数据&#xff0c;包括文本、数字、地理空间、结构化和…