wall_density_generate.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import os
  2. import numpy as np
  3. import json
  4. import cv2
  5. import open3d as o3d
  6. import argparse
  7. def build_floor_transform_matrix(j_info: dict, floor_id: int):
  8. tab = [[0.0] * 3 for _ in range(3)]
  9. res_width = None
  10. res_height = None
  11. for in_json in j_info.get("floors", []):
  12. floor_id_in_json = in_json.get("id")
  13. if floor_id_in_json != floor_id:
  14. continue
  15. res_width = in_json.get("resolution", {}).get("width")
  16. res_height = in_json.get("resolution", {}).get("height")
  17. x_min = in_json.get("bound", {}).get("x_min")
  18. x_max = in_json.get("bound", {}).get("x_max")
  19. y_min = in_json.get("bound", {}).get("y_min")
  20. y_max = in_json.get("bound", {}).get("y_max")
  21. # 这个矩阵用于从归一化 [0,1] 空间转换到世界坐标空间
  22. tab[0][0] = x_max - x_min
  23. tab[0][1] = 0.0
  24. tab[0][2] = x_min
  25. tab[1][0] = 0.0
  26. tab[1][1] = y_min - y_max
  27. tab[1][2] = y_max
  28. tab[2][0] = 0.0
  29. tab[2][1] = 0.0
  30. tab[2][2] = 1.0
  31. break
  32. if res_width is None:
  33. return np.identity(3).tolist(), None, None
  34. tab_array = np.array(tab, dtype=np.float64)
  35. if np.linalg.det(tab_array) == 0:
  36. raise ValueError("矩阵是奇异的,无法求逆。")
  37. tab_inverse_array = np.linalg.inv(tab_array)
  38. tab_inverse = tab_inverse_array.tolist()
  39. return tab_inverse, res_width, res_height
  40. def generate_maps(ply_path, floor_json_path, floor_id, output_mask_path, output_density_path, output_binary_path):
  41. """
  42. Generates multiple maps from a .ply file: a raw projection, a density map,
  43. and a manually binarized map.
  44. Args:
  45. ply_path (str): Path to the input PLY file.
  46. floor_json_path (str): Path to the floor JSON file.
  47. floor_id (int): The ID of the floor to process.
  48. output_mask_path (str): Path to save the raw projection mask.
  49. output_density_path (str): Path to save the density map.
  50. output_binary_path (str): Path to save the manually binarized map.
  51. """
  52. try:
  53. with open(floor_json_path, 'r') as f:
  54. j_info = json.load(f)
  55. except FileNotFoundError:
  56. print(f"JSON file not found at {floor_json_path}")
  57. return
  58. except json.JSONDecodeError:
  59. print(f"Error decoding JSON from {floor_json_path}")
  60. return
  61. matrix, res_w, res_h = build_floor_transform_matrix(j_info, floor_id)
  62. if res_w is None or res_h is None:
  63. print(f"Could not find floor_id {floor_id} in {os.path.basename(floor_json_path)}")
  64. return
  65. try:
  66. pcd = o3d.io.read_point_cloud(ply_path)
  67. if not pcd.has_points():
  68. print(f"Warning: Point cloud is empty in {ply_path}")
  69. return
  70. cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
  71. denoised_pcd = pcd.select_by_index(ind)
  72. points = np.asarray(denoised_pcd.points)[:, :2]
  73. except Exception as e:
  74. print(f"Failed to read or process point cloud file {ply_path}: {e}")
  75. return
  76. mask_image = np.zeros((res_h, res_w), dtype=np.uint8)
  77. density_accumulator = np.zeros((res_h, res_w), dtype=np.float32)
  78. matrix = np.array(matrix, dtype=np.float64)
  79. for world_point in points:
  80. homogeneous_point = np.array([world_point[0], world_point[1], 1.0], dtype=np.float64)
  81. normalized_point = matrix @ homogeneous_point
  82. pixel_x = int(normalized_point[0] * res_w)
  83. pixel_y = int(normalized_point[1] * res_h)
  84. if 0 <= pixel_x < res_w and 0 <= pixel_y < res_h:
  85. mask_image[pixel_y, pixel_x] = 255
  86. density_accumulator[pixel_y, pixel_x] += 1
  87. output_dir = os.path.dirname(output_mask_path)
  88. if output_dir and not os.path.exists(output_dir):
  89. os.makedirs(output_dir)
  90. # Save raw projection mask
  91. # cv2.imwrite(output_mask_path, mask_image)
  92. # print(f"Successfully generated and saved wall mask to {output_mask_path}")
  93. # Save grayscale density map
  94. density_map_grayscale = cv2.normalize(density_accumulator, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
  95. # cv2.imwrite(output_density_path, density_map_grayscale)
  96. # print(f"Successfully generated and saved density map to {output_density_path}")
  97. # Binarize the grayscale density map using a manual threshold.
  98. # This threshold value (0-255) can be tuned to change sensitivity.
  99. manual_threshold = 20 #10
  100. _, binary_map = cv2.threshold(density_map_grayscale, manual_threshold, 255, cv2.THRESH_BINARY)
  101. print(f"Binarizing {os.path.basename(output_binary_path)} with manual threshold: {manual_threshold}")
  102. # Save the manually binarized map.
  103. cv2.imwrite(output_binary_path, binary_map)
  104. print(f"Successfully generated and saved binarized map to {output_binary_path}")
  105. if __name__ == '__main__':
  106. parser = argparse.ArgumentParser(
  107. description="输入单个场景文件夹用于墙壁点云概率密度图的生成",
  108. formatter_class=argparse.RawTextHelpFormatter
  109. )
  110. parser.add_argument(
  111. '-i',
  112. '--input_folder',
  113. type=str,
  114. required=True,
  115. help='指定输入场景的文件夹路径。'
  116. )
  117. args = parser.parse_args()
  118. scene_folder = args.input_folder
  119. scenece = os.path.basename(scene_folder)
  120. floor_id = 0
  121. ply_path = os.path.join(scene_folder, "wall_ply/wall_instances.ply")
  122. output_dir = os.path.join(scene_folder, "output_maps_walldensity")
  123. if not os.path.exists(output_dir):
  124. os.makedirs(output_dir)
  125. floor_json_path = os.path.join(scene_folder, f"{scenece}.json")
  126. output_mask_path = os.path.join(output_dir, f"{scenece}_floor_{floor_id}_mask.png")
  127. output_density_path = os.path.join(output_dir, f"{scenece}_floor_{floor_id}_density.png")
  128. output_binary_path = os.path.join(output_dir, f"{scenece}_floor_{floor_id}_binary.png")
  129. generate_maps(ply_path, floor_json_path, floor_id, output_mask_path, output_density_path, output_binary_path)