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.
Files changed (93) hide show
  1. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/PKG-INFO +115 -30
  2. pyarchinit_photogrammetry-0.3.0/README.md +256 -0
  3. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/pyproject.toml +6 -5
  4. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/__init__.py +9 -2
  5. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/_env.py +62 -0
  6. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/bim/ifc.py +3 -1
  7. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/cli.py +15 -4
  8. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/chunk.py +26 -0
  9. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/demo.py +62 -5
  10. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/doctor.py +207 -0
  11. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/dense.py +2 -2
  12. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/engine/isolate.py +148 -0
  13. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/engine/meshing.py +237 -0
  14. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/__init__.py +126 -0
  15. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/dialogs.py +603 -0
  16. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/icons.py +122 -0
  17. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/image_views.py +124 -16
  18. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/logging_setup.py +135 -0
  19. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/main_window.py +620 -218
  20. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/model_view.py +1 -1
  21. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/panels.py +869 -0
  22. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/start_page.py +119 -0
  23. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/gui/theme.py +132 -0
  24. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/worker.py +90 -18
  25. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/bridge.py +52 -6
  26. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/database.py +17 -0
  27. pyarchinit_photogrammetry-0.3.0/src/pyarchinit_photogrammetry/pyarchinit/demo_db.py +242 -0
  28. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/media.py +4 -3
  29. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/testing/synthetic.py +172 -1
  30. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/PKG-INFO +115 -30
  31. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/SOURCES.txt +12 -0
  32. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/requires.txt +11 -4
  33. pyarchinit_photogrammetry-0.3.0/tests/test_env.py +39 -0
  34. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_gui.py +8 -15
  35. pyarchinit_photogrammetry-0.3.0/tests/test_gui_layers.py +148 -0
  36. pyarchinit_photogrammetry-0.3.0/tests/test_isolate.py +68 -0
  37. pyarchinit_photogrammetry-0.3.0/tests/test_meshing_fallback.py +50 -0
  38. pyarchinit_photogrammetry-0.3.0/tests/test_pottery_demo.py +51 -0
  39. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_regressions.py +35 -3
  40. pyarchinit_photogrammetry-0.2.0/README.md +0 -177
  41. pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/engine/meshing.py +0 -144
  42. pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/__init__.py +0 -43
  43. pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/dialogs.py +0 -266
  44. pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/gui/panels.py +0 -461
  45. pyarchinit_photogrammetry-0.2.0/src/pyarchinit_photogrammetry/pyarchinit/demo_db.py +0 -94
  46. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/LICENSE +0 -0
  47. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/setup.cfg +0 -0
  48. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/__main__.py +0 -0
  49. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/__init__.py +0 -0
  50. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/classify.py +0 -0
  51. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/ai/sam.py +0 -0
  52. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/app.py +0 -0
  53. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/__init__.py +0 -0
  54. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/pottery.py +0 -0
  55. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/sections.py +0 -0
  56. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/stratigraphy.py +0 -0
  57. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/archaeo/terrain.py +0 -0
  58. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/bim/__init__.py +0 -0
  59. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/__init__.py +0 -0
  60. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/camera.py +0 -0
  61. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/crs.py +0 -0
  62. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/document.py +0 -0
  63. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/geometry.py +0 -0
  64. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/marker.py +0 -0
  65. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/pointcloud.py +0 -0
  66. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/core/progress.py +0 -0
  67. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/__init__.py +0 -0
  68. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/dem.py +0 -0
  69. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/ortho.py +0 -0
  70. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/sfm.py +0 -0
  71. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/texturing.py +0 -0
  72. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/engine/visibility.py +0 -0
  73. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/__main__.py +0 -0
  74. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/gui/scene.py +0 -0
  75. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/__init__.py +0 -0
  76. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/exif.py +0 -0
  77. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/exports.py +0 -0
  78. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/io/report.py +0 -0
  79. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/__init__.py +0 -0
  80. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/gcp.py +0 -0
  81. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/markers/targets.py +0 -0
  82. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/__init__.py +0 -0
  83. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/pyarchinit/config.py +0 -0
  84. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry/testing/__init__.py +0 -0
  85. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/dependency_links.txt +0 -0
  86. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/entry_points.txt +0 -0
  87. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/src/pyarchinit_photogrammetry.egg-info/top_level.txt +0 -0
  88. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_archaeo.py +0 -0
  89. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_bim_ai.py +0 -0
  90. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_geometry.py +0 -0
  91. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_markers.py +0 -0
  92. {pyarchinit_photogrammetry-0.2.0 → pyarchinit_photogrammetry-0.3.0}/tests/test_pipeline.py +0 -0
  93. {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.2.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>=0.18; extra == "mesh"
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.11; extra == "gui"
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>=0.18; extra == "all"
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.11; extra == "all"
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.2)**. Python library, command line and desktop interface (on the same API,
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
- ## Installation
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
- pip install "pyarchinit-photogrammetry[all]" # mesh (Open3D), IFC, PostGIS, desktop GUI
74
- pip install "pyarchinit-photogrammetry[sam]" # optional: Segment Anything 2.1 (PyTorch)
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
- pip install -e ".[all,dev]"
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+. Writing geometries into a PyArchInit **SQLite** database needs `mod_spatialite`
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
- ## Try it with the demo data
132
+ ## Start
94
133
 
95
134
  ```bash
96
- ppg demo ~/ppg_demo --process # synthetic trench photos + 8 targets, pottery meshes, PyArchInit DB
97
- ppg-gui ~/ppg_demo/scavo.ppg # open the processed project in the interface
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
- Or from the interface: **File > Crea e apri progetto demo**. `LEGGIMI.txt` in the folder lists
101
- what to try (sections, contours, pottery profile of the sherd, publishing to PyArchInit).
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
+ | ![Start page](docs/img/gui-start.png) | ![Layers, sections and PyArchInit](docs/img/gui-layers.png) |
170
+ | ![Connection to PyArchInit](docs/img/connect.png) | ![IFC elements per stratigraphic unit](docs/img/ifc-units.png) |
171
+ | ![Bowl from the turntable demo](docs/img/gui-pottery.png) | ![Profile drawing of the bowl](docs/img/demo-profile.png) |
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` (or `ppg gui`) opens the desktop application (Italian UI): workspace with chunks,
106
- cameras, markers and products; 3D view (VTK, with a software fallback renderer); orthomosaic
107
- and DEM view with drawing tools (stratigraphic units, section traces, measurements, spot
108
- heights, SAM 2.1 prompts); photo view with manual marker placement; reference pane with
109
- GCP and check point errors; Python console bound to the project (`doc`, `chunk`); background
110
- processing with progress and cancel; a PyArchInit menu to connect, publish orthophotos, DEMs
111
- and models, and write unit polygons and spot heights. See [docs/gui.md](docs/gui.md).
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, visibility
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
+ | ![Start page](docs/img/gui-start.png) | ![Layers, sections and PyArchInit](docs/img/gui-layers.png) |
109
+ | ![Connection to PyArchInit](docs/img/connect.png) | ![IFC elements per stratigraphic unit](docs/img/ifc-units.png) |
110
+ | ![Bowl from the turntable demo](docs/img/gui-pottery.png) | ![Profile drawing of the bowl](docs/img/demo-profile.png) |
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.2.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.11"]
45
- all = ["open3d>=0.18", "ifcopenshell>=0.8", "psycopg2-binary>=2.9", "PySide6>=6.6", "pyvista>=0.43",
46
- "pyvistaqt>=0.11"]
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 .app import app
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.2.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 two minutes)"),
268
- pyarchinit: bool = typer.Option(True, help="create the demo PyArchInit database")):
269
- """Write demo data: trench photos + GCPs, a ready project, pottery meshes, a PyArchInit database."""
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,