安卓拍照扫描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.mzph.cn/news/600131.shtml

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

相关文章

数据结构和算法-插入排序(算法效率 折半优化 顺序表与链表插入排序 代码实现)

文章目录 插入排序算法实现算法效率分析优化-折半插入排序代码实现对链表进行插入排序小结 插入排序 首先49当作第一个已经排好序得元素&#xff0c;将第二个元素与前面得元素对比&#xff0c;发现小于49&#xff0c;于是49移动位置 此时将65与之前元素对比&#xff0c;发现其…

【KingbaseES】实现MySql函数TEXT_EQUAL

TEXT_EQUAL CREATE OR REPLACE FUNCTION text_equal_ci(text_value1 text, text_value2 text) RETURNS boolean AS $$ BEGIN RETURN (lower(text_value1) lower(text_value2)); END; $$ LANGUAGE plpgsql IMMUTABLE;

苦学golang半年,写了一款web服务器

苦学golang半年&#xff0c;写了一款web服务器 文章目录 苦学golang半年&#xff0c;写了一款web服务器example 项目地址&#xff1a;https://github.com/fengyuan-liang/jet-web-fasthttp 苦学golang半年&#xff0c;写了一款web服务器&#xff0c;里面包含笔者各种工程实践&a…

深度学习中的准确率、精确率(查准率)、召回率(查全率)、F1值、ROC曲线的AUC值,

混淆矩阵 其中关于 TP, TN; FP, FN 的解释&#xff1b; 其中首字母 T&#xff0c;F代表预测的情况&#xff0c;即T代表预测的结果是对的&#xff0c; F代表预测的结果是错误的&#xff1b; 第二个字母代表预测是预测为 正样本&#xff0c;还是负样本&#xff0c; Positve 代表…

第11章 GUI Page462~476 步骤二十三 步骤二十四 Undo/Redo ②“添加操作”支持“Undo/Redo”

工程二 1.为AddAction类添加Undo() Redo() GetName()成员函数 2.实现AddAction类的Undo() Redo()函数 3.运行效果&#xff0c;但是日志窗口没有记录 原因&#xff1a;AddAction(EditAction* newAction)函数没有实现&#xff0c;另外参数是EditAction类型 所以我们还需要在基…

线性代数 --- 为什么LU分解中的下三角矩阵L的主对角线上都是1?

为什么LU分解中的下三角矩阵L的主对角线上都是1? 一方面&#xff0c;对于LU分解而言&#xff0c;下三角阵L是对高斯消元过程的记录&#xff0c;是高斯消元的逆过程&#xff0c;是多个消元矩阵E的逆矩阵的乘积(形如下图中的下三角矩阵)&#xff0c;即&#xff1a; 另一方面&…

基于OpenCV的图像平移

基本概念 图像的平移操作是将图像的所有像素坐标进行水平或者垂直方向的移动&#xff0c;也就是所有像素点按照给定的偏移量在水平方向上沿X轴&#xff0c;垂直方向上沿y轴移动。 设原始图像像素点为&#xff08;x0,y0&#xff09;&#xff0c;平移后的图像的像素点为(x,y),x…

Unity游戏内相机(主角头部视角)的旋转问题:“万向节锁定”(Gimbal Lock)

前言&#xff1a; 在Unity中&#xff0c;相机的正前方是Z正半轴&#xff0c;相机的正右方是X正半轴&#xff0c;相机的正上方是Y正半轴。这个很好理解。 现在&#xff0c;我想要相机看向左前上方45&#xff0c;你会觉得要怎么做呢&#xff1f; 如果是我的话&#xff0c;我的第一…

FreeRTOS移植

目录 一、FreeRTOS简介1.1 初识FreeRTOS1.2 FreeRTOS资料获取1.3 开发环境简介 二、FreeRTOS移植2.1 文件添加2.2 keil工程添加2.3 文件修改 一、FreeRTOS简介 1.1 初识FreeRTOS 首先看一下 FreeRTOS 的名字&#xff0c;可以分为两部分&#xff1a;“Free”和“RTOS”&#xf…

