间隙树排序算法的可视化

做文档翻译的OCR程序时,会遇到这样一个场景,因为通常OCR模型的输出都是按文本块逐行返回,当结果进入翻译模型时会丢失行与行之间的信息。为了解决这个问题,需要对OCR结果进行进一步的版面分析,将文本块合并成段落,再输入到翻译模型中去。

算法

间隙·树·排序算法

参考链接:https://github.com/hiroi-sora/GapTree_Sort_Algorithm

算法主要对文本块按间隙进行划分,再经过树形排序,将底层OCR的输出结果从子文本块转化成段落文本块。

算法重构

为了使文本块的定位更加准确,使用原算法输出段落块(content_blocks)的上下左右四个边界点构成新段落块(new_blocks)

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def structure_ocr(self):
for line, tb in enumerate(self.blocks_data):
tb["bbox"] = self.bboxes[line]

gtree = GapTree(lambda tb: tb["bbox"])
sorted_text_blocks = gtree.sort(self.blocks_data) # 文本块排序
# print(sorted_text_blocks)
pp = ParagraphParse(self.get_info, self.set_end)
# 获取所有区块的文本块
nodes_text_blocks = gtree.get_nodes_text_blocks()
content_blocks = []
for tbs in nodes_text_blocks:
content = ""
tbs = pp.run(tbs) # 预测结尾分隔符
for tb in tbs: # 输出文本和结尾分隔符
content += tb["text"] + tb["end"]
content_blocks.append(content)

node_tbs = []
for node in gtree.current_nodes:
if not node["units"]:
continue # 跳过没有块的根节点
x0 = node["x_left"]
x1 = node["x_right"]
y0 = gtree.current_rows[node["r_top"]][0][0][1]
y1 = gtree.current_rows[node["r_bottom"]][0][0][3]
node_tbs.append([[x0, y0], [x1, y0], [x1, y1], [x0, y1]])

return node_tbs, content_blocks

Read More

Surya OCR

环境准备

版本:

python3.9 + surya-ocr 0.4.15

模型准备:

检测模型:surya_det3

识别模型:surya_rec

版面模型:surya_layout3

源码修改

因首次使用下载模型被墙,提前将模型收录至模型文件夹并修改源码导入部分:

