mtt-converter 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.
- mtt_converter-0.1.0/.gitignore +224 -0
- mtt_converter-0.1.0/.python-version +1 -0
- mtt_converter-0.1.0/LICENSE +21 -0
- mtt_converter-0.1.0/PKG-INFO +112 -0
- mtt_converter-0.1.0/README.md +97 -0
- mtt_converter-0.1.0/main.py +6 -0
- mtt_converter-0.1.0/pyproject.toml +22 -0
- mtt_converter-0.1.0/src/mtt/__init__.py +1 -0
- mtt_converter-0.1.0/src/mtt/converter.py +96 -0
- mtt_converter-0.1.0/src/mtt/service/__init__.py +0 -0
- mtt_converter-0.1.0/src/mtt/service/caption_explaner/__init__.py +1 -0
- mtt_converter-0.1.0/src/mtt/service/caption_explaner/service.py +15 -0
- mtt_converter-0.1.0/src/mtt/service/docling/__init__.py +3 -0
- mtt_converter-0.1.0/src/mtt/service/docling/service.py +20 -0
- mtt_converter-0.1.0/src/mtt/service/mesh_llm/__init__.py +3 -0
- mtt_converter-0.1.0/src/mtt/service/mesh_llm/service.py +29 -0
- mtt_converter-0.1.0/src/mtt/types.py +11 -0
- mtt_converter-0.1.0/src/mtt/utils/__init__.py +0 -0
- mtt_converter-0.1.0/src/mtt/utils/caption_utils.py +84 -0
- mtt_converter-0.1.0/src/mtt/utils/docling_utils.py +18 -0
- mtt_converter-0.1.0/src/mtt/utils/mesh_llm_utils.py +167 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/document_tools.py +8 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/image_tools.py +10 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/mesh_geometry.py +113 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/obj_parser.py +37 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/ply_parser.py +127 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/stl_parser.py +156 -0
- mtt_converter-0.1.0/src/mtt/utils/tools/threemf_parser.py +52 -0
- mtt_converter-0.1.0/uv.lock +2897 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# Claude Code
|
|
221
|
+
.claude
|
|
222
|
+
|
|
223
|
+
# In test
|
|
224
|
+
.test_resource
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KIMJUNHEE9339
|
|
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,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mtt-converter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: anthropic>=0.117.1
|
|
8
|
+
Requires-Dist: beautifulsoup4>=4.15.0
|
|
9
|
+
Requires-Dist: docling[rapidocr]>=2.114.0
|
|
10
|
+
Requires-Dist: google-genai>=2.12.1
|
|
11
|
+
Requires-Dist: markdown>=3.10.2
|
|
12
|
+
Requires-Dist: openai>=2.46.0
|
|
13
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# mtt-converter
|
|
17
|
+
|
|
18
|
+
multiform data to text converter — 문서, 이미지, 3D 모델 등 다양한 형태의 데이터를 텍스트로 변환해주는 멀티모달 파이썬 라이브러리.
|
|
19
|
+
|
|
20
|
+
## 설치
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install mtt-converter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Python 3.12 이상이 필요합니다.
|
|
27
|
+
|
|
28
|
+
## 빠른 시작
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from mtt import MultiModalConverter
|
|
32
|
+
|
|
33
|
+
mc = MultiModalConverter()
|
|
34
|
+
|
|
35
|
+
# 확장자를 보고 mode를 자동으로 추정
|
|
36
|
+
text = mc.convert("report.pdf")
|
|
37
|
+
|
|
38
|
+
# mode를 직접 지정
|
|
39
|
+
text = mc.convert("photo.png", mode="ocr")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`mode`를 넘기지 않으면 파일 확장자로 자동 추정하며, 이때 경고 메시지가 출력됩니다.
|
|
43
|
+
|
|
44
|
+
## 지원하는 mode
|
|
45
|
+
|
|
46
|
+
| mode | 설명 | 지원 확장자 |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `doc` | 문서를 텍스트로 변환 ([docling](https://github.com/docling-project/docling) 사용) | `.pdf` `.doc` `.docx` `.ppt` `.pptx` `.xls` `.xlsx` `.html` `.htm` `.md` `.csv` |
|
|
49
|
+
| `ocr` | 이미지 속 텍스트를 OCR로 추출 (docling 사용) | 직접 호출 시 임의의 이미지 파일 |
|
|
50
|
+
| `caption` | 이미지 내용을 멀티모달 LLM으로 캡셔닝/설명 | `.jpg` `.jpeg` `.tif` `.tiff` `.png` `.bmp` |
|
|
51
|
+
| `3dmodel` | 3D 모델 파일을 파싱해 지오메트리 요약(바운딩 박스, 표면적, 부피 등)을 텍스트로 반환 | `.stl` `.obj` `.ply` `.3mf` |
|
|
52
|
+
|
|
53
|
+
> `.glb`도 `3dmodel`로 추정되지만 아직 파서가 구현되어 있지 않습니다.
|
|
54
|
+
|
|
55
|
+
## 사용 예시
|
|
56
|
+
|
|
57
|
+
### 문서 → 텍스트
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
mc.convert("report.pdf", mode="doc")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 이미지 OCR
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
mc.convert("scanned_page.png", mode="ocr")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 이미지 캡셔닝
|
|
70
|
+
|
|
71
|
+
Gemini, GPT-4o, Claude 중 하나로 이미지를 설명합니다. 사용할 모델과 API 키는 `MultiModalConverter` 생성 시 지정합니다.
|
|
72
|
+
* **Token cost**가 발생할 수 있습니다.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
mc = MultiModalConverter(model_name="gemini-3.5-flash", api_key="...")
|
|
76
|
+
mc.convert("photo.jpg", mode="caption", sub_prompt="이 사진을 한국어로 설명해줘.")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `model_name`: `"gemini-*"` / `"gpt-4o"` / `"claude-*"` 중 하나의 실제 API 모델 id (기본값 `"gemini-3.5-flash"`)
|
|
80
|
+
- `api_key`를 넘기지 않으면 각 provider SDK가 환경 변수(`GOOGLE_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)에서 자동으로 읽습니다.
|
|
81
|
+
- `sub_prompt`를 지정하지 않으면 기본 프롬프트(`"Describe image in detail."`)가 사용됩니다.
|
|
82
|
+
|
|
83
|
+
### 3D 모델 파싱
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
mc.convert("model.stl", mode="3dmodel")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
솔리드 이름, 삼각형 개수, 바운딩 박스, 표면적, 부피(근사) 등을 요약한 텍스트를 반환합니다.
|
|
90
|
+
|
|
91
|
+
## 출력 형식
|
|
92
|
+
|
|
93
|
+
모든 `convert`/모드별 메서드는 `markdown` 파라미터를 받습니다.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
mc.convert("report.pdf", markdown=False) # 마크다운 서식 없는 일반 텍스트
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
기본값은 `markdown=True`입니다.
|
|
100
|
+
|
|
101
|
+
## 로드맵 (v1 기준 현황)
|
|
102
|
+
|
|
103
|
+
- ✅ 문서 변환 (docling)
|
|
104
|
+
- ✅ 이미지 OCR (docling)
|
|
105
|
+
- ✅ 이미지 캡셔닝 (Gemini / GPT-4o / Claude)
|
|
106
|
+
- ✅ 3D 모델 파싱: `.stl`, `.obj`, `.ply`, `.3mf`
|
|
107
|
+
- 🚧 3D 모델 파싱: `.step`/`.stp`, `.usd`/`.usdz`, `.fbx` — PointNet 기반 임베딩 후 기존 파서로 구축한 DB에서 유사 형상을 검색/리매핑하는 방식으로 구현 예정 (v2)
|
|
108
|
+
- 🚧 임베딩 함수 제공 (시간이 되면)
|
|
109
|
+
|
|
110
|
+
## 라이선스
|
|
111
|
+
|
|
112
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# mtt-converter
|
|
2
|
+
|
|
3
|
+
multiform data to text converter — 문서, 이미지, 3D 모델 등 다양한 형태의 데이터를 텍스트로 변환해주는 멀티모달 파이썬 라이브러리.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install mtt-converter
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Python 3.12 이상이 필요합니다.
|
|
12
|
+
|
|
13
|
+
## 빠른 시작
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from mtt import MultiModalConverter
|
|
17
|
+
|
|
18
|
+
mc = MultiModalConverter()
|
|
19
|
+
|
|
20
|
+
# 확장자를 보고 mode를 자동으로 추정
|
|
21
|
+
text = mc.convert("report.pdf")
|
|
22
|
+
|
|
23
|
+
# mode를 직접 지정
|
|
24
|
+
text = mc.convert("photo.png", mode="ocr")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`mode`를 넘기지 않으면 파일 확장자로 자동 추정하며, 이때 경고 메시지가 출력됩니다.
|
|
28
|
+
|
|
29
|
+
## 지원하는 mode
|
|
30
|
+
|
|
31
|
+
| mode | 설명 | 지원 확장자 |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `doc` | 문서를 텍스트로 변환 ([docling](https://github.com/docling-project/docling) 사용) | `.pdf` `.doc` `.docx` `.ppt` `.pptx` `.xls` `.xlsx` `.html` `.htm` `.md` `.csv` |
|
|
34
|
+
| `ocr` | 이미지 속 텍스트를 OCR로 추출 (docling 사용) | 직접 호출 시 임의의 이미지 파일 |
|
|
35
|
+
| `caption` | 이미지 내용을 멀티모달 LLM으로 캡셔닝/설명 | `.jpg` `.jpeg` `.tif` `.tiff` `.png` `.bmp` |
|
|
36
|
+
| `3dmodel` | 3D 모델 파일을 파싱해 지오메트리 요약(바운딩 박스, 표면적, 부피 등)을 텍스트로 반환 | `.stl` `.obj` `.ply` `.3mf` |
|
|
37
|
+
|
|
38
|
+
> `.glb`도 `3dmodel`로 추정되지만 아직 파서가 구현되어 있지 않습니다.
|
|
39
|
+
|
|
40
|
+
## 사용 예시
|
|
41
|
+
|
|
42
|
+
### 문서 → 텍스트
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
mc.convert("report.pdf", mode="doc")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 이미지 OCR
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
mc.convert("scanned_page.png", mode="ocr")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 이미지 캡셔닝
|
|
55
|
+
|
|
56
|
+
Gemini, GPT-4o, Claude 중 하나로 이미지를 설명합니다. 사용할 모델과 API 키는 `MultiModalConverter` 생성 시 지정합니다.
|
|
57
|
+
* **Token cost**가 발생할 수 있습니다.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
mc = MultiModalConverter(model_name="gemini-3.5-flash", api_key="...")
|
|
61
|
+
mc.convert("photo.jpg", mode="caption", sub_prompt="이 사진을 한국어로 설명해줘.")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `model_name`: `"gemini-*"` / `"gpt-4o"` / `"claude-*"` 중 하나의 실제 API 모델 id (기본값 `"gemini-3.5-flash"`)
|
|
65
|
+
- `api_key`를 넘기지 않으면 각 provider SDK가 환경 변수(`GOOGLE_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)에서 자동으로 읽습니다.
|
|
66
|
+
- `sub_prompt`를 지정하지 않으면 기본 프롬프트(`"Describe image in detail."`)가 사용됩니다.
|
|
67
|
+
|
|
68
|
+
### 3D 모델 파싱
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
mc.convert("model.stl", mode="3dmodel")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
솔리드 이름, 삼각형 개수, 바운딩 박스, 표면적, 부피(근사) 등을 요약한 텍스트를 반환합니다.
|
|
75
|
+
|
|
76
|
+
## 출력 형식
|
|
77
|
+
|
|
78
|
+
모든 `convert`/모드별 메서드는 `markdown` 파라미터를 받습니다.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
mc.convert("report.pdf", markdown=False) # 마크다운 서식 없는 일반 텍스트
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
기본값은 `markdown=True`입니다.
|
|
85
|
+
|
|
86
|
+
## 로드맵 (v1 기준 현황)
|
|
87
|
+
|
|
88
|
+
- ✅ 문서 변환 (docling)
|
|
89
|
+
- ✅ 이미지 OCR (docling)
|
|
90
|
+
- ✅ 이미지 캡셔닝 (Gemini / GPT-4o / Claude)
|
|
91
|
+
- ✅ 3D 모델 파싱: `.stl`, `.obj`, `.ply`, `.3mf`
|
|
92
|
+
- 🚧 3D 모델 파싱: `.step`/`.stp`, `.usd`/`.usdz`, `.fbx` — PointNet 기반 임베딩 후 기존 파서로 구축한 DB에서 유사 형상을 검색/리매핑하는 방식으로 구현 예정 (v2)
|
|
93
|
+
- 🚧 임베딩 함수 제공 (시간이 되면)
|
|
94
|
+
|
|
95
|
+
## 라이선스
|
|
96
|
+
|
|
97
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mtt-converter"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"docling[rapidocr]>=2.114.0",
|
|
9
|
+
"google-genai>=2.12.1",
|
|
10
|
+
"markdown>=3.10.2",
|
|
11
|
+
"beautifulsoup4>=4.15.0",
|
|
12
|
+
"python-dotenv>=1.2.2",
|
|
13
|
+
"anthropic>=0.117.1",
|
|
14
|
+
"openai>=2.46.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/mtt"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .converter import MultiModalConverter
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from mtt.service.docling import DoclingService
|
|
5
|
+
from mtt.service.mesh_llm import MeshLLMService
|
|
6
|
+
from mtt.service.caption_explaner import CaptionExplanerService
|
|
7
|
+
from mtt.types import ModeType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MultiModalConverter:
|
|
11
|
+
MODE_ESTIMATE_MAP: dict[str, ModeType] = {
|
|
12
|
+
".jpg": "caption",
|
|
13
|
+
".jpeg": "caption",
|
|
14
|
+
".tif": "caption",
|
|
15
|
+
".tiff": "caption",
|
|
16
|
+
".png": "caption",
|
|
17
|
+
".bmp": "caption",
|
|
18
|
+
".pdf": "doc",
|
|
19
|
+
".doc": "doc",
|
|
20
|
+
".docx": "doc",
|
|
21
|
+
".ppt": "doc",
|
|
22
|
+
".pptx": "doc",
|
|
23
|
+
".xls": "doc",
|
|
24
|
+
".xlsx": "doc",
|
|
25
|
+
".html": "doc",
|
|
26
|
+
".htm": "doc",
|
|
27
|
+
".md": "doc",
|
|
28
|
+
".csv": "doc",
|
|
29
|
+
".stl": "3dmodel",
|
|
30
|
+
".obj": "3dmodel",
|
|
31
|
+
".ply": "3dmodel",
|
|
32
|
+
".3mf": "3dmodel",
|
|
33
|
+
".glb": "3dmodel",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def __init__(self, **kwargs):
|
|
37
|
+
# TODO: 나중에 임베딩 모델 api 관련해서 파라미터를 받을 수 있게하거나 할듯. 그리고 일부 시스템 프롬프트를 조절할 수 있게 할듯.
|
|
38
|
+
self._docling_service = DoclingService()
|
|
39
|
+
self._mesh_llm_service = MeshLLMService()
|
|
40
|
+
self._caption_explaner_service = CaptionExplanerService(
|
|
41
|
+
model_name=kwargs.get("model_name", "gemini-3.5-flash"), # default: gemini
|
|
42
|
+
api_key=kwargs.get("api_key")
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def convert(self, input_path: str | Path, sub_prompt=None, markdown=True, mode: ModeType | None = None):
|
|
46
|
+
if sub_prompt is None:
|
|
47
|
+
sub_prompt = "Describe image in detail."
|
|
48
|
+
|
|
49
|
+
if mode is None:
|
|
50
|
+
print("[Warning] The 'mode' argument is missing, so the 'mode' value is estimated and used.")
|
|
51
|
+
_, ext = os.path.splitext(input_path)
|
|
52
|
+
|
|
53
|
+
mode = self.MODE_ESTIMATE_MAP.get(ext.lower())
|
|
54
|
+
print(f"[Warning] the mode was set to {mode}")
|
|
55
|
+
|
|
56
|
+
if mode == "caption":
|
|
57
|
+
# input 가공 가능
|
|
58
|
+
return self.caption(input_path, sub_prompt=sub_prompt, markdown=markdown)
|
|
59
|
+
elif mode == "ocr":
|
|
60
|
+
# input 가공 가능
|
|
61
|
+
return self.ocr(input_path, markdown=markdown)
|
|
62
|
+
elif mode == "3dmodel":
|
|
63
|
+
# input 가공 가능함.
|
|
64
|
+
return self.scan3dmodel(input_path, markdown=markdown)
|
|
65
|
+
elif mode in ["doc", "docs", "document"]:
|
|
66
|
+
# input 가공 가능함.
|
|
67
|
+
return self.doc2text(input_path, markdown=markdown)
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(f"There is no valid function for the mode. | mode: {mode}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def caption(self, input_path: str | Path, sub_prompt=None, markdown=True) -> str:
|
|
74
|
+
'''
|
|
75
|
+
image의 정보를 텍스트로 설명 ; 캡셔닝
|
|
76
|
+
'''
|
|
77
|
+
return self._caption_explaner_service.to_text(input_path, sub_prompt=sub_prompt, markdown=markdown) #이미지 용 멀티모달 임베딩 api를 찾아서 구현할듯.
|
|
78
|
+
|
|
79
|
+
def ocr(self, input_path: str | Path, markdown=True) -> str:
|
|
80
|
+
'''
|
|
81
|
+
image내 텍스트를 ocr하여 얻어옴.
|
|
82
|
+
'''
|
|
83
|
+
return self._docling_service.to_text(input_path, markdown=markdown)
|
|
84
|
+
|
|
85
|
+
def scan3dmodel(self, input_path: str | Path, markdown=True) -> str:
|
|
86
|
+
'''
|
|
87
|
+
3D 모델을 텍스트로 설명
|
|
88
|
+
'''
|
|
89
|
+
return self._mesh_llm_service.to_text(input_path, markdown=markdown) # TODO: point net과 parser들로 구현할 예정 ; 가능한 parser는 claude가 이미 구현한듯?
|
|
90
|
+
|
|
91
|
+
def doc2text(self, input_path: str | Path, markdown=True) -> str:
|
|
92
|
+
'''
|
|
93
|
+
문서를 텍스트로 변환 [docling 모듈을 이용]
|
|
94
|
+
'''
|
|
95
|
+
return self._docling_service.to_text(input_path, markdown=markdown)
|
|
96
|
+
# 만약 docling관련해서 분리가 필요하면 분리하기.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .service import CaptionExplanerService
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from mtt.types import CaptionModelType as ModelType
|
|
5
|
+
from mtt.utils.caption_utils import caption_explain, chat_output_to_text
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CaptionExplanerService:
|
|
9
|
+
def __init__(self, model_name:ModelType, api_key=None):
|
|
10
|
+
self.model_name: ModelType=model_name
|
|
11
|
+
self.api_key=api_key
|
|
12
|
+
|
|
13
|
+
def to_text(self, input_path: str | Path, sub_prompt=None, markdown=True) -> str:
|
|
14
|
+
result = caption_explain(input_path, model_name=self.model_name, api_key=self.api_key, sub_prompt=sub_prompt)
|
|
15
|
+
return chat_output_to_text(result, model_name=self.model_name, markdown=markdown)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from docling.document_converter import DocumentConverter
|
|
4
|
+
|
|
5
|
+
from mtt.utils.docling_utils import convert_document, document_to_markdown, document_to_text
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DoclingService:
|
|
9
|
+
'''docling utils 함수를 엮어서 문서/이미지를 텍스트로 변환하는 서비스.
|
|
10
|
+
|
|
11
|
+
문서(pdf, docx, ...)는 doc2text에, 이미지는 ocr에 재사용된다.
|
|
12
|
+
docling이 이미지 입력에 대해서도 OCR 파이프라인을 자동으로 태우기 때문.
|
|
13
|
+
'''
|
|
14
|
+
|
|
15
|
+
def __init__(self, converter: DocumentConverter | None = None):
|
|
16
|
+
self._converter = converter or DocumentConverter()
|
|
17
|
+
|
|
18
|
+
def to_text(self, input_path: str | Path, markdown=True) -> str:
|
|
19
|
+
result = convert_document(self._converter, input_path)
|
|
20
|
+
return document_to_markdown(result) if markdown else document_to_text(result)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from mtt.utils.mesh_llm_utils import parsing_stl, parsing_obj, parsing_ply, parsing_3mf
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MeshLLMService:
|
|
8
|
+
'''PointNet(허깅페이스, 8bit 양자화) 기반 3D 모델 -> context vector -> projection(나중에 직접 만들거임) ->임베딩 벡터
|
|
9
|
+
|
|
10
|
+
TODO: PointNet기반 모델 로딩/추론 구현 또는 임베딩 벡터 기반 검색으로 구현할 예정.
|
|
11
|
+
'''
|
|
12
|
+
|
|
13
|
+
def __init__(self, **kwargs):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def to_text(self, input_path: str | Path, markdown=True) -> str: #TODO: v1이고, v2에서는 pointNet기반으로 context vector를 embedding vector로 projection해서 줄듯.
|
|
17
|
+
_, ext = os.path.splitext(input_path)
|
|
18
|
+
ext = ext.lower()
|
|
19
|
+
if ext == ".stl":
|
|
20
|
+
return parsing_stl(input_path, markdown=markdown)
|
|
21
|
+
elif ext == ".obj":
|
|
22
|
+
return parsing_obj(input_path, markdown=markdown)
|
|
23
|
+
elif ext == ".ply":
|
|
24
|
+
return parsing_ply(input_path, markdown=markdown)
|
|
25
|
+
elif ext == ".3mf":
|
|
26
|
+
return parsing_3mf(input_path, markdown=markdown)
|
|
27
|
+
# TODO: .step/.stp, .usd/.usdz, .fbx는 PointNet으로 임베딩해서 기존 stl 등으로 구축해둔 DB에서
|
|
28
|
+
# 검색 후 리매핑하는 방식으로 구현 예정 (직접 파싱 대신 유사 형상 검색).
|
|
29
|
+
raise ValueError(f"Unreadable file or incorrect extension. input_path: {input_path}")
|
|
File without changes
|