新的信息

This commit is contained in:
dongchangxi committed 2024-09-02 16:52:37 +08:00
1 parent c66fc76e10
commit 345790cb05
51 files changed
+5187 -61

No files matched your search

+244
View File
@@ -0,0 +1,244 @@
import bpy
import sys
import open3d as o3d
import numpy as np
from sklearn.neighbors import NearestNeighbors
import os
import argparse
def estimate_point_curvatures(point_cloud, k=30):
point_cloud_points = np.asarray(point_cloud.points)
# Initialize the nearest neighbors finder
nn = NearestNeighbors(n_neighbors=k)
nn.fit(point_cloud_points)
curvatures = {i: 0 for i in range(len(point_cloud.points))}
for i, point in enumerate(point_cloud_points):
# Find the k-nearest neighbors (including the point itself)
distances, indices = nn.kneighbors([point])
neighbors = point_cloud_points[indices[0]]
# Compute the covariance matrix
covariance_matrix = np.cov(neighbors - neighbors.mean(axis=0), rowvar=False)
# Compute the eigenvalues (sorted)
eigenvalues, _ = np.linalg.eigh(covariance_matrix)
eigenvalues.sort()
# Calculate curvature using the smallest eigenvalue
curvature = eigenvalues[0] / sum(eigenvalues)
curvatures[i] = curvature
return curvatures
def compute_adjacent_normal_angle_variance(pcd, pcd_normal, distance_scale):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
adjacent_normal_angles_variance = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i, point in enumerate(pcd.points):
# Search for the k-nearest neighbors of the point, up to 1500
k, idx, dist = kdtree.search_knn_vector_3d(point, 200)
idx = np.asarray(idx)
indices = idx[dist < mean_distance * distance_scale]
current_normal = pcd_normal[i]
# Calculate dot products between the current normal and its neighbors' normals
dot_products = np.dot(pcd_normal[indices], current_normal)
# Handle any potential NaN values in dot products
valid_dot_products = np.isfinite(dot_products)
dot_products = dot_products[valid_dot_products]
# Clip dot product values to valid range for arccos
dot_products = np.clip(dot_products, -1.0, 1.0)
# Compute angles in degrees and adjust for any NaNs which might be due to numerical errors
angles_array = np.degrees(np.arccos(dot_products))
# Set the angle with itself to zero since it's not meaningful
angles_array[indices == i] = 0
# Calculate the mean angle excluding the zero for the current point
valid_angles = angles_array[indices != i]
if valid_angles.size > 0:
adjacent_normal_angles_variance[i] = np.std(valid_angles)
else:
adjacent_normal_angles_variance[i] = 0 # Default to 0 if no valid neighbors exist
return adjacent_normal_angles_variance
def compute_adjacent_point_vector_degrees_mean(pcd, pcd_normal, distance_scale):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
adjacent_point_vector_degrees_mean = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i, point in enumerate(pcd.points):
# Search for the k-nearest neighbors of the point, up to 200
k, idx, dist = kdtree.search_knn_vector_3d(point, 200)
idx = np.asarray(idx)
indices = idx[dist < mean_distance * distance_scale]
indices = indices[indices != i]
# Retrieve the normal of the current point
current_normal = pcd_normal[i]
adjacent_point_vector = np.asarray(pcd.points)[indices] - point
adjacent_point_vector_cos = np.dot(adjacent_point_vector, current_normal) / np.linalg.norm(adjacent_point_vector, axis = 1) * 1
valid_adjacent_point_vector_cos = np.isfinite(adjacent_point_vector_cos)
adjacent_point_vector_cos = adjacent_point_vector_cos[valid_adjacent_point_vector_cos]
if len(adjacent_point_vector_cos) > 0:
adjacent_point_vector_degrees = np.degrees(np.arccos(adjacent_point_vector_cos))
adjacent_point_vector_mean_degrees = np.mean(adjacent_point_vector_degrees)
adjacent_point_vector_degrees_mean[i] = adjacent_point_vector_mean_degrees
else:
adjacent_point_vector_degrees_mean[i] = 0
return adjacent_point_vector_degrees_mean
def spread_point(pcd, selected_index, distance_scale, count = False):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
if count is False:
spread_selected_index = set()
for i in selected_index:
k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200)
idx = np.asarray(idx)
dist = np.asarray(dist)
indices = idx[dist < mean_distance * distance_scale]
spread_selected_index.update(indices)
spread_selected_index = np.array(list(spread_selected_index))
else:
spread_selected_index = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i in selected_index:
k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200)
idx = np.asarray(idx)
dist = np.asarray(dist)
indices = idx[dist < mean_distance * distance_scale]
for indice in indices:
spread_selected_index[indice] += 1
return spread_selected_index
def ApplySmoothEffort(object, selected_vertice_indices = None, factor = 0.5, iterations = 30):
if selected_vertice_indices is not None:
# Assuming 'mesh' is already defined, e.g., mesh = bpy.context.object.data
selected_vertices_index = set() # Initialize an empty set to store unique vertex indices
# Iterate over each index in the list of polygon indices
for idx in selected_vertice_indices:
polygon = object.data.polygons[idx]
for vert_idx in polygon.vertices:
selected_vertices_index.add(vert_idx)
# Switch to Edit mode
bpy.ops.object.mode_set(mode='EDIT')
# Ensure we're dealing with the latest data
bpy.ops.object.mode_set(mode='OBJECT')
for i, vertex in enumerate(object.data.vertices):
vertex.select = False
if i in selected_vertices_index:
vertex.select = True
# Update the object to reflect changes
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
# Add a Smooth modifier and set it to use the vertex group
smooth_mod = object.modifiers.new(name="SmoothMod", type='SMOOTH')
if selected_vertice_indices is not None:
# Create a new vertex group
group = object.vertex_groups.new(name='Smooth Group')
# Add selected vertices to the vertex group
group.add([v.index for v in object.data.vertices if v.select], weight=1.0, type='ADD')
smooth_mod.vertex_group = 'Smooth Group'
smooth_mod.factor = factor
smooth_mod.iterations = iterations
# bpy.ops.object.modifier_apply(modifier="SmoothMod")
# if selected_vertice_indices is not None:
# object.vertex_groups.clear()
def process(input_mesh_path, output_mesh_path):
# Select and delete all objects in the current scene to start fresh
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(confirm=False)
# Path to the OBJ file to be imported
bpy.ops.wm.obj_import(filepath=input_mesh_path)
# Set the current object to the newly imported mesh
current_obj = bpy.context.object
# Retrieve the mesh data from the current object
mesh_data = current_obj.data
bpy.context.view_layer.update()
# Convert Blender mesh vertices to a NumPy array
vertices_array = np.array([vertex.co[:] for vertex in mesh_data.vertices])
# Convert Blender mesh polygons to a NumPy array of triangles
triangles_array = np.array([polygon.vertices[:] for polygon in mesh_data.polygons])
# Create an Open3D mesh object from the arrays
open3d_mesh = o3d.geometry.TriangleMesh()
open3d_mesh.vertices = o3d.utility.Vector3dVector(vertices_array)
open3d_mesh.triangles = o3d.utility.Vector3iVector(triangles_array)
open3d_mesh.compute_triangle_normals()
# Retrieve and store the normals as a NumPy array
triangle_normals_array = np.asarray(open3d_mesh.triangle_normals)
# Compute centroids for each face of the mesh
triangle_centroids_array = np.array([
np.mean(vertices_array[triangle], axis=0)
for triangle in open3d_mesh.triangles
])
# Create a point cloud of face centroids
pcd_triangle_centroid = o3d.geometry.PointCloud()
pcd_triangle_centroid.points = o3d.utility.Vector3dVector(triangle_centroids_array)
point_curvatures = estimate_point_curvatures(pcd_triangle_centroid, 30)
np_point_curvatures = np.array(list(point_curvatures.values()))
point_curvatures_index = np.arange(len(np_point_curvatures))[np_point_curvatures > 0.25]
spread_point_curvatures_index = spread_point(pcd_triangle_centroid, point_curvatures_index, 50)
ApplySmoothEffort(current_obj, spread_point_curvatures_index, 0.5, 30)
adjacent_normal_angle_variance = compute_adjacent_normal_angle_variance(pcd_triangle_centroid, triangle_normals_array, 40)
adjacent_point_vector_degrees_mean = compute_adjacent_point_vector_degrees_mean(pcd_triangle_centroid, triangle_normals_array, 40)
array_adjacent_point_vector_degrees_mean = np.array(list(adjacent_point_vector_degrees_mean.values()))
array_adjacent_normal_angle_variance = np.array(list(adjacent_normal_angle_variance.values()))
use_data = np.where(array_adjacent_point_vector_degrees_mean < 90, array_adjacent_normal_angle_variance, 0)
# Calculate the minimum and maximum values in the angle array, ignoring NaNs.
min_angle = np.nanmin(use_data)
max_angle = np.nanmax(use_data)
# Perform min-max normalization on the angles to scale them between 0 and 1.
# This normalization helps in comparing values on a common scale.
normalized_angles = (use_data - min_angle) / (max_angle - min_angle)
first_select_thread = normalized_angles > 0.2
first_select_thread_number = first_select_thread.astype(np.float64)
thread_normalized_angles_index = np.arange(len(first_select_thread))[first_select_thread]
spread_point_count = spread_point(pcd_triangle_centroid, thread_normalized_angles_index, 3, count=True)
overlapp_trigular_points = np.array(list(spread_point_count.values())) > 3
overlapp_trigular_points_index = np.arange(len(overlapp_trigular_points))[overlapp_trigular_points]
ApplySmoothEffort(current_obj, overlapp_trigular_points_index, 0.5, 30)
ApplySmoothEffort(current_obj, factor=0.5, iterations=30)
bpy.ops.wm.obj_export(filepath=output_mesh_path)
def process_meshes(input_path, output_path):
"""
Processes each mesh file in the input directory and saves the cleaned meshes in the output directory.
Args:
input_path (str): Directory containing the input meshes or a single mesh file.
output_path (str): Directory where the cleaned meshes will be saved.
"""
if os.path.isdir(input_path):
for filename in sorted(os.listdir(input_path)):
full_input_path = os.path.join(input_path, filename)
full_output_path = os.path.join(output_path, filename)
process(full_input_path, full_output_path)
else:
process(input_path, output_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Processes mesh files.")
parser.add_argument("-ip", "--input_path", type=str, required=True, help="Input path for the mesh file or directory.")
parser.add_argument("-op", "--output_path", type=str, required=True, help="Output path for the cleaned mesh file or directory.")
args = parser.parse_args()
process_meshes(args.input_path, args.output_path)