(源码位置:...Python39/Lib/site-packages/surya/settings.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from typing import Dict, Optional

from dotenv import find_dotenv
from pydantic import computed_field
from pydantic_settings import BaseSettings
import torch
import os


class Settings(BaseSettings):
# General
TORCH_DEVICE: Optional[str] = None
IMAGE_DPI: int = 96
IN_STREAMLIT: bool = False # Whether we're running in streamlit

# Paths
DATA_DIR: str = "data"
RESULT_DIR: str = "results"
BASE_DIR: str = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FONT_DIR: str = os.path.join(BASE_DIR, "static", "fonts")

@computed_field
def TORCH_DEVICE_MODEL(self) -> str:
if self.TORCH_DEVICE is not None:
return self.TORCH_DEVICE

if torch.cuda.is_available():
return "cuda"

if torch.backends.mps.is_available():
return "mps"

return "cpu"

# Text detection
DETECTOR_BATCH_SIZE: Optional[int] = None # Defaults to 2 for CPU/MPS, 32 otherwise
DETECTOR_MODEL_CHECKPOINT: str = r"D:\pycharmproject_2\translate_plat\surya_ocr\models\surya_det3"
DETECTOR_BENCH_DATASET_NAME: str = "vikp/doclaynet_bench"
DETECTOR_IMAGE_CHUNK_HEIGHT: int = 1400 # Height at which to slice images vertically
DETECTOR_TEXT_THRESHOLD: float = 0.6 # Threshold for text detection (above this is considered text)
DETECTOR_BLANK_THRESHOLD: float = 0.35 # Threshold for blank space (below this is considered blank)
DETECTOR_POSTPROCESSING_CPU_WORKERS: int = min(8, os.cpu_count()) # Number of workers for postprocessing
DETECTOR_MIN_PARALLEL_THRESH: int = 3 # Minimum number of images before we parallelize

# Text recognition
RECOGNITION_MODEL_CHECKPOINT: str = r"D:\pycharmproject_2\translate_plat\surya_ocr\models\surya_rec"
RECOGNITION_MAX_TOKENS: int = 175
RECOGNITION_BATCH_SIZE: Optional[int] = None # Defaults to 8 for CPU/MPS, 256 otherwise
RECOGNITION_IMAGE_SIZE: Dict = {"height": 196, "width": 896}
RECOGNITION_RENDER_FONTS: Dict[str, str] = {
"all": os.path.join(FONT_DIR, "GoNotoCurrent-Regular.ttf"),
"zh": os.path.join(FONT_DIR, "GoNotoCJKCore.ttf"),
"ja": os.path.join(FONT_DIR, "GoNotoCJKCore.ttf"),
"ko": os.path.join(FONT_DIR, "GoNotoCJKCore.ttf"),
}
RECOGNITION_FONT_DL_BASE: str = "https://github.com/satbyy/go-noto-universal/releases/download/v7.0"
RECOGNITION_BENCH_DATASET_NAME: str = "vikp/rec_bench"
RECOGNITION_PAD_VALUE: int = 255 # Should be 0 or 255
RECOGNITION_STATIC_CACHE: bool = False # Static cache for torch compile
RECOGNITION_MAX_LANGS: int = 4

# Layout
LAYOUT_MODEL_CHECKPOINT: str = r"D:\pycharmproject_2\translate_plat\surya_ocr\models\surya_layout3"
LAYOUT_BENCH_DATASET_NAME: str = "vikp/publaynet_bench"

# Ordering
ORDER_MODEL_CHECKPOINT: str = "vikp/surya_order"
ORDER_IMAGE_SIZE: Dict = {"height": 1024, "width": 1024}
ORDER_MAX_BOXES: int = 256
ORDER_BATCH_SIZE: Optional[int] = None # Defaults to 4 for CPU/MPS, 32 otherwise
ORDER_BENCH_DATASET_NAME: str = "vikp/order_bench"

# Tesseract (for benchmarks only)
TESSDATA_PREFIX: Optional[str] = None

@computed_field
@property
def MODEL_DTYPE(self) -> torch.dtype:
return torch.float32 if self.TORCH_DEVICE_MODEL == "cpu" else torch.float16

class Config:
env_file = find_dotenv("local.env")
extra = "ignore"


settings = Settings()

Read More

doccano

Doccano是一种用于文本标注的开源工具,旨在简化和加速标注任务的进行。它提供了一个直观的用户界面,使标注人员能够轻松地对文本数据进行标注,并创建高质量的训练数据集用于机器学习和自然语言处理任务。

链接:https://github.com/doccano/doccano

一、安装部署

环境

操作系统:Centos7.9

python:3.10

doccano:1.6.2

pip安装

:百度源没有相应安装包

pip install doccano==1.6.2 -i https://pypi.tuna.tsinghua.edu.cn/simple

初始化

doccano init

设置超级管理员账号密码

doccano createuser --username admin --password 123456

启动服务

doccano webserver --port 8000

Read More

Bert+GRU地址归一算法

一、 算法简介

本地址归一算法(已经下简称算法)旨在对输入文本中出现的地址信息或一般地址信息做地址结构化抽取,并输出该地址映射到数据库中的标准化地址。

二、算法模块

算法由以下不同模块共同组成,各模块在算法的各个生命周期起到重要作用:

1、建立原始地址库

使用postgres数据库(以下简pg)创建存储全国各级原始地址的原始地址库。通过网络爬虫不断采集和更新地址数据(主要来源为高德地图),并存储到原始地址库,地址库包括地址的名称信息、poi信息、类别信息、经纬度信息等原始内容,为后续工作的开展做数据支撑。

2、文本地址抽取

使用UIE(Universal Information Extraction)框架,结合ERNIE3.0模型,使模型具备从无结构或半结构的文本中抽取地址信息的能力。

3、地址分级算法

i)分级标准

首先需要确定一套地址的分级标准细节,本算法采用的分级标准基于阿里《文地址要素解析标注规范》,并做一定程度范围的修改,将地址分为18个不同级别:

① Prov:省级行政区划,省、自治区、直辖市

② City:地级行政区划,地级市、地区、自治州等

