kopia lustrzana https://github.com/OpenDroneMap/ODM
Fix ply (#1829)
* Create README.md for fix_ply * Create fix_ply.py * Update fix_ply.py to match current * Update README.md to match Jaime's description * Add more descriptionpull/1888/head
rodzic
4aa9cae393
commit
c6d94d6a64
|
@ -0,0 +1,19 @@
|
||||||
|
# Fix Ply
|
||||||
|
|
||||||
|
Use to translate a modified ply into a compatible format for subsequent steps in ODM. Via Jaime Chacoff, https://community.opendronemap.org/t/edited-point-cloud-with-cloudcompare-wont-rerun-from-odm-meshing/21449/6
|
||||||
|
|
||||||
|
The basic idea is to process through ODM until the point cloud is created, use a 3rd party tool, like CloudCompare to edit the point cloud, and then continue processing in OpenDroneMap.
|
||||||
|
|
||||||
|
This useful bit of python will convert the PLY exported from CloudCompare back into a compatible format for continued processing in OpenDroneMap.
|
||||||
|
|
||||||
|
1. Run project in WebODM and add this to your settings: `end-with: odm-filterpoints`
|
||||||
|
1. Once complete, go to your NodeODM container and copy `/var/www/data/[Task ID]/odm-filterpoints` directory
|
||||||
|
1. Open CloudCompare and from `odm-filterpoints` directory you've copied, open `point_cloud.ply`
|
||||||
|
1. In the box that pops up, add a scalar field `vertex - views`
|
||||||
|
1. To see the actual colours again - select the point cloud, then in properties change colours from "Scalar field" to "RGB"
|
||||||
|
1. Make your changes to the point cloud
|
||||||
|
1. Compute normals (Edit > Normals > Compute)
|
||||||
|
1. Save PLY file as ASCII
|
||||||
|
1. Run Python file above to fix PLY file and convert to binary
|
||||||
|
1. Copy `odm_filterpoints` directory (or just `point_cloud.ply`) back into NodeODM container
|
||||||
|
1. Restart project in WebODM "From Meshing" (don't forget to edit settings to remove `end-with: odm-filterpoints` or it's not going to do anything).
|
|
@ -0,0 +1,68 @@
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from plyfile import PlyData, PlyElement
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
def pcd_ascii_to_binary_ply(ply_file: str, binary_ply: str) -> None:
|
||||||
|
"""Converts ASCII PLY to binary, ensuring 'views' is present and of type uchar.
|
||||||
|
Raises ValueError if neither 'scalar_views' nor 'views' is found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
logging.info(f"Reading ASCII PLY file: {ply_file}")
|
||||||
|
ply_data: PlyData = PlyData.read(ply_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.error(f"File not found: {ply_file}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error reading PLY file: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
new_elements: list[PlyElement] = []
|
||||||
|
|
||||||
|
for element in ply_data.elements:
|
||||||
|
new_data = element.data.copy()
|
||||||
|
|
||||||
|
if 'scalar_views' in element.data.dtype.names:
|
||||||
|
new_data['views'] = new_data['scalar_views'].astype('u1')
|
||||||
|
del new_data['scalar_views']
|
||||||
|
elif 'views' in element.data.dtype.names:
|
||||||
|
new_data['views'] = new_data['views'].astype('u1')
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Neither 'scalar_views' nor 'views' found - did you import them when opened the file in CloudCompare?")
|
||||||
|
|
||||||
|
|
||||||
|
new_element = PlyElement.describe(new_data, element.name)
|
||||||
|
new_elements.append(new_element)
|
||||||
|
|
||||||
|
new_ply_data = PlyData(new_elements, text=False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
logging.info(f"Writing binary PLY file: {binary_ply}")
|
||||||
|
new_ply_data.write(binary_ply)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error writing PLY file: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("PLY conversion complete.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
base: str = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ply_file: str = os.path.join(base, 'point_cloud_ascii.ply')
|
||||||
|
binary_ply_file: str = os.path.join(base, 'point_cloud.ply')
|
||||||
|
|
||||||
|
if not os.path.exists(ply_file):
|
||||||
|
logging.error(f"Input file not found: {ply_file}")
|
||||||
|
exit(1) # Exit with error code
|
||||||
|
|
||||||
|
try:
|
||||||
|
pcd_ascii_to_binary_ply(ply_file, binary_ply_file)
|
||||||
|
except ValueError as e:
|
||||||
|
logging.error(f"PLY conversion failed: {e}")
|
||||||
|
exit(1) # Exit with error code to indicate failure
|
Ładowanie…
Reference in New Issue