Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3D Mesh Generation from DSM Images from Sat-NeRF output #35

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions Create_DSM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import numpy as np
from PIL import Image
import open3d as o3d
from tqdm import tqdm

# Output paths: Set the paths for saving the results
output_ply_path = 'your_path/output.ply' # Path for the point cloud PLY file
output_mesh_ply_path = 'your_path/mesh_output.ply' # Path for the high-quality mesh PLY file
output_mesh_obj_path = 'your_path/mesh_output.obj' # Path for the high-quality mesh OBJ file

# Display progress bar for DSM Image Load
print("Loading DSM Image...")
with tqdm(total=100, desc="Progress", ncols=100) as pbar:
dsm_image = Image.open('your_tif_path/.tif')
dsm_array = np.array(dsm_image)
pbar.update(20) # 20% completed



# Check if the DSM array is empty or not
if dsm_array.size == 0:
raise ValueError("DSM image data is empty. Please check the input file.")

# Create Ply using vectorized numpy operations: Generate point cloud using vectorized operations
print("Creating Point Cloud...")
h, w = dsm_array.shape # Get the height and width of the DSM image
x, y = np.meshgrid(np.arange(w), np.arange(h)) # Create a grid of x and y coordinates
z = dsm_array.flatten() # Flatten the elevation data into a 1D array
points = np.vstack((x.flatten(), y.flatten(), z)).T # Combine x, y, z coordinates into a single array to form points
pbar.update(20) # 40% completed

# PointCloud Object Creation: Create a point cloud object using Open3D
print("Creating PointCloud Object...")
point_cloud = o3d.geometry.PointCloud()
point_cloud.points = o3d.utility.Vector3dVector(points) # Set the coordinates in the point cloud
pbar.update(20) # 60% completed

# **Normal Estimation**: Estimate normals for the point cloud
print("Estimating Normals...")
point_cloud.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=1.0, max_nn=30))
# The radius and max_nn parameters define the search radius and the number of nearest neighbors to consider; adjust if needed
pbar.update(10) # 70% completed

# Poisson Surface Reconstruction with GPU (if available): Generate a mesh using GPU
print("Performing Poisson Surface Reconstruction...")
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(point_cloud, depth=12, scale=1.1)
# The depth and scale parameters control the level of detail and scaling of the mesh; adjust if needed
pbar.update(10) # 80% completed

# Check if densities array is empty to avoid errors
densities_np = np.asarray(densities)
if densities_np.size == 0:
raise ValueError("Densities array is empty. Poisson reconstruction might have failed. Check the input point cloud.")

# Remove low-density vertices to refine the mesh
print("Refining Mesh...")
vertices_to_remove = densities_np < np.quantile(densities_np, 0.01) # Identify vertices to remove based on density threshold
if np.any(vertices_to_remove):
mesh.remove_vertices_by_mask(vertices_to_remove) # Remove the identified vertices from the mesh
pbar.update(10) # 90% completed

# Save high-quality mesh to PLY and OBJ files
print("Saving Mesh and Point Cloud...")
o3d.io.write_triangle_mesh(output_mesh_ply_path, mesh) # Save as PLY
o3d.io.write_triangle_mesh(output_mesh_obj_path, mesh) # Save as OBJ
o3d.io.write_point_cloud(output_ply_path, point_cloud) # Save the original point cloud to a PLY file
pbar.update(10) # 100% completed

print("Process Completed.")
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
&#128293; &#128293; **UPDATE JUNE 2023 !!!** Have a look at [EO-NeRF](https://rogermm14.github.io/eonerf/), our latest method for multi-view satellite photogrammetry using neural radiance fields. &#128293; &#128293;

---
### Updates and Fixes in the Project
Issue: TypeError with SSIM Calculation
During the execution of the training script, a TypeError: 'module' object is not callable was encountered. This issue was identified within the metrics.py file, specifically in the ssim function. The error occurred because the alias ssim_ for the kornia.losses.ssim function was mistakenly treated as a callable function.

Additional Adjustments in main.py
In addition to fixing the TypeError, other enhancements and clarifications were made in the main.py file to improve the overall functionality and readability of the script:

Ensured proper initialization and configuration of the training setup.
Enhanced logging for better traceability of the training process.
Improved handling of training and validation steps to ensure smooth execution.
Adjusted the code to accommodate various configurations and parameters as required by the project.


### [[Project page]](https://centreborelli.github.io/satnerf)

Expand Down
4 changes: 2 additions & 2 deletions metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

import torch
from kornia.losses import ssim as ssim_
from kornia.losses import ssim_loss

class NerfLoss(torch.nn.Module):
def __init__(self):
Expand Down Expand Up @@ -118,4 +118,4 @@ def ssim(image_pred, image_gt):
image_pred and image_gt: (1, 3, H, W)
important: kornia==0.5.3
"""
return torch.mean(ssim_(image_pred, image_gt, 3))
return torch.mean(ssim_loss(image_pred, image_gt, 3))