MySQL基础篇(三)约束

一、概述 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 分类&#xff1a; 注意&#xff1a;约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加约束。 二…

智能分析网关V4太阳能风光互补远程视频智能监控方案

一、背景需求 在一些偏远地区&#xff0c;也具有视频监控的需求。但是这类场景中&#xff0c;一般无法就近获取市电&#xff0c;如果要长距离拉取市电&#xff0c;建设的成本非常高且长距离传输有安全隐患&#xff0c;因此风光互补远程视频监控方案的需求也较多。利用风光电转化…

MyBatisPlus学习二:常用注解、条件构造器、自定义sql

常用注解 基本约定 MybatisPlus通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库表信息。可以理解为在继承BaseMapper 要指定对应的泛型 public interface UserMapper extends BaseMapper<User> 实体类中&#xff0c;类名驼峰转下划线作为表名、名为id的…

CentOS 7.6下的HTTP隧道代理配置详解

在CentOS 7.6操作系统中&#xff0c;配置HTTP隧道代理需要一定的技术知识和经验。下面我们将详细介绍如何配置HTTP隧道代理&#xff0c;以确保网络通信的安全性和稳定性。 首先&#xff0c;我们需要了解HTTP隧道代理的基本原理。HTTP隧道代理是一种通过HTTP协议传输其他协议数…

Linux习题3

解析&#xff1a; grep&#xff1a;查找文件内的内容 gzip&#xff1a;压缩文件&#xff0c;文件经压缩后会增加 gz&#xff1a;扩展名 find&#xff1a;在指定目录下查找文件 解析&#xff1a; A hosts文件是Linux系统上一个负责ip地址与域名快速解析的文件&#xff0c;以…

gitlab 8.13.0 关闭注册功能

新版本基本都可以在网上找到关闭注册的教程&#xff0c;但是老版本会比较麻烦&#xff0c;可以通过如下路径在网页中设置&#xff08;root 管理员登录&#xff09; ​​​​​​http://ip:port/admin/application_settings 最后保存即可

【go语言】select多路选择

select基础知识 select 是 Go 语言中用于处理通道操作的控制结构&#xff0c;它类似于 switch 语句&#xff0c;但专门用于通道的选择。select 语句使得一个 goroutine 可以等待多个通道操作&#xff0c;当其中任意一个通道操作可以进行时&#xff0c;就会执行相应的 case 分支…

Reids原理及简单命令

目录 1.关系数据库与非关系型数据库 关系型数据库 非关系型数据库 关系型数据库和非关系型数据库区别 数据存储方式不同 扩展方式不同 对事务性的支持不同 总结&#xff1a; 2. Redis简介 什么是reids reids优点 reids使用场景&#xff1a; reids快的原因 Redis数…

功能强大且直观的日程和任务管理工具—Things 3 for Mac

在现代生活中&#xff0c;我们面对着繁忙的日程安排和众多的任务&#xff0c;我们需要一款高效的工具来帮助我们管理和组织这些事务。而事务管理的首选工具&#xff0c;非 Things 3 for Mac 莫属。 Things 3 for Mac 是一款功能强大且直观的日程和任务管理工具。它的设计简洁&…

Vue3——element-plus表格组件怎样得到当前行的id

实现方法&#xff1a; <el-table-column property"address" label"操作" show-overflow-tooltip header-align"center" v-slot"scope"><el-button type"success" click"editBtn(scope.row.id)">编辑…

01-线程池项目背景:C++的数据库操作

从0开始学习C与数据库的联动 1.原始方式-使用MySQL Connector/C 提供的API查询 1.1 数据库预操作 我的本地电脑上有mysql数据库&#xff0c;里面预先创建了一个database名叫chat&#xff0c;用户名root&#xff0c;密码password。 1.2 Visual Studio预操作 在Windows上使用…