From f4f650fa98e9d6007ce661be9ee8d1c676136e40 Mon Sep 17 00:00:00 2001 From: Cass Date: Wed, 29 Jul 2026 21:59:32 -0400 Subject: [PATCH] Give PoissonRecon a writable temp directory --- opendm/mesh.py | 5 +++++ tests/test_mesh.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/test_mesh.py diff --git a/opendm/mesh.py b/opendm/mesh.py index 584458bfb..bb6200b97 100644 --- a/opendm/mesh.py +++ b/opendm/mesh.py @@ -136,6 +136,9 @@ def dem_to_mesh_gridded(inGeotiff, outMesh, maxVertexCount, maxConcurrency=1): def screened_poisson_reconstruction(inPointCloud, outMesh, depth = 8, samples = 1, maxVertexCount=100000, pointWeight=4, threads=context.num_cores): mesh_path, mesh_filename = os.path.split(outMesh) + # PoissonRecon otherwise writes out-of-core PR_* files to the caller's + # working directory, which may be read-only even when mesh_path is writable. + tempdir = os.path.abspath(mesh_path or '.') # mesh_path = path/to # mesh_filename = odm_mesh.ply @@ -157,6 +160,7 @@ def screened_poisson_reconstruction(inPointCloud, outMesh, depth = 8, samples = 'bin': context.poisson_recon_path, 'outfile': outMeshDirty, 'infile': inPointCloud, + 'tempdir': tempdir, 'depth': depth, 'samples': samples, 'pointWeight': pointWeight, @@ -167,6 +171,7 @@ def screened_poisson_reconstruction(inPointCloud, outMesh, depth = 8, samples = try: system.run('"{bin}" --in "{infile}" ' '--out "{outfile}" ' + '--tempDir "{tempdir}" ' '--depth {depth} ' '--pointWeight {pointWeight} ' '--samplesPerNode {samples} ' diff --git a/tests/test_mesh.py b/tests/test_mesh.py new file mode 100644 index 000000000..04901e4c8 --- /dev/null +++ b/tests/test_mesh.py @@ -0,0 +1,47 @@ +import os +import tempfile +import unittest +from unittest.mock import patch + +from opendm import mesh + + +class TestMesh(unittest.TestCase): + def test_poisson_uses_output_directory_for_temporary_files(self): + with tempfile.TemporaryDirectory() as root: + mesh_dir = os.path.join(root, "mesh output") + os.mkdir(mesh_dir) + point_cloud = os.path.join(root, "cloud.ply") + output = os.path.join(mesh_dir, "odm_mesh.ply") + dirty = os.path.join(mesh_dir, "odm_mesh.dirty.ply") + commands = [] + + def run(command): + commands.append(command) + if "--linearFit" in command: + with open(dirty, "wb"): + pass + + with patch.object(mesh.system, "run", side_effect=run): + with patch.object( + mesh.context, "poisson_recon_path", "PoissonRecon" + ): + with patch.object( + mesh.context, + "omvs_reconstructmesh_path", + "ReconstructMesh", + ): + result = mesh.screened_poisson_reconstruction( + point_cloud, output, threads=3 + ) + + self.assertEqual(result, output) + self.assertEqual(len(commands), 2) + self.assertIn( + '--tempDir "{}"'.format(os.path.abspath(mesh_dir)), + commands[0], + ) + + +if __name__ == "__main__": + unittest.main()