From 56bd56914be0e23b529855a0a644551501ecd460 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Fri, 6 Mar 2026 03:50:27 -0500 Subject: [PATCH 1/4] Use plugin venv - Setts the -E flag to tell Python to ignore `PYTHONPATH` and `PYTHONHOME` so Meshroom's injected venv path can't contaminate the subprocess's package resolution. --- meshroom/geolocation/Download2dMap.py | 11 ++++++++--- meshroom/geolocation/DownloadLidar3dMap.py | 11 ++++++++--- meshroom/geolocation/DownloadTopography3dMap.py | 11 ++++++++--- meshroom/geolocation/GeolocationLidarLasToMesh.py | 11 ++++++++--- meshroom/geolocation/MergeLidarLas.py | 11 ++++++++--- meshroom/geolocation/North.py | 11 ++++++++--- meshroom/geolocation/Sun.py | 11 ++++++++--- meshroom/geolocation/WeatherHDRI.py | 11 ++++++++--- 8 files changed, 64 insertions(+), 24 deletions(-) diff --git a/meshroom/geolocation/Download2dMap.py b/meshroom/geolocation/Download2dMap.py index 4fe5782..3ef627e 100644 --- a/meshroom/geolocation/Download2dMap.py +++ b/meshroom/geolocation/Download2dMap.py @@ -16,11 +16,16 @@ class Download2dMap(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/download2dMap.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/DownloadLidar3dMap.py b/meshroom/geolocation/DownloadLidar3dMap.py index 2c7a02e..67de742 100644 --- a/meshroom/geolocation/DownloadLidar3dMap.py +++ b/meshroom/geolocation/DownloadLidar3dMap.py @@ -16,11 +16,16 @@ class DownloadLidar3dMap(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/downloadLidar3dMap.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/DownloadTopography3dMap.py b/meshroom/geolocation/DownloadTopography3dMap.py index 8042b5b..f315119 100644 --- a/meshroom/geolocation/DownloadTopography3dMap.py +++ b/meshroom/geolocation/DownloadTopography3dMap.py @@ -16,11 +16,16 @@ class DownloadTopography3dMap(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/DEMto3DFULL.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' diff --git a/meshroom/geolocation/GeolocationLidarLasToMesh.py b/meshroom/geolocation/GeolocationLidarLasToMesh.py index fbf1a8d..c8344bb 100644 --- a/meshroom/geolocation/GeolocationLidarLasToMesh.py +++ b/meshroom/geolocation/GeolocationLidarLasToMesh.py @@ -16,11 +16,16 @@ class GeolocationLidarLasToMesh(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/lidarLasToMesh.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/MergeLidarLas.py b/meshroom/geolocation/MergeLidarLas.py index 4fb715c..c051f6b 100644 --- a/meshroom/geolocation/MergeLidarLas.py +++ b/meshroom/geolocation/MergeLidarLas.py @@ -10,11 +10,16 @@ class MergeLidarLas(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/mergeLidarLas.py").resolve() - commandLine = pythonPath.as_posix() + ' ' + targetScriptPath.as_posix() + ' {allParams}' + commandLine = pythonPath.as_posix() + ' -E ' + targetScriptPath.as_posix() + ' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/North.py b/meshroom/geolocation/North.py index e4ccde5..91b152d 100644 --- a/meshroom/geolocation/North.py +++ b/meshroom/geolocation/North.py @@ -16,11 +16,16 @@ class North(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/north.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/Sun.py b/meshroom/geolocation/Sun.py index 012ab3a..a04ff93 100644 --- a/meshroom/geolocation/Sun.py +++ b/meshroom/geolocation/Sun.py @@ -16,11 +16,16 @@ class Sun(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/sun.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' diff --git a/meshroom/geolocation/WeatherHDRI.py b/meshroom/geolocation/WeatherHDRI.py index e3ee2fb..962c75c 100644 --- a/meshroom/geolocation/WeatherHDRI.py +++ b/meshroom/geolocation/WeatherHDRI.py @@ -16,11 +16,16 @@ class WeatherHDRI(desc.CommandLineNode): currentFilePath = Path(__file__).absolute() currentFileFolderPath = currentFilePath.parent - # Get python environnement or global python - pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", "python")) + # Resolve venv Python if present, then MESHROOM_GEOLOC_PYTHON, then bare 'python' + _plugin_dir = (currentFileFolderPath / "../..").resolve() + _venv_python = next( + (p for p in [_plugin_dir / ".venv/Scripts/python.exe", _plugin_dir / ".venv/bin/python"] if p.exists()), + None + ) + pythonPath = Path(os.environ.get("MESHROOM_GEOLOC_PYTHON", str(_venv_python) if _venv_python else "python")) targetScriptPath = (currentFileFolderPath / "../../scripts/weatherHDRI.py").resolve() - commandLine = pythonPath.as_posix() +' '+ targetScriptPath.as_posix() +' {allParams}' + commandLine = pythonPath.as_posix() +' -E '+ targetScriptPath.as_posix() +' {allParams}' category = 'Geolocation' documentation = ''' From cc43b1176df4210d453dd2c91bda17a87f5c850a Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Fri, 6 Mar 2026 04:00:10 -0500 Subject: [PATCH 2/4] Adds venv setup instructions & script --- README.md | 16 ++++++++++++++-- setup.py | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 setup.py diff --git a/README.md b/README.md index 69a1787..b0a202e 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,17 @@ It is a module for [Meshroom](https://alicevision.org/#meshroom). You can [download pre-compiled binaries for the latest release](https://github.com/alicevision/meshroom/releases). -Get the source code and install runtime requirements: +Get the source code and set up the plugin environment: ```bash git clone --recursive https://github.com/alicevision/MeshroomGeolocation.git cd MeshroomGeolocation -pip install -r requirements.txt +python setup.py ``` +`setup.py` creates a `.venv` inside the plugin folder and installs all required dependencies into it. This isolated environment is used automatically by all plugin nodes — no environment variables need to be set. + +> **Note:** Run `setup.py` with a standard Python 3 installation (not Meshroom's bundled Python). Python 3.11 is recommended to match Meshroom's interpreter. + ## Environment variables Custom nodes can be added to Meshroom by setting the environment variable `MESHROOM_NODES_PATH`. @@ -37,6 +41,14 @@ Here `MESHROOM_PIPELINE_TEMPLATES_PATH = path/to/MeshroomGeolocation/pipelines`. All the pipelines will be available in Pipelines category in Meshroom UI. You can learn more about them [here](#pipelines). +### Optional: override the Python interpreter + +By default the plugin uses the `.venv` created by `setup.py`. To use a different Python interpreter, set `MESHROOM_GEOLOC_PYTHON` to its path before launching Meshroom: + +```bash +set MESHROOM_GEOLOC_PYTHON=C:/path/to/python.exe +``` + ## Plugin System Since recently a plugin system has been added to Meshroom. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b3a37d6 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +""" +Run this script once to create a .venv and install plugin dependencies: + + python setup.py +""" + +import subprocess +import sys +from pathlib import Path + +plugin_dir = Path(__file__).parent +venv_dir = plugin_dir / ".venv" +requirements = plugin_dir / "requirements.txt" + +print(f"Creating virtual environment at {venv_dir} ...") +subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True) + +venv_python = venv_dir / "Scripts" / "python.exe" +if not venv_python.exists(): + venv_python = venv_dir / "bin" / "python" + +print("Installing requirements ...") +subprocess.run([str(venv_python), "-m", "pip", "install", "-r", str(requirements)], check=True) + +print("\nSetup complete. You can now launch Meshroom.") From 4adbb683707dbe547e2c6a519596aee0eeb9bc64 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Fri, 6 Mar 2026 04:00:42 -0500 Subject: [PATCH 3/4] Remove version note Should be easier to keep updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0a202e..81e6840 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ python setup.py `setup.py` creates a `.venv` inside the plugin folder and installs all required dependencies into it. This isolated environment is used automatically by all plugin nodes — no environment variables need to be set. -> **Note:** Run `setup.py` with a standard Python 3 installation (not Meshroom's bundled Python). Python 3.11 is recommended to match Meshroom's interpreter. +> **Note:** Run `setup.py` with a standard Python 3 installation (not Meshroom's bundled Python). ## Environment variables Custom nodes can be added to Meshroom by setting the environment variable `MESHROOM_NODES_PATH`. From 80e06dc5e8654e1f3ceb0eaf77e03682c811282d Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Fri, 6 Mar 2026 04:33:59 -0500 Subject: [PATCH 4/4] Remove spaces in acronym --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81e6840..8c0efb8 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Here are screenshots of them : ![Map 2d pipeline](./external_files/map2d_pipeline.png) -- **Generate Weather H D R I** is also a simple node with an HDRI downloaded for the weather during the dataset. +- **Generate Weather HDRI** is also a simple node with an HDRI downloaded for the weather during the dataset. ![Weather pipeline](./external_files/weather_pipeline.png)