Packaged desktop application¶
Ready-to-run builds of the interface, with Python and every library inside, are produced by PyInstaller:
| Platform | File | Run |
|---|---|---|
| macOS 15 or newer (Apple Silicon) | PyArchInit-Photogrammetry-<version>-macos-arm64.dmg |
open the disk image, drag the app to Applications |
| Windows 10/11 (64 bit) | PyArchInit-Photogrammetry-<version>-windows-x64.zip |
unzip, run PyArchInit Photogrammetry.exe |
They are attached to every GitHub Release
and produced by the desktop workflow (.github/workflows/desktop.yml, runners macos-15
and windows-latest). The packaged application contains the same code as the pip
package: Open3D, VTK, qtawesome and IfcOpenShell are included; Segment Anything (PyTorch)
is not, and is installed on first use in a separate environment (see
integrations.md).
First start¶
- macOS: the application is not signed with an Apple developer certificate. Gatekeeper shows "cannot be opened because the developer cannot be verified" (or "is damaged" on the newest systems). Right-click the app and choose Open once, or remove the quarantine attribute from the terminal:
xattr -dr com.apple.quarantine "/Applications/PyArchInit Photogrammetry.app"
-
Windows: SmartScreen may ask for confirmation the first time (More info > Run anyway). Writing geometries into a PyArchInit SQLite database needs
mod_spatialite.dll: it is found in a QGIS or OSGeo4W installation, or setSPATIALITE_LIBRARY_PATH. -
Windows on ARM (Snapdragon laptops, Windows 11 in Parallels on a Mac with Apple Silicon): the x64 application runs under the emulation of Windows 11 - checked on Windows 11 ARM64 with
--checkand--doctor(Python, pycolmap, Open3D, OpenCV, rasterio, Qt and VTK load) and with the window.
Logs are written in ~/Library/Logs/pyarchinit-photogrammetry (macOS) or
%LOCALAPPDATA%\pyarchinit-photogrammetry\logs (Windows); Aiuto > Apri cartella dei log
opens the folder, Aiuto > Diagnostica ambiente runs the same checks as ppg doctor.
The application runs Python scripts too, with its own Python and modules and without the
window, as Metashape does with -r (the exit code is the script's):
"/Applications/PyArchInit Photogrammetry.app/Contents/MacOS/PyArchInit Photogrammetry" --run script.py args
"PyArchInit Photogrammetry.exe" --run script.py args > out.txt 2>&1 # Windows: no console, redirect
Building locally¶
pip install -e ".[all,build]"
pip install ./native # native kernels (Rust toolchain needed), bundled when installed
python packaging/build.py # icons, PyInstaller, smoke test (--check), dmg/zip in dist/
python packaging/build.py --no-smoke
packaging/ppg_gui.spec: PyInstaller spec (one-folder build, windowed, the hooks for rasterio, pyproj, Open3D, VTK, qtawesome and pycolmap data, Qt modules that are not used are excluded).packaging/launcher.py: entry point; callsmultiprocessing.freeze_support()first so the isolated worker processes (Poisson reconstruction) work in the frozen application.packaging/build.py: renders the icons fromgui/resources/logo.svg, runs PyInstaller, rewrites the references of each package's private libraries back to@loader_path(macOS: rasterio and pyproj ship different builds oflibprojunder the same name, and PyInstaller would otherwise make them share one copy), starts the result with--check(offscreen) and--doctor, then writes the.dmg(hdiutil) or the.zip.
The smoke test opens the main window, renders it and exits with code 0; the build fails
when the window does not appear. Its line also says whether the native core is inside
(native: 0.1.0).
Native core (Rust)¶
native/ holds pyarchinit-photogrammetry-core, imported as ppg_core: the kernels that the
Python code ran in numpy and that dominated the CPU time of the depth maps and of the
orthomosaic - fusion of the depth estimates of a photo, disparity back to depth, normals,
projection of points with the COLMAP camera models, scoring of the photos of an orthomosaic
tile, agreement of points with a depth map (confidence). PyO3 + rayon, one abi3 wheel per
platform for Python 3.10 and later. It is optional: without it (or with PPG_NATIVE=0) the
numpy code runs, and tests/test_native.py checks that both give the same maps, pixels and
counts. Measured on the synthetic trench, CPU depth maps of 48 photos: 5.7 s with numpy one
photo at a time, 1.6 s with the native kernels, 0.9 s with six photos at a time.
From 0.2.0 the core also holds PatchMatch multi-view stereo (patchmatch_depth, the depth-map
backend patchmatch, which the automatic choice takes for surveys from the air: photos whose GPS
positions spread over the site). It has no numpy version (it would be far too slow): without the core
auto falls back to two-view SGBM, the backend is not offered in the window and the step stops with a
message when asked for it by name.
pip install ./native # build and install (maturin is fetched by pip)
maturin build --release -m native/Cargo.toml # a wheel in native/target/wheels
PPG_NATIVE=0 ppg ... # the numpy code, for comparison
The release profile keeps the symbols: the library stripped by Rust's default release profile
is refused by the dynamic loader of macOS 26/27 ("mis-aligned LINKEDIT string pool"). The
native workflow builds the wheels (macOS arm64 and x86-64, Windows x64, Linux x86-64 and
aarch64) and, on a core-v* tag, publishes them to PyPI; the tests workflow runs Python 3.12
with the native core and 3.11 without it; the desktop workflow bundles it in the applications.
Linux on ARM (aarch64): pycolmap and Open3D¶
pycolmap 4.2.1 and Open3D 0.19.0 publish no wheels for Linux on ARM (Open3D 0.20.0 does, but its
Poisson reconstruction crashes and the requirements exclude it). packaging/arm/build-linux.sh
builds them in Docker on an arm64 machine - a Mac with Apple Silicon, an arm64 Linux host - and
the arm-wheels workflow does the same on GitHub's arm64 runners, then runs the tests of this
project with them (on demand; the wheels stay as an artifact, or are attached to a release whose
tag is given).
packaging/arm/build-linux.sh # both, Python 3.10-3.12, wheels in dist/arm
PYTHONS=3.12 JOBS=3 packaging/arm/build-linux.sh pycolmap
packaging/arm/build-linux.sh test # the tests with them (Ubuntu 24.04, Python 3.12)
pip install dist/arm/pycolmap-*-cp312-*.whl dist/arm/open3d-*-cp312-*.whl
pip install "pyarchinit-photogrammetry[all]"
- pycolmap: COLMAP's own recipe for its x86-64 wheels (the manylinux_2_28 image, vcpkg, GCC 12)
with an arm64 triplet;
manylinux_2_28_aarch64wheels (glibc 2.28 or later). - Open3D: Open3D's own arm64 recipe (Ubuntu 20.04, OpenBLAS, VTK and Embree built from source)
without its new GUI (Filament), which this program does not use;
manylinux_2_31_aarch64wheels (glibc 2.31 or later), package nameopen3d. - The work goes to folders of the host, not to Docker's own disk (which only keeps the two
builder images); on a Mac to a case-sensitive disk image,
build/arm.sparseimage, because Linux packages hold names that differ only in case (hdiutil detach build/armcloses it). - A few files of COLMAP and Open3D need several GB each to compile:
JOBScompilers at a time (3 by default, for a Docker with 8 GB), the ones that run out of memory compiled again alone. - Open3D on ARM links the shared
libgfortran(put into the wheel by auditwheel): with the static one, as Open3D's own ARM build does, the module cannot be imported ("cannot allocate memory in static TLS block"). pye57(E57 scans, in[all]) has no wheel for aarch64 either: it is compiled when installed and needs the Xerces-C headers (sudo apt install libxerces-c-devon Debian and Ubuntu).
Signing, notarization, TestFlight¶
The builds are unsigned. Two Apple routes exist:
- Developer ID + notarization (download from the web/GitHub, no warnings): needs a
Developer ID Application certificate (created by the Account Holder of the Apple
Developer team) and a notary profile (
xcrun notarytool store-credentials). ThenPPG_CODESIGN_IDENTITY="Developer ID Application: ... (TEAMID)" PPG_NOTARY_PROFILE=<profile> python packaging/build.pysigns every binary with the hardened runtime (packaging/entitlements.plist), signs and notarizes the dmg, and staples the ticket. - TestFlight / Mac App Store: needs an Apple Distribution certificate, a provisioning
profile and the App Sandbox. The sandbox forbids what this application relies on:
loading
mod_spatialitefrom Homebrew/QGIS, starting worker processes from other interpreters (isolated Poisson, SAM environment), downloading and runninguv, writing next to the user's projects without a file dialog, opening QGIS. A sandboxed edition would need those parts redesigned, so Developer ID is the recommended channel.