③ District:县级行政区划,市辖区、县级市、县等

④ Devzone:广义的上的开发区,包含一般性产业 园区、度假区

⑤ Town:乡级行政区划,镇、街道、乡等

⑥ Community:包含社区、行政村(生产大队、村委会),自然村

⑦ Village Group:限定 xx 组、xx 队、xx 社

⑧ Road:有正式名称的道路,包括隧道、高架、街、弄、巷等。 步行街、商业街

⑨ Roadno:路牌号

⑩ Poi:目标兴趣点

⑪ Subpoi:目标兴趣点的子兴趣点

⑫ Houseno:楼栋号,农村地址的门牌号(包括类似南楼、北楼一类的描述)

⑬ Cellno:单元号,包括甲乙丙丁等

⑭ Floorno:楼层号

⑮ Roomno:房间号

⑯ Assist:定位词,包括方位、解释性名词

⑰ Intersection:路桥交叉口、交汇处、十字路口等

⑱ Distance:距离

ii)算法细节

从爬虫获取的原始地址库构建地址结构化数据集,并对数据集划分为训练集和验证集进行数据标注。标注方式采用B-I-E-O-S五位序列标注法,该标注法将尽可能的保留被标注地址的分级信息。

构建地址分级模型。使用基于多头注意力机制Transformers架构的BERT大模型作为预训练模型,并将模型结合CRF、GRU算法。CRF:全称为条件随机场(Conditional Random Fields),结合了最大熵模型和隐马尔可夫模型的特点,是一种无向图模型。它在序列标注任务如分词、词性标注和命名实体识别等方面取得了很好的效果;GRU:全称为门控循环单元(Gated Recurrent Unit),是一种常用于序列数据建模的神经网络模型,能够很好解决BERT循环神经网络中的长期依赖问题,捕获序列中的长期特征,避免训练过程中的梯度消失和梯度爆炸,使完成的模型结构具有更优秀的泛化能力、更好地拟合真实地址数据。

Read More

yolov8-pose:关键点姿态检测

环境&安装

同上文yolov8:火灾检测

模型使用yolov8n-pose

数据标注

标注工具:labelme

对图像中的目标(人物)及其关键点进行标记,包括1个目标类别和17个关键点类别

数据格式转换

将labelme数据格式转为yolo格式,通用转换代码:

1
2
3
# TODO:
# 参考yolov8-火灾检测,未完待续
...

创建训练yaml文件

参考yolov8n-pose.yaml

1
2
3
4
5
6
7
8
9
10
11
12
train: /exp/work/video/yolov8/datasets/human-pose/images/train #训练集文件夹
val: /exp/work/video/yolov8/datasets/human-pose/images/val # 验证集文件夹
test: /exp/work/video/yolov8/datasets/human-pose/images/val # 测试集文件夹
nc: 1 # 分类数

# 关键点,每个关键点有 X Y 是否可见 三个参数
# 可见性:2-可见不遮挡 1-遮挡 0-没有点
kpt_shape: [17, 3]

# 框的类别(对于关键点检测,只有一类)
names:
0: people

Read More

yolov8-火灾检测

环境

GPU

  • NVIDIA 3090*2
  • 显卡驱动 535.104.05
  • CUDA版本 12.2
  • CUDAtoolkit (cuda_12.2.2_535.104.05_linux)
  • cuDNN (v8.9.7)

yolo版本

  • v8.1.5 (ultralytics yolov8)

pytorch版本

  • v2.1.2

python环境

  • CentOS7.9
  • anaconda3
  • python3.9

安装

源码主页:https://github.com/ultralytics/ultralytics

官方文档:https://docs.ultralytics.com/zh

克隆源码

1
git clone https://hub.nuaa.cf/ultralytics/ultralytics.git

安装依赖

1
pip install pip install ultralytics -i https://mirror.baidu.com/pypi/simple

环境验证

python

1
2
import ultralytics
ultralytics.checks()

cli

1
yolo predict model=yolov8n.pt source=ultralytics/assets/zidane.jpg

执行完毕后得到输出的结果如下:

