text-eraser 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- text_eraser-0.1.0/LICENSE +21 -0
- text_eraser-0.1.0/PKG-INFO +170 -0
- text_eraser-0.1.0/README.md +132 -0
- text_eraser-0.1.0/pyproject.toml +59 -0
- text_eraser-0.1.0/setup.cfg +4 -0
- text_eraser-0.1.0/tests/test_textpatch.py +72 -0
- text_eraser-0.1.0/text_eraser.egg-info/PKG-INFO +170 -0
- text_eraser-0.1.0/text_eraser.egg-info/SOURCES.txt +21 -0
- text_eraser-0.1.0/text_eraser.egg-info/dependency_links.txt +1 -0
- text_eraser-0.1.0/text_eraser.egg-info/entry_points.txt +2 -0
- text_eraser-0.1.0/text_eraser.egg-info/requires.txt +12 -0
- text_eraser-0.1.0/text_eraser.egg-info/top_level.txt +1 -0
- text_eraser-0.1.0/textpatch/__init__.py +32 -0
- text_eraser-0.1.0/textpatch/__main__.py +5 -0
- text_eraser-0.1.0/textpatch/assets/example.png +0 -0
- text_eraser-0.1.0/textpatch/eraser.py +816 -0
- text_eraser-0.1.0/textpatch/ml_text_select.py +320 -0
- text_eraser-0.1.0/textpatch/patch_fill.py +437 -0
- text_eraser-0.1.0/textpatch/static/app.js +572 -0
- text_eraser-0.1.0/textpatch/static/index.html +150 -0
- text_eraser-0.1.0/textpatch/static/style.css +545 -0
- text_eraser-0.1.0/textpatch/text_select.py +1612 -0
- text_eraser-0.1.0/textpatch/webapp.py +468 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lehuaner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: text-eraser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 图片文字擦除工具:DBNet 文字检测 + PatchMatch 内容识别填充 + 去发光,附本地 Web 界面
|
|
5
|
+
Author: lehuaner
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lehuaner/TextPatch
|
|
8
|
+
Project-URL: Repository, https://github.com/lehuaner/TextPatch
|
|
9
|
+
Project-URL: Issues, https://github.com/lehuaner/TextPatch/issues
|
|
10
|
+
Keywords: text-removal,inpainting,patchmatch,dbnet,ocr,image-processing,text-eraser
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Multimedia :: Graphics :: Editors
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: fastapi>=0.110
|
|
27
|
+
Requires-Dist: uvicorn[standard]>=0.27
|
|
28
|
+
Requires-Dist: numpy>=1.26
|
|
29
|
+
Requires-Dist: opencv-python-headless>=4.8
|
|
30
|
+
Requires-Dist: Pillow>=10.0
|
|
31
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
32
|
+
Requires-Dist: onnxruntime>=1.16
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# TextPatch
|
|
40
|
+
|
|
41
|
+
[](https://github.com/lehuaner/TextPatch/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/text-eraser/)
|
|
43
|
+
[](https://pypi.org/project/text-eraser/)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
|
|
46
|
+
**图片文字擦除工具** — DBNet 文字检测 + PatchMatch 内容识别填充,附带去发光(去除绿光/辉光文字的光晕)与本地 Web 界面。
|
|
47
|
+
*Image text removal via DBNet text detection + PatchMatch-based content-aware fill, with glow removal and a local web UI.*
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
## 特性
|
|
52
|
+
|
|
53
|
+
- **一键擦除文字**:DBNet(PP-OCRv4 det ONNX,约 5MB,CPU 推理)定位文字框,框内 Otsu 生成逐像素字形蒙版,PatchMatch 算法用周围背景填充
|
|
54
|
+
- **去发光(deglow)**:绿光/辉光文字的光晕污染背景,普通填充会残留绿斑——v2 方案用 alpha 分解 + 调和场插值恢复干净背景
|
|
55
|
+
- **所见即所得**:「移动边缘」让展示蒙版与真实填充区完全一致,支持自动判定边缘外扩量
|
|
56
|
+
- **纯本地运行**:无 API、无上传,模型首次运行自动下载后完全离线
|
|
57
|
+
- **两种用法**:本地 Web 界面(拖图即擦)或 Python 库调用(`erase_text()` 一步出结果)
|
|
58
|
+
|
|
59
|
+
## 安装
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install text-eraser
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
需要 Python 3.10+。DBNet 模型(约 5MB)在首次使用时自动从 HuggingFace 下载,之后离线可用。
|
|
66
|
+
|
|
67
|
+
> PyPI 发行名为 `text-eraser`(`textpatch` 与既有包名过于相似未获准),Python 导入名是 `textpatch`。
|
|
68
|
+
|
|
69
|
+
从源码运行:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/lehuaner/TextPatch.git
|
|
73
|
+
cd TextPatch
|
|
74
|
+
pip install -e .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 快速上手
|
|
78
|
+
|
|
79
|
+
### Web 界面
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
textpatch # 或 python -m textpatch
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
浏览器打开 <http://127.0.0.1:8765/>,拖入图片即可擦除;支持逐面板查看蒙版/去发光中间结果、调整参数、保留历史记录。
|
|
86
|
+
|
|
87
|
+
- 端口/地址:环境变量 `TEXTPATCH_PORT`(默认 8765)、`TEXTPATCH_HOST`(默认 127.0.0.1)
|
|
88
|
+
- 运行数据目录:`TEXTPATCH_DATA_DIR`(仓库开发用 `data/`,pip 安装默认 `~/.textpatch/data`)
|
|
89
|
+
- 模型缓存目录:`TEXTPATCH_MODEL_DIR`(pip 安装默认 `~/.textpatch/models`)
|
|
90
|
+
|
|
91
|
+
### Python 库调用
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from PIL import Image
|
|
95
|
+
from textpatch import erase_text, to_rgb_uint8
|
|
96
|
+
|
|
97
|
+
rgb = to_rgb_uint8(Image.open("demo.png")) # HxWx3 uint8 RGB
|
|
98
|
+
result, mask, meta = erase_text(rgb, return_mask=True)
|
|
99
|
+
Image.fromarray(result).save("out.png")
|
|
100
|
+
# mask: 255=被擦除的文字; meta: mask_pix / inpaint_seconds / edge_used 等
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
只用填充器(去水印/去任意内容,无需文字检测):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import numpy as np
|
|
107
|
+
from textpatch import inpaint
|
|
108
|
+
|
|
109
|
+
hole = np.zeros(rgb.shape[:2], np.uint8) # >0 = 要清除的区域
|
|
110
|
+
hole[10:40, 20:80] = 255
|
|
111
|
+
filled = inpaint(rgb, hole, sample_mask=255 - hole)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 主要参数(`erase_text`)
|
|
115
|
+
|
|
116
|
+
| 参数 | 默认 | 说明 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `edge` | 1 | 「移动边缘」:蒙版与填充区同步外扩(>0)/收缩(<0)像素 |
|
|
119
|
+
| `auto_edge` | True | 按文字色残留自动判定最小外扩(多数图 1,硬图自动到 2) |
|
|
120
|
+
| `q_off` | 55 | 蒙版紧密度 [30,70],越高越贴字形 |
|
|
121
|
+
| `direction` | None | 纹理方向角度°(木纹/条带类背景) |
|
|
122
|
+
| `deglow_scheme` | "v2" | 去发光方案:"v2" / "off"(无发光图自动零改动) |
|
|
123
|
+
| `max_side` | 960 | DBNet 推理最长边,调大可提升小字召回 |
|
|
124
|
+
|
|
125
|
+
完整函数级 API 见 [docs/ALGORITHM.md](docs/ALGORITHM.md)。
|
|
126
|
+
|
|
127
|
+
## 算法概览
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
原图 RGB
|
|
131
|
+
→ DBNet 文字框 → 框内 Otsu + 纯白补全 → 逐像素文字蒙版
|
|
132
|
+
→ 去发光 v2 (强绿信号门 → alpha 分解恢复背景 → 调和场插值)
|
|
133
|
+
→ 移动边缘 → PatchMatch 填充 (Criminisi 优先级 + 颜色自适应 + TELEA 兜底)
|
|
134
|
+
→ 擦除结果
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- 设计细节与参数速查:[docs/ALGORITHM.md](docs/ALGORITHM.md)
|
|
138
|
+
- 去发光 v4 规格说明:[docs/DEGLOW_V4.md](docs/DEGLOW_V4.md)
|
|
139
|
+
|
|
140
|
+
## 已知限制
|
|
141
|
+
|
|
142
|
+
- **发光文字路径仍在迭代**:绿光/辉光场景经过多轮修复已大幅改善,但个别复杂背景(暖色多弧段等)仍可能有色差残留,持续按实际观感调整中
|
|
143
|
+
- 文字检测依赖 DBNet 召回;极端小字(<8px 高)或严重模糊的字体可能漏检
|
|
144
|
+
- 大图(4K+)CPU 推理约 0.1s/张,填充耗时与蒙版面积成正比
|
|
145
|
+
|
|
146
|
+
## 开发
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install -e .[dev]
|
|
150
|
+
pytest # 合成图测试套件
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
TextPatch/
|
|
155
|
+
├── textpatch/ # 包本体 (检测/蒙版/填充/去发光/Web)
|
|
156
|
+
├── tests/ # 合成图测试 (CI 用, 自足不依赖样图)
|
|
157
|
+
├── docs/
|
|
158
|
+
│ ├── ALGORITHM.md # 函数级算法与参数指南
|
|
159
|
+
│ ├── DEGLOW_V4.md # 去发光 v4 规格说明
|
|
160
|
+
│ ├── assets/ # README 演示图
|
|
161
|
+
│ └── dev/ # 开发日志与专项修复报告
|
|
162
|
+
├── scripts/ # 离线诊断/回归脚本 (见下)
|
|
163
|
+
└── deglow/ # 去发光 v4 实验模块 (不随 pip 包发布)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`scripts/` 下还有一套基于真实样图的回归脚本(`regress_*.py`,样图不入库,需本地自备 `data/` 样图),用于算法调参时的逐位回归验证。
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# TextPatch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/lehuaner/TextPatch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/text-eraser/)
|
|
5
|
+
[](https://pypi.org/project/text-eraser/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**图片文字擦除工具** — DBNet 文字检测 + PatchMatch 内容识别填充,附带去发光(去除绿光/辉光文字的光晕)与本地 Web 界面。
|
|
9
|
+
*Image text removal via DBNet text detection + PatchMatch-based content-aware fill, with glow removal and a local web UI.*
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## 特性
|
|
14
|
+
|
|
15
|
+
- **一键擦除文字**:DBNet(PP-OCRv4 det ONNX,约 5MB,CPU 推理)定位文字框,框内 Otsu 生成逐像素字形蒙版,PatchMatch 算法用周围背景填充
|
|
16
|
+
- **去发光(deglow)**:绿光/辉光文字的光晕污染背景,普通填充会残留绿斑——v2 方案用 alpha 分解 + 调和场插值恢复干净背景
|
|
17
|
+
- **所见即所得**:「移动边缘」让展示蒙版与真实填充区完全一致,支持自动判定边缘外扩量
|
|
18
|
+
- **纯本地运行**:无 API、无上传,模型首次运行自动下载后完全离线
|
|
19
|
+
- **两种用法**:本地 Web 界面(拖图即擦)或 Python 库调用(`erase_text()` 一步出结果)
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install text-eraser
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
需要 Python 3.10+。DBNet 模型(约 5MB)在首次使用时自动从 HuggingFace 下载,之后离线可用。
|
|
28
|
+
|
|
29
|
+
> PyPI 发行名为 `text-eraser`(`textpatch` 与既有包名过于相似未获准),Python 导入名是 `textpatch`。
|
|
30
|
+
|
|
31
|
+
从源码运行:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/lehuaner/TextPatch.git
|
|
35
|
+
cd TextPatch
|
|
36
|
+
pip install -e .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 快速上手
|
|
40
|
+
|
|
41
|
+
### Web 界面
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
textpatch # 或 python -m textpatch
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
浏览器打开 <http://127.0.0.1:8765/>,拖入图片即可擦除;支持逐面板查看蒙版/去发光中间结果、调整参数、保留历史记录。
|
|
48
|
+
|
|
49
|
+
- 端口/地址:环境变量 `TEXTPATCH_PORT`(默认 8765)、`TEXTPATCH_HOST`(默认 127.0.0.1)
|
|
50
|
+
- 运行数据目录:`TEXTPATCH_DATA_DIR`(仓库开发用 `data/`,pip 安装默认 `~/.textpatch/data`)
|
|
51
|
+
- 模型缓存目录:`TEXTPATCH_MODEL_DIR`(pip 安装默认 `~/.textpatch/models`)
|
|
52
|
+
|
|
53
|
+
### Python 库调用
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from PIL import Image
|
|
57
|
+
from textpatch import erase_text, to_rgb_uint8
|
|
58
|
+
|
|
59
|
+
rgb = to_rgb_uint8(Image.open("demo.png")) # HxWx3 uint8 RGB
|
|
60
|
+
result, mask, meta = erase_text(rgb, return_mask=True)
|
|
61
|
+
Image.fromarray(result).save("out.png")
|
|
62
|
+
# mask: 255=被擦除的文字; meta: mask_pix / inpaint_seconds / edge_used 等
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
只用填充器(去水印/去任意内容,无需文字检测):
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import numpy as np
|
|
69
|
+
from textpatch import inpaint
|
|
70
|
+
|
|
71
|
+
hole = np.zeros(rgb.shape[:2], np.uint8) # >0 = 要清除的区域
|
|
72
|
+
hole[10:40, 20:80] = 255
|
|
73
|
+
filled = inpaint(rgb, hole, sample_mask=255 - hole)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 主要参数(`erase_text`)
|
|
77
|
+
|
|
78
|
+
| 参数 | 默认 | 说明 |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `edge` | 1 | 「移动边缘」:蒙版与填充区同步外扩(>0)/收缩(<0)像素 |
|
|
81
|
+
| `auto_edge` | True | 按文字色残留自动判定最小外扩(多数图 1,硬图自动到 2) |
|
|
82
|
+
| `q_off` | 55 | 蒙版紧密度 [30,70],越高越贴字形 |
|
|
83
|
+
| `direction` | None | 纹理方向角度°(木纹/条带类背景) |
|
|
84
|
+
| `deglow_scheme` | "v2" | 去发光方案:"v2" / "off"(无发光图自动零改动) |
|
|
85
|
+
| `max_side` | 960 | DBNet 推理最长边,调大可提升小字召回 |
|
|
86
|
+
|
|
87
|
+
完整函数级 API 见 [docs/ALGORITHM.md](docs/ALGORITHM.md)。
|
|
88
|
+
|
|
89
|
+
## 算法概览
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
原图 RGB
|
|
93
|
+
→ DBNet 文字框 → 框内 Otsu + 纯白补全 → 逐像素文字蒙版
|
|
94
|
+
→ 去发光 v2 (强绿信号门 → alpha 分解恢复背景 → 调和场插值)
|
|
95
|
+
→ 移动边缘 → PatchMatch 填充 (Criminisi 优先级 + 颜色自适应 + TELEA 兜底)
|
|
96
|
+
→ 擦除结果
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- 设计细节与参数速查:[docs/ALGORITHM.md](docs/ALGORITHM.md)
|
|
100
|
+
- 去发光 v4 规格说明:[docs/DEGLOW_V4.md](docs/DEGLOW_V4.md)
|
|
101
|
+
|
|
102
|
+
## 已知限制
|
|
103
|
+
|
|
104
|
+
- **发光文字路径仍在迭代**:绿光/辉光场景经过多轮修复已大幅改善,但个别复杂背景(暖色多弧段等)仍可能有色差残留,持续按实际观感调整中
|
|
105
|
+
- 文字检测依赖 DBNet 召回;极端小字(<8px 高)或严重模糊的字体可能漏检
|
|
106
|
+
- 大图(4K+)CPU 推理约 0.1s/张,填充耗时与蒙版面积成正比
|
|
107
|
+
|
|
108
|
+
## 开发
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install -e .[dev]
|
|
112
|
+
pytest # 合成图测试套件
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
TextPatch/
|
|
117
|
+
├── textpatch/ # 包本体 (检测/蒙版/填充/去发光/Web)
|
|
118
|
+
├── tests/ # 合成图测试 (CI 用, 自足不依赖样图)
|
|
119
|
+
├── docs/
|
|
120
|
+
│ ├── ALGORITHM.md # 函数级算法与参数指南
|
|
121
|
+
│ ├── DEGLOW_V4.md # 去发光 v4 规格说明
|
|
122
|
+
│ ├── assets/ # README 演示图
|
|
123
|
+
│ └── dev/ # 开发日志与专项修复报告
|
|
124
|
+
├── scripts/ # 离线诊断/回归脚本 (见下)
|
|
125
|
+
└── deglow/ # 去发光 v4 实验模块 (不随 pip 包发布)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`scripts/` 下还有一套基于真实样图的回归脚本(`regress_*.py`,样图不入库,需本地自备 `data/` 样图),用于算法调参时的逐位回归验证。
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# PyPI 发行名: text-eraser (textpatch 与既有 text-patch 过于相似被 PyPI 拒绝)
|
|
7
|
+
# import 包名: textpatch
|
|
8
|
+
name = "text-eraser"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
description = "图片文字擦除工具:DBNet 文字检测 + PatchMatch 内容识别填充 + 去发光,附本地 Web 界面"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
authors = [{ name = "lehuaner" }]
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
|
+
keywords = ["text-removal", "inpainting", "patchmatch", "dbnet", "ocr", "image-processing", "text-eraser"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Multimedia :: Graphics :: Editors",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"fastapi>=0.110",
|
|
32
|
+
"uvicorn[standard]>=0.27",
|
|
33
|
+
"numpy>=1.26",
|
|
34
|
+
"opencv-python-headless>=4.8",
|
|
35
|
+
"Pillow>=10.0",
|
|
36
|
+
"python-multipart>=0.0.9",
|
|
37
|
+
"onnxruntime>=1.16",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dev = ["pytest>=8.0", "build>=1.0", "twine>=5.0"]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/lehuaner/TextPatch"
|
|
45
|
+
Repository = "https://github.com/lehuaner/TextPatch"
|
|
46
|
+
Issues = "https://github.com/lehuaner/TextPatch/issues"
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
textpatch = "textpatch.webapp:main"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
include = ["textpatch*"]
|
|
53
|
+
exclude = ["deglow*", "scripts*", "tests*"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-data]
|
|
56
|
+
textpatch = ["static/**/*", "assets/*"]
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""textpatch 包级冒烟测试(全部合成图,不依赖仓库内样图与历史数据)。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _synthetic_rgb(w=320, h=200, seed=7) -> np.ndarray:
|
|
9
|
+
"""带纹理的中性渐变背景,用于填充/蒙版测试。"""
|
|
10
|
+
rng = np.random.default_rng(seed)
|
|
11
|
+
yy, xx = np.mgrid[0:h, 0:w].astype(np.float32)
|
|
12
|
+
t = (xx / w * 0.6 + yy / h * 0.4)
|
|
13
|
+
base = np.stack([110 + 40 * t, 108 + 39 * t, 104 + 37 * t], axis=-1)
|
|
14
|
+
noise = rng.normal(0, 3.5, (h, w, 1)).repeat(3, axis=2)
|
|
15
|
+
return np.clip(base + noise, 0, 255).astype(np.uint8)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_version_and_exports():
|
|
19
|
+
import textpatch
|
|
20
|
+
assert textpatch.__version__
|
|
21
|
+
for name in textpatch.__all__:
|
|
22
|
+
assert hasattr(textpatch, name), name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_patch_fill_inpaints_hole():
|
|
26
|
+
"""纯色洞应被周围纹理背景填回,不残留黑洞/亮斑。"""
|
|
27
|
+
rgb = _synthetic_rgb()
|
|
28
|
+
hole = np.zeros(rgb.shape[:2], np.uint8)
|
|
29
|
+
hole[80:120, 120:200] = 255 # 中央 40x80 洞, 填 0(黑)
|
|
30
|
+
rgb_hole = rgb.copy()
|
|
31
|
+
rgb_hole[hole > 0] = (0, 0, 0)
|
|
32
|
+
|
|
33
|
+
from textpatch import inpaint
|
|
34
|
+
filled = inpaint(rgb_hole, hole, sample_mask=(255 - hole))
|
|
35
|
+
|
|
36
|
+
center = filled[95:105, 150:170].astype(float)
|
|
37
|
+
ring = rgb[95:105, 150:170].astype(float) # 原图同位置 ≈ 真背景
|
|
38
|
+
assert abs(center.mean() - ring.mean()) < 12.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_detect_text_mask_classic_synthetic():
|
|
42
|
+
"""经典路径:合成图上大块非文字区域不应被当成文字。"""
|
|
43
|
+
from textpatch import detect_text_mask
|
|
44
|
+
rgb = _synthetic_rgb()
|
|
45
|
+
mask, _ = detect_text_mask(rgb, method="classic",
|
|
46
|
+
max_area_ratio=0.40, max_box_ratio=0.40)
|
|
47
|
+
assert mask.shape == rgb.shape[:2]
|
|
48
|
+
assert mask.dtype == np.uint8
|
|
49
|
+
# 纯纹理背景无文字 → 蒙版应接近空
|
|
50
|
+
assert mask.mean() < 1.0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_erase_text_end_to_end():
|
|
54
|
+
"""全流程冒烟:中央大字块被擦除且不残留白色(ml 路径,模型缺失则跳过)。"""
|
|
55
|
+
pytest.importorskip("onnxruntime")
|
|
56
|
+
from textpatch import erase_text
|
|
57
|
+
try:
|
|
58
|
+
from textpatch.ml_text_select import get_model_path
|
|
59
|
+
get_model_path()
|
|
60
|
+
except Exception as e: # 离线/网络受限环境: 跳过而非失败
|
|
61
|
+
pytest.skip(f"DBNet model unavailable: {e}")
|
|
62
|
+
|
|
63
|
+
rgb = _synthetic_rgb(w=480, h=280)
|
|
64
|
+
# 画一个粗白字块("口"形方框, 结构简单且 DBNet 稳定可检)
|
|
65
|
+
rgb[100:180, 190:290] = (250, 250, 250)
|
|
66
|
+
rgb[110:170, 200:280] = _synthetic_rgb(w=80, h=60, seed=9)[20]
|
|
67
|
+
|
|
68
|
+
result, mask, meta = erase_text(rgb, return_mask=True)
|
|
69
|
+
assert mask.any(), "文字未被检出"
|
|
70
|
+
# 原白框区域不应残留大片纯白
|
|
71
|
+
resid = (result[100:180, 190:290].min(axis=-1) > 235).mean()
|
|
72
|
+
assert resid < 0.05, f"白框残留 {resid:.2%}"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: text-eraser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 图片文字擦除工具:DBNet 文字检测 + PatchMatch 内容识别填充 + 去发光,附本地 Web 界面
|
|
5
|
+
Author: lehuaner
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lehuaner/TextPatch
|
|
8
|
+
Project-URL: Repository, https://github.com/lehuaner/TextPatch
|
|
9
|
+
Project-URL: Issues, https://github.com/lehuaner/TextPatch/issues
|
|
10
|
+
Keywords: text-removal,inpainting,patchmatch,dbnet,ocr,image-processing,text-eraser
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Multimedia :: Graphics :: Editors
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: fastapi>=0.110
|
|
27
|
+
Requires-Dist: uvicorn[standard]>=0.27
|
|
28
|
+
Requires-Dist: numpy>=1.26
|
|
29
|
+
Requires-Dist: opencv-python-headless>=4.8
|
|
30
|
+
Requires-Dist: Pillow>=10.0
|
|
31
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
32
|
+
Requires-Dist: onnxruntime>=1.16
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# TextPatch
|
|
40
|
+
|
|
41
|
+
[](https://github.com/lehuaner/TextPatch/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/text-eraser/)
|
|
43
|
+
[](https://pypi.org/project/text-eraser/)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
|
|
46
|
+
**图片文字擦除工具** — DBNet 文字检测 + PatchMatch 内容识别填充,附带去发光(去除绿光/辉光文字的光晕)与本地 Web 界面。
|
|
47
|
+
*Image text removal via DBNet text detection + PatchMatch-based content-aware fill, with glow removal and a local web UI.*
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
## 特性
|
|
52
|
+
|
|
53
|
+
- **一键擦除文字**:DBNet(PP-OCRv4 det ONNX,约 5MB,CPU 推理)定位文字框,框内 Otsu 生成逐像素字形蒙版,PatchMatch 算法用周围背景填充
|
|
54
|
+
- **去发光(deglow)**:绿光/辉光文字的光晕污染背景,普通填充会残留绿斑——v2 方案用 alpha 分解 + 调和场插值恢复干净背景
|
|
55
|
+
- **所见即所得**:「移动边缘」让展示蒙版与真实填充区完全一致,支持自动判定边缘外扩量
|
|
56
|
+
- **纯本地运行**:无 API、无上传,模型首次运行自动下载后完全离线
|
|
57
|
+
- **两种用法**:本地 Web 界面(拖图即擦)或 Python 库调用(`erase_text()` 一步出结果)
|
|
58
|
+
|
|
59
|
+
## 安装
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install text-eraser
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
需要 Python 3.10+。DBNet 模型(约 5MB)在首次使用时自动从 HuggingFace 下载,之后离线可用。
|
|
66
|
+
|
|
67
|
+
> PyPI 发行名为 `text-eraser`(`textpatch` 与既有包名过于相似未获准),Python 导入名是 `textpatch`。
|
|
68
|
+
|
|
69
|
+
从源码运行:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/lehuaner/TextPatch.git
|
|
73
|
+
cd TextPatch
|
|
74
|
+
pip install -e .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 快速上手
|
|
78
|
+
|
|
79
|
+
### Web 界面
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
textpatch # 或 python -m textpatch
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
浏览器打开 <http://127.0.0.1:8765/>,拖入图片即可擦除;支持逐面板查看蒙版/去发光中间结果、调整参数、保留历史记录。
|
|
86
|
+
|
|
87
|
+
- 端口/地址:环境变量 `TEXTPATCH_PORT`(默认 8765)、`TEXTPATCH_HOST`(默认 127.0.0.1)
|
|
88
|
+
- 运行数据目录:`TEXTPATCH_DATA_DIR`(仓库开发用 `data/`,pip 安装默认 `~/.textpatch/data`)
|
|
89
|
+
- 模型缓存目录:`TEXTPATCH_MODEL_DIR`(pip 安装默认 `~/.textpatch/models`)
|
|
90
|
+
|
|
91
|
+
### Python 库调用
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from PIL import Image
|
|
95
|
+
from textpatch import erase_text, to_rgb_uint8
|
|
96
|
+
|
|
97
|
+
rgb = to_rgb_uint8(Image.open("demo.png")) # HxWx3 uint8 RGB
|
|
98
|
+
result, mask, meta = erase_text(rgb, return_mask=True)
|
|
99
|
+
Image.fromarray(result).save("out.png")
|
|
100
|
+
# mask: 255=被擦除的文字; meta: mask_pix / inpaint_seconds / edge_used 等
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
只用填充器(去水印/去任意内容,无需文字检测):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import numpy as np
|
|
107
|
+
from textpatch import inpaint
|
|
108
|
+
|
|
109
|
+
hole = np.zeros(rgb.shape[:2], np.uint8) # >0 = 要清除的区域
|
|
110
|
+
hole[10:40, 20:80] = 255
|
|
111
|
+
filled = inpaint(rgb, hole, sample_mask=255 - hole)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 主要参数(`erase_text`)
|
|
115
|
+
|
|
116
|
+
| 参数 | 默认 | 说明 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `edge` | 1 | 「移动边缘」:蒙版与填充区同步外扩(>0)/收缩(<0)像素 |
|
|
119
|
+
| `auto_edge` | True | 按文字色残留自动判定最小外扩(多数图 1,硬图自动到 2) |
|
|
120
|
+
| `q_off` | 55 | 蒙版紧密度 [30,70],越高越贴字形 |
|
|
121
|
+
| `direction` | None | 纹理方向角度°(木纹/条带类背景) |
|
|
122
|
+
| `deglow_scheme` | "v2" | 去发光方案:"v2" / "off"(无发光图自动零改动) |
|
|
123
|
+
| `max_side` | 960 | DBNet 推理最长边,调大可提升小字召回 |
|
|
124
|
+
|
|
125
|
+
完整函数级 API 见 [docs/ALGORITHM.md](docs/ALGORITHM.md)。
|
|
126
|
+
|
|
127
|
+
## 算法概览
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
原图 RGB
|
|
131
|
+
→ DBNet 文字框 → 框内 Otsu + 纯白补全 → 逐像素文字蒙版
|
|
132
|
+
→ 去发光 v2 (强绿信号门 → alpha 分解恢复背景 → 调和场插值)
|
|
133
|
+
→ 移动边缘 → PatchMatch 填充 (Criminisi 优先级 + 颜色自适应 + TELEA 兜底)
|
|
134
|
+
→ 擦除结果
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- 设计细节与参数速查:[docs/ALGORITHM.md](docs/ALGORITHM.md)
|
|
138
|
+
- 去发光 v4 规格说明:[docs/DEGLOW_V4.md](docs/DEGLOW_V4.md)
|
|
139
|
+
|
|
140
|
+
## 已知限制
|
|
141
|
+
|
|
142
|
+
- **发光文字路径仍在迭代**:绿光/辉光场景经过多轮修复已大幅改善,但个别复杂背景(暖色多弧段等)仍可能有色差残留,持续按实际观感调整中
|
|
143
|
+
- 文字检测依赖 DBNet 召回;极端小字(<8px 高)或严重模糊的字体可能漏检
|
|
144
|
+
- 大图(4K+)CPU 推理约 0.1s/张,填充耗时与蒙版面积成正比
|
|
145
|
+
|
|
146
|
+
## 开发
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install -e .[dev]
|
|
150
|
+
pytest # 合成图测试套件
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
TextPatch/
|
|
155
|
+
├── textpatch/ # 包本体 (检测/蒙版/填充/去发光/Web)
|
|
156
|
+
├── tests/ # 合成图测试 (CI 用, 自足不依赖样图)
|
|
157
|
+
├── docs/
|
|
158
|
+
│ ├── ALGORITHM.md # 函数级算法与参数指南
|
|
159
|
+
│ ├── DEGLOW_V4.md # 去发光 v4 规格说明
|
|
160
|
+
│ ├── assets/ # README 演示图
|
|
161
|
+
│ └── dev/ # 开发日志与专项修复报告
|
|
162
|
+
├── scripts/ # 离线诊断/回归脚本 (见下)
|
|
163
|
+
└── deglow/ # 去发光 v4 实验模块 (不随 pip 包发布)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`scripts/` 下还有一套基于真实样图的回归脚本(`regress_*.py`,样图不入库,需本地自备 `data/` 样图),用于算法调参时的逐位回归验证。
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
tests/test_textpatch.py
|
|
5
|
+
text_eraser.egg-info/PKG-INFO
|
|
6
|
+
text_eraser.egg-info/SOURCES.txt
|
|
7
|
+
text_eraser.egg-info/dependency_links.txt
|
|
8
|
+
text_eraser.egg-info/entry_points.txt
|
|
9
|
+
text_eraser.egg-info/requires.txt
|
|
10
|
+
text_eraser.egg-info/top_level.txt
|
|
11
|
+
textpatch/__init__.py
|
|
12
|
+
textpatch/__main__.py
|
|
13
|
+
textpatch/eraser.py
|
|
14
|
+
textpatch/ml_text_select.py
|
|
15
|
+
textpatch/patch_fill.py
|
|
16
|
+
textpatch/text_select.py
|
|
17
|
+
textpatch/webapp.py
|
|
18
|
+
textpatch/assets/example.png
|
|
19
|
+
textpatch/static/app.js
|
|
20
|
+
textpatch/static/index.html
|
|
21
|
+
textpatch/static/style.css
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
textpatch
|