pyarchinit-photogrammetry 0.2.0__tar.gz → 0.3.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.
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/PKG-INFO +115 -30
- pyarchinit_photogrammetry-0.3.0/README.md +256 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/pyproject.toml +6 -5
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/__init__.py +9 -2
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/_env.py +62 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/bim/ifc.py +3 -1
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/cli.py +15 -4
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/chunk.py +26 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/demo.py +62 -5
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/doctor.py +207 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/dense.py +2 -2
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/engine/isolate.py +148 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/engine/meshing.py +237 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/__init__.py +126 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/dialogs.py +603 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/icons.py +122 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/image_views.py +124 -16
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/logging_setup.py +135 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/main_window.py +620 -218
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/model_view.py +1 -1
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/panels.py +869 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/start_page.py +119 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/theme.py +132 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/worker.py +90 -18
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/bridge.py +52 -6
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/database.py +17 -0
- pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/pyarchinit/demo_db.py +242 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/media.py +4 -3
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/testing/synthetic.py +172 -1
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/PKG-INFO +115 -30
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/SOURCES.txt +12 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/requires.txt +11 -4
- pyarchinit_photogrammetry-0.3.0/tests/test_env.py +39 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_gui.py +8 -15
- pyarchinit_photogrammetry-0.3.0/tests/test_gui_layers.py +148 -0
- pyarchinit_photogrammetry-0.3.0/tests/test_isolate.py +68 -0
- pyarchinit_photogrammetry-0.3.0/tests/test_meshing_fallback.py +50 -0
- pyarchinit_photogrammetry-0.3.0/tests/test_pottery_demo.py +51 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_regressions.py +35 -3
- pyarchinit_photogrammetry-0.2.0/README.md +0 -177
- pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/engine/meshing.py +0 -144
- pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/__init__.py +0 -43
- pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/dialogs.py +0 -266
- pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/panels.py +0 -461
- pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/pyarchinit/demo_db.py +0 -94
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/LICENSE +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/setup.cfg +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/__main__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/classify.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/sam.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/app.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/pottery.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/sections.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/stratigraphy.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/terrain.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/bim/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/camera.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/crs.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/document.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/geometry.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/marker.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/pointcloud.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/progress.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/dem.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/ortho.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/sfm.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/texturing.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/visibility.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/__main__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/scene.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/exif.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/exports.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/report.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/gcp.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/targets.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/config.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/testing/__init__.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/dependency_links.txt +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/entry_points.txt +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/top_level.txt +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_archaeo.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_bim_ai.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_geometry.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_markers.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_pipeline.py +0 -0
- {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_pyarchinit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyarchinit-photogrammetry
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Open-source photogrammetry engine for archaeology and BIM, integrated with PyArchInit
|
|
5
5
|
Author-email: Enzo Cocca <enzo.ccc@gmail.com>
|
|
6
6
|
License-Expression: GPL-2.0-or-later
|
|
@@ -30,7 +30,7 @@ Requires-Dist: laspy>=2.4
|
|
|
30
30
|
Requires-Dist: sqlalchemy>=2.0
|
|
31
31
|
Requires-Dist: typer>=0.9
|
|
32
32
|
Provides-Extra: mesh
|
|
33
|
-
Requires-Dist: open3d
|
|
33
|
+
Requires-Dist: open3d!=0.20.0,>=0.18; extra == "mesh"
|
|
34
34
|
Provides-Extra: bim
|
|
35
35
|
Requires-Dist: ifcopenshell>=0.8; extra == "bim"
|
|
36
36
|
Provides-Extra: postgis
|
|
@@ -41,16 +41,22 @@ Requires-Dist: sam2>=1.1; extra == "sam"
|
|
|
41
41
|
Provides-Extra: gui
|
|
42
42
|
Requires-Dist: PySide6>=6.6; extra == "gui"
|
|
43
43
|
Requires-Dist: pyvista>=0.43; extra == "gui"
|
|
44
|
-
Requires-Dist: pyvistaqt>=0.
|
|
44
|
+
Requires-Dist: pyvistaqt>=0.13; extra == "gui"
|
|
45
|
+
Requires-Dist: qtawesome>=1.3; extra == "gui"
|
|
45
46
|
Provides-Extra: all
|
|
46
|
-
Requires-Dist: open3d
|
|
47
|
+
Requires-Dist: open3d!=0.20.0,>=0.18; extra == "all"
|
|
47
48
|
Requires-Dist: ifcopenshell>=0.8; extra == "all"
|
|
48
49
|
Requires-Dist: psycopg2-binary>=2.9; extra == "all"
|
|
49
50
|
Requires-Dist: PySide6>=6.6; extra == "all"
|
|
50
51
|
Requires-Dist: pyvista>=0.43; extra == "all"
|
|
51
|
-
Requires-Dist: pyvistaqt>=0.
|
|
52
|
+
Requires-Dist: pyvistaqt>=0.13; extra == "all"
|
|
53
|
+
Requires-Dist: qtawesome>=1.3; extra == "all"
|
|
52
54
|
Provides-Extra: dev
|
|
53
55
|
Requires-Dist: pytest>=7; extra == "dev"
|
|
56
|
+
Provides-Extra: build
|
|
57
|
+
Requires-Dist: pyinstaller>=6.10; extra == "build"
|
|
58
|
+
Requires-Dist: pyinstaller-hooks-contrib>=2024.8; extra == "build"
|
|
59
|
+
Requires-Dist: pillow>=9.1; extra == "build"
|
|
54
60
|
Dynamic: license-file
|
|
55
61
|
|
|
56
62
|
# pyarchinit-photogrammetry
|
|
@@ -64,51 +70,128 @@ cut/fill volumes, point-cloud classification, Segment Anything 2.1, IFC export,
|
|
|
64
70
|
publishing into a PyArchInit database. Everything is scriptable in Python and runs on CPU,
|
|
65
71
|
Apple Silicon included.
|
|
66
72
|
|
|
67
|
-
> Status: **alpha (0.
|
|
68
|
-
> with a built-in Python console). Italian version: [README.it.md](README.it.md).
|
|
73
|
+
> Status: **alpha (0.3)**. Python library, command line and desktop application (on the same
|
|
74
|
+
> API, with a built-in Python console). Italian version: [README.it.md](README.it.md).
|
|
69
75
|
|
|
70
|
-
##
|
|
76
|
+
## Desktop application (no Python needed)
|
|
77
|
+
|
|
78
|
+
Download the build for your system from the
|
|
79
|
+
[Releases](https://github.com/enzococca/pyarchinit-photogrammetry/releases) page:
|
|
80
|
+
|
|
81
|
+
| Platform | File |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| macOS 15 or newer (Apple Silicon) | `PyArchInit-Photogrammetry-<version>-macos-arm64.dmg` |
|
|
84
|
+
| Windows 10/11 (64 bit) | `PyArchInit-Photogrammetry-<version>-windows-x64.zip` |
|
|
85
|
+
|
|
86
|
+
Open the disk image and drag the app to *Applications* (macOS), or unzip and run
|
|
87
|
+
`PyArchInit Photogrammetry.exe` (Windows). The application starts on a page with *Nuovo
|
|
88
|
+
progetto*, *Apri progetto* and *Progetto demo*: the demo creates a synthetic trench and a
|
|
89
|
+
bowl on a turntable and processes them in a few minutes. If PyArchInit 5 is installed
|
|
90
|
+
(`~/pyarchinit_5`), the interface connects to its database at start and the demo database is
|
|
91
|
+
written inside `pyarchinit_DB_folder` so that QGIS can open it too; PostgreSQL/PostGIS servers
|
|
92
|
+
are reachable from *PyArchInit > Connetti*. The builds are unsigned: on macOS right-click > *Open*
|
|
93
|
+
the first time (or `xattr -dr com.apple.quarantine "/Applications/PyArchInit Photogrammetry.app"`),
|
|
94
|
+
on Windows choose *More info > Run anyway*. Details in [docs/packaging.md](docs/packaging.md).
|
|
95
|
+
|
|
96
|
+
## Installation with pip
|
|
97
|
+
|
|
98
|
+
Use a dedicated virtual environment (not the Anaconda `base` environment, see
|
|
99
|
+
*Troubleshooting*). With [uv](https://docs.astral.sh/uv/):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv venv --seed --python 3.12 ~/venvs/ppg && source ~/venvs/ppg/bin/activate
|
|
103
|
+
uv pip install "pyarchinit-photogrammetry[all]" # engine, mesh (Open3D), IFC, PostGIS, desktop GUI
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
With the standard tools:
|
|
71
107
|
|
|
72
108
|
```bash
|
|
73
|
-
|
|
74
|
-
pip install "pyarchinit-photogrammetry[
|
|
109
|
+
python3 -m venv ~/venvs/ppg && source ~/venvs/ppg/bin/activate # Windows: ~\venvs\ppg\Scripts\activate
|
|
110
|
+
pip install -U pip && pip install "pyarchinit-photogrammetry[all]"
|
|
75
111
|
```
|
|
76
112
|
|
|
113
|
+
Optional: `pip install "pyarchinit-photogrammetry[sam]"` adds Segment Anything 2.1 (PyTorch).
|
|
114
|
+
A global `ppg`/`ppg-gui` without activating anything: `uv tool install "pyarchinit-photogrammetry[all]"`
|
|
115
|
+
(or `pipx install`).
|
|
116
|
+
|
|
77
117
|
From source:
|
|
78
118
|
|
|
79
119
|
```bash
|
|
80
120
|
git clone https://github.com/enzococca/pyarchinit-photogrammetry
|
|
81
121
|
cd pyarchinit-photogrammetry
|
|
82
|
-
|
|
122
|
+
uv venv --seed --python 3.12 .venv && source .venv/bin/activate
|
|
123
|
+
uv pip install -e ".[all,dev]"
|
|
83
124
|
pytest -q
|
|
84
125
|
```
|
|
85
126
|
|
|
86
|
-
Python 3.10
|
|
127
|
+
Python 3.10-3.12. Writing geometries into a PyArchInit **SQLite** database needs `mod_spatialite`
|
|
87
128
|
(found automatically in QGIS, MacPorts, Homebrew and Linux locations, or set
|
|
88
|
-
`SPATIALITE_LIBRARY_PATH`).
|
|
89
|
-
|
|
90
|
-
On headless Linux servers Open3D needs the EGL/GL libraries:
|
|
129
|
+
`SPATIALITE_LIBRARY_PATH`). On headless Linux servers Open3D needs the EGL/GL libraries:
|
|
91
130
|
`sudo apt-get install libegl1 libgl1 libgomp1`.
|
|
92
131
|
|
|
93
|
-
##
|
|
132
|
+
## Start
|
|
94
133
|
|
|
95
134
|
```bash
|
|
96
|
-
ppg
|
|
97
|
-
ppg
|
|
135
|
+
ppg doctor # checks the environment and says what to fix
|
|
136
|
+
ppg demo ~/ppg_demo --process # synthetic trench + 8 targets, bowl on a turntable, pottery meshes, PyArchInit DB
|
|
137
|
+
ppg-gui ~/ppg_demo/scavo.ppg # open the processed trench in the interface
|
|
138
|
+
ppg-gui ~/ppg_demo/ceramica/ceramica.ppg # the bowl (local metric frame from the turntable targets)
|
|
139
|
+
ppg-gui # start page: new, open, demo, recent projects
|
|
98
140
|
```
|
|
99
141
|
|
|
100
|
-
|
|
101
|
-
|
|
142
|
+
`ppg-gui` is the same as `ppg gui` and `python -m pyarchinit_photogrammetry.gui`. The
|
|
143
|
+
interface writes a log in `~/Library/Logs/pyarchinit-photogrammetry` (macOS),
|
|
144
|
+
`%LOCALAPPDATA%\pyarchinit-photogrammetry\logs` (Windows) or `~/.local/state/pyarchinit-photogrammetry/logs`
|
|
145
|
+
(Linux); *Aiuto > Apri cartella dei log* opens it.
|
|
146
|
+
|
|
147
|
+
## Troubleshooting
|
|
148
|
+
|
|
149
|
+
* **`ppg-gui` does nothing, or crashes on start.** Run `ppg doctor`. A frequent cause is an
|
|
150
|
+
old copy of the package installed in another Python (for example Anaconda's `base`) that
|
|
151
|
+
comes first on `PATH`: activate the right environment (`which ppg-gui` must point inside it)
|
|
152
|
+
or uninstall the other copy (`/opt/anaconda3/bin/python -m pip uninstall pyarchinit-photogrammetry`).
|
|
153
|
+
* **Anaconda `base` environment**: its SciPy wheels may not load on recent macOS
|
|
154
|
+
(`ImportError ... __thread_bss ... zero-fill section`) and its PROJ data is picked up by
|
|
155
|
+
rasterio. Use a dedicated virtual environment; `PROJ_LIB`/`PROJ_DATA`/`GDAL_DATA` exported
|
|
156
|
+
by Anaconda or QGIS are redirected to the data bundled with rasterio.
|
|
157
|
+
* **The mesh step says "Poisson reconstruction failed... height field instead".** Poisson
|
|
158
|
+
reconstruction runs in a separate process because PoissonRecon (Open3D) can crash on some
|
|
159
|
+
clouds; the pipeline retries and then builds a 2.5D surface. Open3D 0.20.0 crashes
|
|
160
|
+
systematically on macOS and is excluded (`open3d!=0.20.0`); 0.19 works.
|
|
161
|
+
* **Native dialogs crashing on macOS 26/27** (`NSInternalInconsistencyException ...
|
|
162
|
+
targetSizeInPoints`): the interface uses Qt's own message boxes, no action needed.
|
|
163
|
+
* **The 3D view hangs on macOS**: pyvistaqt older than 0.13 hangs on start; `pip install -U pyvistaqt`.
|
|
164
|
+
|
|
165
|
+
## Screenshots
|
|
166
|
+
|
|
167
|
+
| | |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
|  |  |
|
|
170
|
+
|  |  |
|
|
171
|
+
|  |  |
|
|
172
|
+
|
|
173
|
+
Top right: the orthomosaic/DEM view with the US polygons loaded from PyArchInit, contour lines
|
|
174
|
+
and a section trace as layers, the *Sezioni* pane with the drawing, and the PyArchInit pane.
|
|
175
|
+
Middle right: the IFC export opened with IfcOpenShell, one element per unit. Bottom: the bowl
|
|
176
|
+
of the turntable demo reconstructed from 32 synthetic photos and its profile drawing (rim
|
|
177
|
+
16.0 cm measured against 16.0 cm true).
|
|
102
178
|
|
|
103
179
|
## Desktop interface
|
|
104
180
|
|
|
105
|
-
`ppg-gui`
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
181
|
+
`ppg-gui` opens the desktop application (Italian UI): start page with recent projects; a
|
|
182
|
+
toolbar with the workflow (photos, markers, alignment, references, dense cloud, model,
|
|
183
|
+
texture, DEM, orthophoto, *Elabora tutto*); workspace with chunks, cameras, markers and
|
|
184
|
+
products; 3D view (VTK, with a software fallback renderer); orthomosaic and DEM view with
|
|
185
|
+
drawing tools (stratigraphic units, section traces, measurements, spot heights, SAM 2.1
|
|
186
|
+
prompts); photo view with manual marker placement; reference pane with GCP and check point
|
|
187
|
+
errors; Python console bound to the project (`doc`, `chunk`); background processing with
|
|
188
|
+
progress, cancel and a status-bar indicator; a drawing toolbar and a *Layer* pane (show/hide,
|
|
189
|
+
zoom, attributes on click) for units, sections, contour lines and spot heights; a *Sezioni*
|
|
190
|
+
pane with the drawings; a log pane with level filter, search, copy and
|
|
191
|
+
save; error windows with a hint, the traceback and a *Copia* button; light and dark themes;
|
|
192
|
+
*Aiuto > Diagnostica ambiente* (the same checks as `ppg doctor`); a PyArchInit menu to
|
|
193
|
+
connect, publish orthophotos, DEMs and models, and write unit polygons and spot heights.
|
|
194
|
+
See [docs/gui.md](docs/gui.md).
|
|
112
195
|
|
|
113
196
|
## Quick start (Python)
|
|
114
197
|
|
|
@@ -152,7 +235,7 @@ then `ppg.app.document` inside the script (in the GUI: **Strumenti > Esegui scri
|
|
|
152
235
|
| --- | --- |
|
|
153
236
|
| Alignment | SIFT features and matching, incremental SfM (COLMAP via pycolmap), camera calibration, bundle adjustment, tie points, pair selection (exhaustive, sequential, by GNSS position) |
|
|
154
237
|
| Dense | Depth maps and fusion on CPU (built-in), COLMAP PatchMatch on NVIDIA GPUs, OpenMVS when installed |
|
|
155
|
-
| Products | Point cloud (PLY/LAS 1.4 with classes), mesh (Poisson or 2.5D), texture atlas (OBJ+MTL+JPG), DEM, orthomosaic, hillshade, slope, HTML report |
|
|
238
|
+
| Products | Point cloud (PLY/LAS 1.4 with classes), mesh (Poisson in an isolated process with automatic fallback, or 2.5D), texture atlas (OBJ+MTL+JPG), DEM, orthomosaic, hillshade, slope, HTML report |
|
|
156
239
|
| Markers | Printable ArUco/AprilTag sheets with scale bar, turntable mats with known layout, sub-pixel detection, triangulation, GCP/check points, scale bars, weighted 7-parameter transformation, any EPSG CRS (geographic GCPs go to the UTM zone) |
|
|
157
240
|
| Archaeology | Vertical sections and parallel cross sections, plan slices at a given elevation, DXF/SVG drawings at scale, contour lines, cut/fill volumes, pottery: rotation axis from sherds, profile, rim/base diameter, height, wall thickness, drawing with filled section |
|
|
158
241
|
| BIM | IFC4 export with IfcMapConversion + IfcProjectedCRS georeferencing, one element per stratigraphic unit with `Pset_PyArchInit_US` |
|
|
@@ -209,16 +292,18 @@ median error < 3 mm, orthophoto target positions < 5 mm.
|
|
|
209
292
|
```
|
|
210
293
|
pyarchinit_photogrammetry/
|
|
211
294
|
core/ Document, Chunk, Camera, Sensor, Marker, ScaleBar, PointCloud, CRS
|
|
212
|
-
engine/ sfm (pycolmap), dense, meshing, texturing, dem, ortho
|
|
295
|
+
engine/ sfm (pycolmap), dense, meshing, isolate (crash-proof workers), texturing, dem, ortho
|
|
213
296
|
markers/ coded targets, GCP import/export, georeferencing
|
|
214
297
|
archaeo/ sections, pottery, terrain (contours, volumes), stratigraphy (units)
|
|
215
298
|
bim/ IFC export
|
|
216
299
|
ai/ classification, SAM 2.1
|
|
217
300
|
pyarchinit/ config.cfg, database, media, high-level bridge
|
|
218
301
|
io/ EXIF, exports, report
|
|
219
|
-
gui/ desktop interface (PySide6, pyvista)
|
|
302
|
+
gui/ desktop interface (PySide6, pyvista, qtawesome icons, themes, log, start page)
|
|
303
|
+
doctor.py environment diagnosis (`ppg doctor`)
|
|
220
304
|
demo.py demo data set
|
|
221
305
|
cli.py `ppg` command
|
|
306
|
+
packaging/ PyInstaller spec and build script for the desktop application
|
|
222
307
|
```
|
|
223
308
|
|
|
224
309
|
Projects are a JSON file (`name.ppg`) plus a folder (`name.ppg.files/`) with the data of each
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# pyarchinit-photogrammetry
|
|
2
|
+
|
|
3
|
+
Open-source photogrammetry engine for archaeology and heritage BIM, integrated with
|
|
4
|
+
[PyArchInit](https://github.com/pyarchinit/pyarchinit).
|
|
5
|
+
|
|
6
|
+
From photos to georeferenced point clouds, meshes, DEMs and orthophotos, with the tools an
|
|
7
|
+
excavation needs: coded targets, ground control, sections, pottery profiles, contour lines,
|
|
8
|
+
cut/fill volumes, point-cloud classification, Segment Anything 2.1, IFC export, and direct
|
|
9
|
+
publishing into a PyArchInit database. Everything is scriptable in Python and runs on CPU,
|
|
10
|
+
Apple Silicon included.
|
|
11
|
+
|
|
12
|
+
> Status: **alpha (0.3)**. Python library, command line and desktop application (on the same
|
|
13
|
+
> API, with a built-in Python console). Italian version: [README.it.md](README.it.md).
|
|
14
|
+
|
|
15
|
+
## Desktop application (no Python needed)
|
|
16
|
+
|
|
17
|
+
Download the build for your system from the
|
|
18
|
+
[Releases](https://github.com/enzococca/pyarchinit-photogrammetry/releases) page:
|
|
19
|
+
|
|
20
|
+
| Platform | File |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| macOS 15 or newer (Apple Silicon) | `PyArchInit-Photogrammetry-<version>-macos-arm64.dmg` |
|
|
23
|
+
| Windows 10/11 (64 bit) | `PyArchInit-Photogrammetry-<version>-windows-x64.zip` |
|
|
24
|
+
|
|
25
|
+
Open the disk image and drag the app to *Applications* (macOS), or unzip and run
|
|
26
|
+
`PyArchInit Photogrammetry.exe` (Windows). The application starts on a page with *Nuovo
|
|
27
|
+
progetto*, *Apri progetto* and *Progetto demo*: the demo creates a synthetic trench and a
|
|
28
|
+
bowl on a turntable and processes them in a few minutes. If PyArchInit 5 is installed
|
|
29
|
+
(`~/pyarchinit_5`), the interface connects to its database at start and the demo database is
|
|
30
|
+
written inside `pyarchinit_DB_folder` so that QGIS can open it too; PostgreSQL/PostGIS servers
|
|
31
|
+
are reachable from *PyArchInit > Connetti*. The builds are unsigned: on macOS right-click > *Open*
|
|
32
|
+
the first time (or `xattr -dr com.apple.quarantine "/Applications/PyArchInit Photogrammetry.app"`),
|
|
33
|
+
on Windows choose *More info > Run anyway*. Details in [docs/packaging.md](docs/packaging.md).
|
|
34
|
+
|
|
35
|
+
## Installation with pip
|
|
36
|
+
|
|
37
|
+
Use a dedicated virtual environment (not the Anaconda `base` environment, see
|
|
38
|
+
*Troubleshooting*). With [uv](https://docs.astral.sh/uv/):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv venv --seed --python 3.12 ~/venvs/ppg && source ~/venvs/ppg/bin/activate
|
|
42
|
+
uv pip install "pyarchinit-photogrammetry[all]" # engine, mesh (Open3D), IFC, PostGIS, desktop GUI
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
With the standard tools:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
python3 -m venv ~/venvs/ppg && source ~/venvs/ppg/bin/activate # Windows: ~\venvs\ppg\Scripts\activate
|
|
49
|
+
pip install -U pip && pip install "pyarchinit-photogrammetry[all]"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Optional: `pip install "pyarchinit-photogrammetry[sam]"` adds Segment Anything 2.1 (PyTorch).
|
|
53
|
+
A global `ppg`/`ppg-gui` without activating anything: `uv tool install "pyarchinit-photogrammetry[all]"`
|
|
54
|
+
(or `pipx install`).
|
|
55
|
+
|
|
56
|
+
From source:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/enzococca/pyarchinit-photogrammetry
|
|
60
|
+
cd pyarchinit-photogrammetry
|
|
61
|
+
uv venv --seed --python 3.12 .venv && source .venv/bin/activate
|
|
62
|
+
uv pip install -e ".[all,dev]"
|
|
63
|
+
pytest -q
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Python 3.10-3.12. Writing geometries into a PyArchInit **SQLite** database needs `mod_spatialite`
|
|
67
|
+
(found automatically in QGIS, MacPorts, Homebrew and Linux locations, or set
|
|
68
|
+
`SPATIALITE_LIBRARY_PATH`). On headless Linux servers Open3D needs the EGL/GL libraries:
|
|
69
|
+
`sudo apt-get install libegl1 libgl1 libgomp1`.
|
|
70
|
+
|
|
71
|
+
## Start
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ppg doctor # checks the environment and says what to fix
|
|
75
|
+
ppg demo ~/ppg_demo --process # synthetic trench + 8 targets, bowl on a turntable, pottery meshes, PyArchInit DB
|
|
76
|
+
ppg-gui ~/ppg_demo/scavo.ppg # open the processed trench in the interface
|
|
77
|
+
ppg-gui ~/ppg_demo/ceramica/ceramica.ppg # the bowl (local metric frame from the turntable targets)
|
|
78
|
+
ppg-gui # start page: new, open, demo, recent projects
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`ppg-gui` is the same as `ppg gui` and `python -m pyarchinit_photogrammetry.gui`. The
|
|
82
|
+
interface writes a log in `~/Library/Logs/pyarchinit-photogrammetry` (macOS),
|
|
83
|
+
`%LOCALAPPDATA%\pyarchinit-photogrammetry\logs` (Windows) or `~/.local/state/pyarchinit-photogrammetry/logs`
|
|
84
|
+
(Linux); *Aiuto > Apri cartella dei log* opens it.
|
|
85
|
+
|
|
86
|
+
## Troubleshooting
|
|
87
|
+
|
|
88
|
+
* **`ppg-gui` does nothing, or crashes on start.** Run `ppg doctor`. A frequent cause is an
|
|
89
|
+
old copy of the package installed in another Python (for example Anaconda's `base`) that
|
|
90
|
+
comes first on `PATH`: activate the right environment (`which ppg-gui` must point inside it)
|
|
91
|
+
or uninstall the other copy (`/opt/anaconda3/bin/python -m pip uninstall pyarchinit-photogrammetry`).
|
|
92
|
+
* **Anaconda `base` environment**: its SciPy wheels may not load on recent macOS
|
|
93
|
+
(`ImportError ... __thread_bss ... zero-fill section`) and its PROJ data is picked up by
|
|
94
|
+
rasterio. Use a dedicated virtual environment; `PROJ_LIB`/`PROJ_DATA`/`GDAL_DATA` exported
|
|
95
|
+
by Anaconda or QGIS are redirected to the data bundled with rasterio.
|
|
96
|
+
* **The mesh step says "Poisson reconstruction failed... height field instead".** Poisson
|
|
97
|
+
reconstruction runs in a separate process because PoissonRecon (Open3D) can crash on some
|
|
98
|
+
clouds; the pipeline retries and then builds a 2.5D surface. Open3D 0.20.0 crashes
|
|
99
|
+
systematically on macOS and is excluded (`open3d!=0.20.0`); 0.19 works.
|
|
100
|
+
* **Native dialogs crashing on macOS 26/27** (`NSInternalInconsistencyException ...
|
|
101
|
+
targetSizeInPoints`): the interface uses Qt's own message boxes, no action needed.
|
|
102
|
+
* **The 3D view hangs on macOS**: pyvistaqt older than 0.13 hangs on start; `pip install -U pyvistaqt`.
|
|
103
|
+
|
|
104
|
+
## Screenshots
|
|
105
|
+
|
|
106
|
+
| | |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
|  |  |
|
|
109
|
+
|  |  |
|
|
110
|
+
|  |  |
|
|
111
|
+
|
|
112
|
+
Top right: the orthomosaic/DEM view with the US polygons loaded from PyArchInit, contour lines
|
|
113
|
+
and a section trace as layers, the *Sezioni* pane with the drawing, and the PyArchInit pane.
|
|
114
|
+
Middle right: the IFC export opened with IfcOpenShell, one element per unit. Bottom: the bowl
|
|
115
|
+
of the turntable demo reconstructed from 32 synthetic photos and its profile drawing (rim
|
|
116
|
+
16.0 cm measured against 16.0 cm true).
|
|
117
|
+
|
|
118
|
+
## Desktop interface
|
|
119
|
+
|
|
120
|
+
`ppg-gui` opens the desktop application (Italian UI): start page with recent projects; a
|
|
121
|
+
toolbar with the workflow (photos, markers, alignment, references, dense cloud, model,
|
|
122
|
+
texture, DEM, orthophoto, *Elabora tutto*); workspace with chunks, cameras, markers and
|
|
123
|
+
products; 3D view (VTK, with a software fallback renderer); orthomosaic and DEM view with
|
|
124
|
+
drawing tools (stratigraphic units, section traces, measurements, spot heights, SAM 2.1
|
|
125
|
+
prompts); photo view with manual marker placement; reference pane with GCP and check point
|
|
126
|
+
errors; Python console bound to the project (`doc`, `chunk`); background processing with
|
|
127
|
+
progress, cancel and a status-bar indicator; a drawing toolbar and a *Layer* pane (show/hide,
|
|
128
|
+
zoom, attributes on click) for units, sections, contour lines and spot heights; a *Sezioni*
|
|
129
|
+
pane with the drawings; a log pane with level filter, search, copy and
|
|
130
|
+
save; error windows with a hint, the traceback and a *Copia* button; light and dark themes;
|
|
131
|
+
*Aiuto > Diagnostica ambiente* (the same checks as `ppg doctor`); a PyArchInit menu to
|
|
132
|
+
connect, publish orthophotos, DEMs and models, and write unit polygons and spot heights.
|
|
133
|
+
See [docs/gui.md](docs/gui.md).
|
|
134
|
+
|
|
135
|
+
## Quick start (Python)
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import pyarchinit_photogrammetry as ppg
|
|
139
|
+
|
|
140
|
+
doc = ppg.Document()
|
|
141
|
+
chunk = doc.add_chunk("Saggio 1")
|
|
142
|
+
chunk.add_photos(["foto/"])
|
|
143
|
+
chunk.detect_markers("aruco_4x4_50") # coded targets, decoded automatically
|
|
144
|
+
chunk.match_photos(quality="high")
|
|
145
|
+
chunk.align_cameras()
|
|
146
|
+
chunk.import_reference("gcp.csv", crs="EPSG:3004")
|
|
147
|
+
report = chunk.update_transform() # GCP georeferencing + accuracy report
|
|
148
|
+
print(report["control_rmse"])
|
|
149
|
+
|
|
150
|
+
chunk.build_depth_maps(quality="medium")
|
|
151
|
+
chunk.build_point_cloud()
|
|
152
|
+
chunk.build_model() # Poisson; surface="height_field" for terrain
|
|
153
|
+
chunk.build_texture()
|
|
154
|
+
chunk.build_dem() # GeoTIFF in the chunk CRS
|
|
155
|
+
chunk.build_orthomosaic() # GeoTIFF RGBA, true-ortho with occlusion test
|
|
156
|
+
chunk.generate_report("report.html")
|
|
157
|
+
doc.save("scavo.ppg")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The same workflow from the command line:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
ppg markers sheet targets.pdf --codes 0-23 --size-mm 40 # print the targets
|
|
164
|
+
ppg process scavo.ppg foto/ --gcp gcp.csv --crs EPSG:3004 # whole pipeline + report
|
|
165
|
+
ppg export scavo.ppg --ortho ortho.tif --dem dem.tif --model model.obj --cloud cloud.las
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Scripts can work on an open project, as in the GUI: `ppg run myscript.py --project scavo.ppg`,
|
|
169
|
+
then `ppg.app.document` inside the script (in the GUI: **Strumenti > Esegui script Python**).
|
|
170
|
+
|
|
171
|
+
## What it does
|
|
172
|
+
|
|
173
|
+
| Area | Features |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| Alignment | SIFT features and matching, incremental SfM (COLMAP via pycolmap), camera calibration, bundle adjustment, tie points, pair selection (exhaustive, sequential, by GNSS position) |
|
|
176
|
+
| Dense | Depth maps and fusion on CPU (built-in), COLMAP PatchMatch on NVIDIA GPUs, OpenMVS when installed |
|
|
177
|
+
| Products | Point cloud (PLY/LAS 1.4 with classes), mesh (Poisson in an isolated process with automatic fallback, or 2.5D), texture atlas (OBJ+MTL+JPG), DEM, orthomosaic, hillshade, slope, HTML report |
|
|
178
|
+
| Markers | Printable ArUco/AprilTag sheets with scale bar, turntable mats with known layout, sub-pixel detection, triangulation, GCP/check points, scale bars, weighted 7-parameter transformation, any EPSG CRS (geographic GCPs go to the UTM zone) |
|
|
179
|
+
| Archaeology | Vertical sections and parallel cross sections, plan slices at a given elevation, DXF/SVG drawings at scale, contour lines, cut/fill volumes, pottery: rotation axis from sherds, profile, rim/base diameter, height, wall thickness, drawing with filled section |
|
|
180
|
+
| BIM | IFC4 export with IfcMapConversion + IfcProjectedCRS georeferencing, one element per stratigraphic unit with `Pset_PyArchInit_US` |
|
|
181
|
+
| AI | Rule-based classification (ground, vegetation, masonry, noise) with archaeological LAS classes 64-71; Segment Anything 2.1 on orthomosaics and photos, masks projected to the point cloud, automatic candidate units |
|
|
182
|
+
| PyArchInit | Reads `config.cfg` and the database (SQLite/SpatiaLite or PostgreSQL/PostGIS) without QGIS; publishes orthophotos, DEMs and 3D models as media with PyArchInit thumbnails, documentation records and footprints, US links, US polygons, spot heights, section traces, volumes |
|
|
183
|
+
|
|
184
|
+
## PyArchInit integration
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from pyarchinit_photogrammetry.pyarchinit import PyArchInit
|
|
188
|
+
|
|
189
|
+
pa = PyArchInit.connect() # the database configured in PyArchInit
|
|
190
|
+
pa.publish_orthomosaic(chunk, sito="Lagoi", nome_doc="Ortofoto saggio 1",
|
|
191
|
+
link_us=[("1", "101"), ("1", "102")])
|
|
192
|
+
pa.publish_dem(chunk, sito="Lagoi", nome_doc="DEM saggio 1")
|
|
193
|
+
pa.publish_model(chunk, sito="Lagoi", nome_doc="Modello saggio 1")
|
|
194
|
+
units = pa.us_polygons("Lagoi") # US polygons as shapely geometries
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Conventions follow PyArchInit 5 (branch `Stratigraph_00001`): `media_table`,
|
|
198
|
+
`media_thumb_table` (`{id}_{name}_thumb.png` 150 px, `{id}_{name}.png` 2008x1417),
|
|
199
|
+
`media_to_entity_table` (`US`/`us_table`, `REPERTO`, `CERAMICA`, ...), `documentazione_table`
|
|
200
|
+
(`Fotopiano`, `DEM`, `Modello tridimensionale`, source `Fotogrammetria`), footprints in
|
|
201
|
+
`pyarchinit_documentazione`, polygons in `pyunitastratigrafiche`, heights in
|
|
202
|
+
`pyarchinit_quote`, sections in `pyarchinit_sezioni`, volumes in `computo_metrico_table`.
|
|
203
|
+
`entity_uuid` is filled when the column exists. Rasters are copied next to the thumbnail
|
|
204
|
+
folder (`<media>/fotogrammetria/<site>/`). See [docs/pyarchinit.md](docs/pyarchinit.md).
|
|
205
|
+
|
|
206
|
+
## Pottery from a turntable
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
ppg markers turntable mat.pdf --n 12 --diameter-mm 180 # also writes mat.csv
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Photograph the vessel or sherd on the mat, process with `--gcp mat.csv` (local frame, mm
|
|
213
|
+
accuracy), then:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
ppg pottery model.obj --out profilo.svg --scale 1 --title "US 101 n. 34"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The axis is estimated from the surface normals (works on sherds); with the turntable you
|
|
220
|
+
can impose it with `--axis 0,0,0,0,0,1`.
|
|
221
|
+
|
|
222
|
+
## Accuracy check
|
|
223
|
+
|
|
224
|
+
The test suite renders a synthetic trench (4 x 3 m, a wall stub, 8 targets at known
|
|
225
|
+
EPSG:3004 coordinates, 28 photos with lens distortion) and checks the products
|
|
226
|
+
metrically: all photos aligned, control RMSE < 2 mm, independent check points < 3 mm, DEM
|
|
227
|
+
median error < 3 mm, orthophoto target positions < 5 mm.
|
|
228
|
+
|
|
229
|
+
## Architecture
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
pyarchinit_photogrammetry/
|
|
233
|
+
core/ Document, Chunk, Camera, Sensor, Marker, ScaleBar, PointCloud, CRS
|
|
234
|
+
engine/ sfm (pycolmap), dense, meshing, isolate (crash-proof workers), texturing, dem, ortho
|
|
235
|
+
markers/ coded targets, GCP import/export, georeferencing
|
|
236
|
+
archaeo/ sections, pottery, terrain (contours, volumes), stratigraphy (units)
|
|
237
|
+
bim/ IFC export
|
|
238
|
+
ai/ classification, SAM 2.1
|
|
239
|
+
pyarchinit/ config.cfg, database, media, high-level bridge
|
|
240
|
+
io/ EXIF, exports, report
|
|
241
|
+
gui/ desktop interface (PySide6, pyvista, qtawesome icons, themes, log, start page)
|
|
242
|
+
doctor.py environment diagnosis (`ppg doctor`)
|
|
243
|
+
demo.py demo data set
|
|
244
|
+
cli.py `ppg` command
|
|
245
|
+
packaging/ PyInstaller spec and build script for the desktop application
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Projects are a JSON file (`name.ppg`) plus a folder (`name.ppg.files/`) with the data of each
|
|
249
|
+
chunk; photo paths inside the project folder are stored relative, so projects can be moved.
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
GNU GPL v2 or later, like PyArchInit. Third-party engines keep their licences (COLMAP BSD,
|
|
254
|
+
OpenMVS AGPL when used as an external program, SAM 2.1 Apache 2.0).
|
|
255
|
+
|
|
256
|
+
Author: Enzo Cocca
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyarchinit-photogrammetry"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Open-source photogrammetry engine for archaeology and BIM, integrated with PyArchInit"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-2.0-or-later"
|
|
@@ -37,14 +37,15 @@ dependencies = [
|
|
|
37
37
|
]
|
|
38
38
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
|
-
mesh = ["open3d>=0.18"]
|
|
40
|
+
mesh = ["open3d>=0.18,!=0.20.0"]
|
|
41
41
|
bim = ["ifcopenshell>=0.8"]
|
|
42
42
|
postgis = ["psycopg2-binary>=2.9"]
|
|
43
43
|
sam = ["torch>=2.3", "sam2>=1.1"]
|
|
44
|
-
gui = ["PySide6>=6.6", "pyvista>=0.43", "pyvistaqt>=0.
|
|
45
|
-
all = ["open3d>=0.18", "ifcopenshell>=0.8", "psycopg2-binary>=2.9", "PySide6>=6.6", "pyvista>=0.43",
|
|
46
|
-
"pyvistaqt>=0.
|
|
44
|
+
gui = ["PySide6>=6.6", "pyvista>=0.43", "pyvistaqt>=0.13", "qtawesome>=1.3"]
|
|
45
|
+
all = ["open3d>=0.18,!=0.20.0", "ifcopenshell>=0.8", "psycopg2-binary>=2.9", "PySide6>=6.6", "pyvista>=0.43",
|
|
46
|
+
"pyvistaqt>=0.13", "qtawesome>=1.3"]
|
|
47
47
|
dev = ["pytest>=7"]
|
|
48
|
+
build = ["pyinstaller>=6.10", "pyinstaller-hooks-contrib>=2024.8", "pillow>=9.1"]
|
|
48
49
|
|
|
49
50
|
[project.scripts]
|
|
50
51
|
ppg = "pyarchinit_photogrammetry.cli:main"
|
|
@@ -19,13 +19,20 @@ Quick start::
|
|
|
19
19
|
chunk.build_orthomosaic()
|
|
20
20
|
doc.save("scavo.ppg")
|
|
21
21
|
"""
|
|
22
|
-
from .
|
|
22
|
+
from ._env import load_open3d_first as _load_open3d_first
|
|
23
|
+
from ._env import use_bundled_proj_data as _use_bundled_proj_data
|
|
24
|
+
|
|
25
|
+
_load_open3d_first() # Open3D's OpenMP runtime must be bound before rasterio/scipy (see _env.py)
|
|
26
|
+
|
|
27
|
+
_use_bundled_proj_data() # before anything opens a CRS (see _env.py)
|
|
28
|
+
|
|
29
|
+
from .app import app # noqa: E402
|
|
23
30
|
from .core import (ARCHAEO_CLASSES, ASPRS_CLASSES, Camera, Chunk, Document, Marker, PointCloud, Projection,
|
|
24
31
|
Reference, ScaleBar, Sensor)
|
|
25
32
|
from .core.progress import Cancelled, Progress, print_progress
|
|
26
33
|
from . import demo # noqa: E402 (ppg.demo.create_demo)
|
|
27
34
|
|
|
28
|
-
__version__ = "0.
|
|
35
|
+
__version__ = "0.3.0"
|
|
29
36
|
|
|
30
37
|
__all__ = ["app", "Document", "Chunk", "Camera", "Sensor", "Marker", "Projection", "Reference", "ScaleBar",
|
|
31
38
|
"PointCloud", "ASPRS_CLASSES", "ARCHAEO_CLASSES", "Progress", "Cancelled", "print_progress", "__version__"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Process-level fixes for mixed installations (Anaconda, QGIS, Homebrew).
|
|
2
|
+
|
|
3
|
+
Anaconda and QGIS export ``PROJ_LIB``/``PROJ_DATA`` (and ``GDAL_DATA``) pointing at
|
|
4
|
+
their own ``proj.db``. The rasterio wheel bundles a newer PROJ, which then refuses that
|
|
5
|
+
database ("proj.db contains DATABASE.LAYOUT.VERSION.MINOR = 2 whereas a number
|
|
6
|
+
>= 3 is expected") and every EPSG lookup fails. When rasterio ships its own PROJ
|
|
7
|
+
data, point its GDAL/PROJ at it *and* rewrite the environment variables: PROJ
|
|
8
|
+
creates one context per thread and each new context reads ``PROJ_DATA`` from the
|
|
9
|
+
environment, so without the rewrite every EPSG lookup in a worker thread (all the
|
|
10
|
+
background tasks of the interface) would still fail. pyproj wheels already prefer
|
|
11
|
+
their bundled data. Nothing happens when rasterio has no bundled data (conda-forge
|
|
12
|
+
or QGIS builds, whose PROJ matches the environment).
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def use_bundled_proj_data() -> str | None:
|
|
21
|
+
"""Return the directory now used by rasterio, or ``None`` when nothing changed."""
|
|
22
|
+
env = os.environ.get("PROJ_DATA") or os.environ.get("PROJ_LIB")
|
|
23
|
+
if not env:
|
|
24
|
+
return None
|
|
25
|
+
try:
|
|
26
|
+
import rasterio
|
|
27
|
+
from rasterio.env import set_proj_data_search_path
|
|
28
|
+
except Exception: # pragma: no cover - rasterio missing or very old
|
|
29
|
+
return None
|
|
30
|
+
bundled = Path(rasterio.__file__).parent / "proj_data"
|
|
31
|
+
if not (bundled / "proj.db").exists():
|
|
32
|
+
return None # conda-forge rasterio: its PROJ matches the environment
|
|
33
|
+
try:
|
|
34
|
+
if any(Path(p).resolve() == bundled.resolve() for p in env.split(os.pathsep) if p):
|
|
35
|
+
return None
|
|
36
|
+
except OSError:
|
|
37
|
+
pass
|
|
38
|
+
set_proj_data_search_path(str(bundled))
|
|
39
|
+
os.environ["PROJ_DATA"] = str(bundled)
|
|
40
|
+
os.environ["PROJ_LIB"] = str(bundled)
|
|
41
|
+
gdal_data = Path(rasterio.__file__).parent / "gdal_data"
|
|
42
|
+
if gdal_data.is_dir() and os.environ.get("GDAL_DATA"):
|
|
43
|
+
os.environ["GDAL_DATA"] = str(gdal_data)
|
|
44
|
+
return str(bundled)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def load_open3d_first() -> None:
|
|
48
|
+
"""Import Open3D before any other native library.
|
|
49
|
+
|
|
50
|
+
Open3D ships its own OpenMP runtime. When it is loaded after the ones that come
|
|
51
|
+
with rasterio, SciPy or scikit-learn, PoissonRecon (``create_from_point_cloud_poisson``)
|
|
52
|
+
crashes the process on macOS (segmentation fault or a silent ``exit(0)``).
|
|
53
|
+
Importing it first, in every process that imports this package, keeps the
|
|
54
|
+
reconstruction stable. Costs under a second; nothing happens without Open3D."""
|
|
55
|
+
import os
|
|
56
|
+
|
|
57
|
+
if os.environ.get("PPG_NO_OPEN3D"):
|
|
58
|
+
return
|
|
59
|
+
try:
|
|
60
|
+
import open3d # noqa: F401
|
|
61
|
+
except Exception: # pragma: no cover - optional dependency
|
|
62
|
+
pass
|
|
@@ -50,7 +50,9 @@ def export_ifc(path: str | Path, elements: list[IfcElement], crs: str | None = N
|
|
|
50
50
|
|
|
51
51
|
model = ifcopenshell.file(schema="IFC4")
|
|
52
52
|
proj = ifcopenshell.api.root.create_entity(model, ifc_class="IfcProject", name=project)
|
|
53
|
-
ifcopenshell.api.unit.assign_unit(model
|
|
53
|
+
ifcopenshell.api.unit.assign_unit(model, length={"is_metric": True, "raw": "METERS"},
|
|
54
|
+
area={"is_metric": True, "raw": "METERS"},
|
|
55
|
+
volume={"is_metric": True, "raw": "METERS"}) # coordinates are metres
|
|
54
56
|
ctx = ifcopenshell.api.context.add_context(model, context_type="Model")
|
|
55
57
|
body = ifcopenshell.api.context.add_context(model, context_type="Model", context_identifier="Body",
|
|
56
58
|
target_view="MODEL_VIEW", parent=ctx)
|
|
@@ -264,14 +264,17 @@ def run(script: Path, project: Optional[Path] = None, args: list[str] = typer.Ar
|
|
|
264
264
|
|
|
265
265
|
@app.command()
|
|
266
266
|
def demo(folder: Path = typer.Argument(Path("ppg_demo"), help="destination folder"),
|
|
267
|
-
process: bool = typer.Option(False, help="also run the whole pipeline (about
|
|
268
|
-
pyarchinit: bool = typer.Option(True, help="create the demo PyArchInit database")
|
|
269
|
-
|
|
267
|
+
process: bool = typer.Option(False, help="also run the whole pipeline (about three minutes)"),
|
|
268
|
+
pyarchinit: bool = typer.Option(True, help="create the demo PyArchInit database"),
|
|
269
|
+
pottery: bool = typer.Option(True, help="also the turntable photos and project of the bowl")):
|
|
270
|
+
"""Write demo data: trench photos + GCPs, a ready project, pottery meshes and turntable photos, a PyArchInit database."""
|
|
270
271
|
from .demo import create_demo
|
|
271
272
|
|
|
272
|
-
d = create_demo(folder, process=process, with_pyarchinit=pyarchinit, progress=print_progress)
|
|
273
|
+
d = create_demo(folder, process=process, with_pyarchinit=pyarchinit, with_pottery=pottery, progress=print_progress)
|
|
273
274
|
typer.echo(f"Project: {d.project}")
|
|
274
275
|
typer.echo(f"Pottery: {d.vessel} {d.sherd}")
|
|
276
|
+
if d.pottery_project:
|
|
277
|
+
typer.echo(f"Turntable: {d.pottery_project}" + (f" profile: {d.pottery_profile}" if d.pottery_profile else ""))
|
|
275
278
|
if d.pyarchinit_home:
|
|
276
279
|
typer.echo(f"PyArchInit: export PYARCHINIT_HOME={d.pyarchinit_home}")
|
|
277
280
|
typer.echo(f"Read {d.folder / 'LEGGIMI.txt'}; open it in the interface with: ppg-gui {d.project}")
|
|
@@ -285,6 +288,14 @@ def gui(project: Optional[Path] = typer.Argument(None)):
|
|
|
285
288
|
raise SystemExit(gui_main([str(project)] if project else []))
|
|
286
289
|
|
|
287
290
|
|
|
291
|
+
@app.command()
|
|
292
|
+
def doctor():
|
|
293
|
+
"""Check the environment (libraries, known pitfalls) and print what to fix."""
|
|
294
|
+
from .doctor import main as doctor_main
|
|
295
|
+
|
|
296
|
+
raise SystemExit(doctor_main())
|
|
297
|
+
|
|
298
|
+
|
|
288
299
|
# ------------------------------------------------------------------ markers
|
|
289
300
|
@markers_app.command("sheet")
|
|
290
301
|
def markers_sheet(out: Path, target_type: str = "aruco_4x4_50", codes: str = "0-11", size_mm: float = 40.0,
|