1
2
3
4
5
6
7
8
(py39_yolov8) [root@jdz yolov8]# yolo predict model=yolov8n.pt source=ultralytics/assets/zidane.jpg 
Ultralytics YOLOv8.1.5 🚀 Python-3.9.18 torch-2.1.2+cu121 CUDA:0 (NVIDIA GeForce RTX 3090, 24260MiB)
YOLOv8n summary (fused): 168 layers, 3151904 parameters, 0 gradients, 8.7 GFLOPs

image 1/1 /exp/work/video/yolov8/ultralytics/assets/zidane.jpg: 384x640 2 persons, 1 tie, 216.9ms
Speed: 7.3ms preprocess, 216.9ms inference, 762.4ms postprocess per image at shape (1, 3, 384, 640)
Results saved to runs/detect/predict
💡 Learn more at https://docs.ultralytics.com/modes/predict

将在Results saved to runs/detect/predict目录下找到输出结果

Read More

NetworkX: 图论算法应用

NetworkX

NetworkX是一款Python的软件包,用于创造、操作复杂网络,以及学习复杂网络的结构、动力学及其功能。有了NetworkX就可以用标准或者不标准的数据格式加载或者存储网络,它可以产生许多种类的随机网络或经典网络,也可以分析网络结构、建立网络模型、设计新的网络算法、绘制网络等

参考文献地址: https://www.osgeo.cn/networkx/reference/index.html

图计算应用方式比较

1.nebula + spark

依赖nebula-spark-connector包、nebula-algorithm包和spark集群的数据读取、图计算方式

2.clickhouse + NetworkX

由于nebula-algorithm依赖spark集群,且nebula-console原生的数据读取能力不佳,在环境受限且计算量有限的情况下优先考虑跳过spark集群和nebula图库,采用clickhouse + NetworkX的图计算方式,其中clickhouse是存储了nebula源数据的列式分布式表,作用类似于方法1中将nebula集群数据通过nebula-spark-connector包导入为spark-DataFrame,仅用做数据读取,再通过将数据转化为NetworkX的图结构进行图计算

Read More

基于VGG16神经网络实现以图搜图

思路

· 预先准备一份图片库,并对其中数据进行批处理操作,使用VGG16卷积神经网络提取图像的512维卷积特征,刷入数据库(ClickHouse)记录;

· 上传目标图像进行识图,同样使用VGG16提取目标图像特征,使用CK数据库距离函数进行匹配,高于阈值即可返回识图结果

神经网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# -*- coding: utf-8 -*-
# @Author : tianL.R
# @Email : rtl1312@163.com
# @Time : 2023.11.26
import time

import numpy as np
from PIL import Image
from keras.applications.vgg16 import VGG16
from keras.applications.vgg16 import preprocess_input
from keras.preprocessing import image
from numpy import linalg


class VGG16Net:
def __init__(self):
self.input_shape = (224, 224, 3)
self.weight = 'imagenet'
self.pooling = 'max'
self.model_vgg = VGG16(weights=self.weight,
input_shape=(self.input_shape[0], self.input_shape[1], self.input_shape[2],),
pooling=self.pooling,
include_top=False)
self.model_vgg.predict(np.zeros((1, 224, 224, 3)))

def detection(self, img_path):
"""
提取VGG16最后一层卷积特征
"""
# img = image.load_img(img_path, target_size=(self.input_shape[0], self.input_shape[1]))
img = img_path.resize((self.input_shape[0], self.input_shape[1]))
img = image.img_to_array(img)
img = np.expand_dims(img, axis=0)
img = preprocess_input(img)
feat = self.model_vgg.predict(img)
norm_feat = feat[0] / linalg.norm(feat[0])
return norm_feat.tolist()


if __name__ == '__main__':
img1 = '333.jpg'
img2 = '555.jpg'
img1 = Image.open(img1)
img2 = Image.open(img2)

vgg = VGG16Net()
queryVec1 = np.array(vgg.detection(img1))
queryVec2 = np.array(vgg.detection(img2))
scores = np.dot(queryVec1, queryVec2)
score2 = queryVec1.dot(queryVec2) / (np.linalg.norm(queryVec1) * np.linalg.norm(queryVec2))
print(scores)
print(score2)

Read More


Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2026 青域 All Rights Reserved.

UV : | PV :