eval.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. import math
  2. import json
  3. import cv2
  4. import os
  5. from tqdm import tqdm
  6. import numpy as np
  7. import scipy.io as sio
  8. import matplotlib.pyplot as plt
  9. from scipy.spatial import cKDTree
  10. def point_to_plane_distance(x, y, z, a, b, c):
  11. z_plane = a * x + b * y + c
  12. distances = np.abs(z - z_plane)
  13. return distances
  14. def fit_plane(x, y, z):
  15. A = np.c_[x, y, np.ones(x.shape)]
  16. C, _, _, _ = np.linalg.lstsq(A, z, rcond=None)
  17. return C
  18. def remove_noise(x, y, z, a, b, c, thresh=0.01):
  19. distances = point_to_plane_distance(x, y, z, a, b, c)
  20. mask = distances < thresh
  21. mask1 = distances >= thresh
  22. x_keep = x[mask]
  23. y_keep = y[mask]
  24. z_keep = z[mask]
  25. z_mean = np.mean(z_keep)
  26. arr_keep = np.column_stack((x_keep,y_keep,z_keep))
  27. x_fliterd = x[mask1]
  28. y_fliterd = y[mask1]
  29. z_fliterd = z[mask1]
  30. arr_filterd = np.column_stack((x_fliterd,y_fliterd,z_fliterd))
  31. # fig = plt.figure(figsize=(10, 7))
  32. # ax = fig.add_subplot(111, projection='3d')
  33. # scatter = ax.scatter(x_keep, y_keep, z_keep, c=z_keep, cmap='viridis', marker='o')
  34. # cbar = plt.colorbar(scatter, ax=ax, shrink=0.5, aspect=5)
  35. # cbar.set_label('Z values')
  36. # ax.set_xlabel('X Axis')
  37. # ax.set_ylabel('Y Axis')
  38. # ax.set_zlabel('Z Axis')
  39. # ax.set_title('Filtered 3D Point Cloud (Outliers Removed)')
  40. # plt.show()
  41. return np.vstack([arr_keep, arr_filterd])
  42. # def point_to_line_distance(point, A, B, C):
  43. # distance = abs(A * point[0] + B * point[1] + C) / np.sqrt(A**2 + B**2)
  44. # return distance
  45. def get_support_points(x_filtered, y_filtered, z_filtered):
  46. support_points = []
  47. center_x = np.mean(x_filtered)
  48. center_y = np.mean(y_filtered)
  49. num_regions = 3
  50. angle_step = 2 * np.pi / num_regions
  51. angles = np.arctan2(y_filtered - center_y, x_filtered - center_x)
  52. distances = np.sqrt((x_filtered - center_x)**2 + (y_filtered - center_y)**2)
  53. for i in range(num_regions):
  54. angle_min = -np.pi + i * angle_step
  55. angle_max = -np.pi + (i + 1) * angle_step
  56. region_mask = (angles >= angle_min) & (angles < angle_min + (2 * np.pi / 360 * 5))
  57. if np.any(region_mask):
  58. region_distances = distances[region_mask]
  59. region_idx = np.argmax(region_distances) #farthest points
  60. filtered_region_x = x_filtered[region_mask]
  61. filtered_region_y = y_filtered[region_mask]
  62. filtered_region_z = z_filtered[region_mask]
  63. point = [filtered_region_x[region_idx], filtered_region_y[region_idx], filtered_region_z[region_idx]]
  64. support_points.append(point)
  65. support_points = np.array(support_points)
  66. return support_points
  67. def get_warpage(x, y, z):
  68. warpage = 0.0
  69. a, b, c = fit_plane(x, y, z)
  70. distances = (a * x + b * y - z + c) / np.sqrt(a**2 + b**2 + 1)
  71. max_distance = np.max(distances)
  72. min_distance = np.min(distances)
  73. warpage = max_distance - min_distance
  74. return warpage
  75. # 定义函数:计算三维空间中点到直线的距离
  76. def point_to_line_distance(point, line_point1, line_point2):
  77. line_vec = line_point2 - line_point1
  78. point_vec = point - line_point1
  79. distance = np.linalg.norm(np.cross(line_vec, point_vec)) / np.linalg.norm(line_vec)
  80. return distance
  81. def get_mean_line_value(x, y, z, far_point_x, far_point_y, dist_thresh=0.5):
  82. a, b, c = fit_plane(x, y, z)
  83. x1,y1 = far_point_x, far_point_y
  84. x2,y2 = np.mean(x), np.mean(y)
  85. A = -(y2 - y1) if x2 != x1 else 1
  86. B = x2 - x1 if x2 != x1 else 0
  87. C = -(x2 * y1 - x1 * y2) if x2 != x1 else - y1
  88. dist_thresh = 2
  89. flat_data = np.column_stack((x, y))
  90. distances = np.abs(A * x + B * y + C) / np.sqrt(A**2 + B**2)
  91. intersections = flat_data[distances < dist_thresh]
  92. #print('len(intersections)= ', len(intersections))
  93. tree = cKDTree(flat_data)
  94. _, idx = tree.query(intersections, k=1)
  95. z_values = z[idx]
  96. if len(z_values) == 0:
  97. return 0, 0, 0
  98. else:
  99. z_plane_values = (a * intersections[:, 0] + b * intersections[:, 1] + c)
  100. warpage_diff = (z_values - z_plane_values)
  101. line_warpage = max(warpage_diff) - min(warpage_diff)
  102. distances_to_far_point = np.sqrt((intersections[:, 0] - far_point_x)**2 + (intersections[:, 1] - far_point_y)**2)
  103. # 找到距离最远的点索引
  104. max_distance_idx = np.argmax(distances_to_far_point)
  105. # 获取最远点的 x, y 坐标
  106. far_point_2 = intersections[max_distance_idx]
  107. # 构建 KD-Tree,便于快速查找最近邻
  108. tree = cKDTree(np.column_stack((x, y)))
  109. # 查找 far_point_x, far_point_y 最近的原始点
  110. _, idx1 = tree.query([far_point_x, far_point_y], k=1)
  111. far_point_1_z = z[idx1] # 找到对应的 z 值
  112. # 查找 far_point_2 最近的原始点
  113. _, idx2 = tree.query(far_point_2, k=1)
  114. far_point_2_z = z[idx2] # 找到对应的 z 值
  115. # 第一个端点的三维坐标
  116. far_point_1_3d = np.array([far_point_x, far_point_y, far_point_1_z])
  117. # 第二个端点的三维坐标
  118. far_point_2_3d = np.array([far_point_2[0], far_point_2[1], far_point_2_z])
  119. #print(far_point_1_3d, far_point_2_3d)
  120. # 查找中心点 (x2, y2) 最近的原始点,确定其 z 值
  121. _, idx_center = tree.query([round(x2), round(y2)], k=1)
  122. center_point_z = z[idx_center] # 根据 (x2, y2) 最近点确定 z 值
  123. center_point_3d = np.array([round(x2), round(y2), center_point_z])
  124. line_bow = point_to_line_distance(center_point_3d, far_point_1_3d, far_point_2_3d)
  125. return line_warpage, line_bow, z_values
  126. def get_theta(x, y, z, num_regions=60):
  127. center_x = np.mean(x)
  128. center_y = np.mean(y)
  129. angle_step = 2 * np.pi / num_regions
  130. angles = np.arctan2(y - center_y, x - center_x)
  131. distances = np.sqrt((x - center_x)**2 + (y - center_y)**2)
  132. max_line_warpage = 0.0
  133. max_line_bow = 0.0
  134. max_warpage_theta = 0.0
  135. max_bow_theta = 0.0
  136. max_warpage_z = []
  137. max_bow_z = []
  138. for i in range(num_regions):
  139. angle_min = -np.pi + i * angle_step
  140. angle_max = -np.pi + (i + 1) * angle_step
  141. region_mask = (angles >= angle_min) & (angles < angle_max)
  142. if np.any(region_mask):
  143. region_distances = distances[region_mask]
  144. region_idx = np.argmax(region_distances)
  145. far_point_x = x[region_mask][region_idx]
  146. far_point_y = y[region_mask][region_idx]
  147. mean_line_warpage, mean_line_bow, z_values = get_mean_line_value(x, y, z, far_point_x, far_point_y)
  148. if max_line_warpage < mean_line_warpage:
  149. max_line_warpage = mean_line_warpage
  150. max_warpage_theta = angle_min
  151. max_warpage_z = z_values
  152. if max_line_bow < mean_line_bow:
  153. max_line_bow = mean_line_bow
  154. max_bow_theta = angle_min
  155. max_bow_z = z_values
  156. #print('mean_line_warpage = ', mean_line_warpage)
  157. if max_warpage_theta < 0:
  158. max_warpage_theta = max_warpage_theta + np.pi
  159. if max_bow_theta < 0:
  160. max_bow_theta = max_bow_theta + np.pi
  161. return max_line_warpage, max_line_bow, max_warpage_theta, max_bow_theta, max_warpage_z, max_bow_z
  162. def get_eval_result(pointcloud_path, json_path, theta_notch, debug):
  163. data = np.loadtxt(os.path.join(pointcloud_path), delimiter=',')
  164. x = data[:, 0]
  165. y = data[:, 1]
  166. z = data[:, 2]
  167. max_z = max(z)
  168. min_z = min(z)
  169. pv = max_z - min_z
  170. print('pv = ', pv)
  171. #a, b, c = fit_plane(x, y, z)
  172. #x_filtered, y_filtered, z_filtered = remove_noise(x, y, z, a, b, c, thresh=0.01)
  173. x_filtered, y_filtered, z_filtered = x, y, z
  174. support_points = get_support_points(x_filtered, y_filtered, z_filtered)
  175. a_bow, b_bow, c_bow = fit_plane(support_points[:,0], support_points[:,1], support_points[:,2])
  176. #center_int = (round(np.mean(x_filtered)), round(np.mean(y_filtered)))
  177. center_int = (np.mean(x_filtered), np.mean(y_filtered))
  178. #print('center_int = ', center_int)
  179. distances = np.sqrt((data[:, 0] - center_int[0]) ** 2 + (data[:, 1] - center_int[1]) ** 2)
  180. # 找到最近的点
  181. nearest_index = np.argmin(distances) # 找到最小距离对应的索引
  182. # 获取最近点的 z 值
  183. z_center = data[nearest_index, 2]
  184. #mask = (data[:, 0] == center_int[0]) & (data[:, 1] == center_int[1])
  185. #z_center = data[mask, 2][0]
  186. fit_z_center = a_bow*center_int[0] + b_bow*center_int[1] + c_bow
  187. bow = (z_center - fit_z_center)
  188. print('global bow = ', bow)
  189. warpage = get_warpage(x_filtered, y_filtered, z_filtered)
  190. print('global warpage = ', warpage)
  191. max_line_warpage, max_line_bow, max_warpage_theta, max_bow_theta, max_warpage_z, max_bow_z = get_theta(x_filtered, y_filtered, z_filtered, 360)
  192. print('max_warpage_theta = ', max_warpage_theta)
  193. print('max_line_warpage = ', max_line_warpage)
  194. print('max_line_bow = ', max_line_bow)
  195. #print('max_warpage_z = ', max_warpage_z)
  196. # x2 = int(round(np.mean(x)))
  197. # y2 = int(round(np.mean(y)))
  198. # horizontal_mask = (y == y2)
  199. # horizontal_z = z[horizontal_mask] # 水平方向对应的 z 值
  200. # horizontal_x = x[horizontal_mask]
  201. # # 提取垂直方向的 z 值(即 x == x2 的所有点)
  202. # vertical_mask = (x == x2)
  203. # vertical_z = z[vertical_mask] # 垂直方向对应的 z 值
  204. # vertical_y = y[vertical_mask]
  205. # 创建二维坐标数组
  206. points = np.column_stack((x, y)) # 将 x 和 y 组合成二维坐标点
  207. # 构建 KD 树
  208. tree = cKDTree(points)
  209. # 定义要查询的中心点
  210. x2 = round(np.mean(x))
  211. y2 = round(np.mean(y))
  212. horizontal_indices = np.where(y == y2)[0] # 找到 y == y_center 的索引
  213. horizontal_x = x[horizontal_indices] # 提取 x 值
  214. horizontal_z = z[horizontal_indices] # 提取对应的 z 值
  215. sorted_horizontal_indices = np.argsort(horizontal_x) # 对 x 排序,返回索引
  216. #print('sorted_horizontal_indices = ', sorted_horizontal_indices)
  217. horizontal_x_sorted = horizontal_x[sorted_horizontal_indices]
  218. horizontal_z_sorted = horizontal_z[sorted_horizontal_indices]
  219. # 提取经过中心点的垂直方向 (x = x_center) 的所有点
  220. vertical_indices = np.where(x == x2)[0] # 找到 x == x2 的索引
  221. vertical_y = y[vertical_indices] # 提取 y 值
  222. vertical_z = z[vertical_indices] # 提取对应的 z 值
  223. # 按照 y 从小到大排序
  224. sorted_vertical_indices = np.argsort(vertical_y) # 对 y 排序,返回索引
  225. vertical_y_sorted = vertical_y[sorted_vertical_indices]
  226. vertical_z_sorted = vertical_z[sorted_vertical_indices]
  227. # ## 查找水平方向上 (y 接近 y2) 的最近点
  228. # horizontal_idx = np.where(np.abs(y - y2) == 0)[0] # 使用一个小容差判断 y 是否接近 y2
  229. # if len(horizontal_idx) > 0:
  230. # horizontal_z = z[horizontal_idx]
  231. # horizontal_x = x[horizontal_idx]
  232. # #print(f"水平方向最近的点 (x, z): {list(zip(horizontal_x, horizontal_z))}")
  233. # else:
  234. # print("没有找到接近 y2 的水平方向点")
  235. # # 查找垂直方向上 (x 接近 x2) 的最近点
  236. # vertical_idx = np.where(np.abs(x - x2) == 0)[0] # 使用一个小容差判断 x 是否接近 x2
  237. # if len(vertical_idx) > 0:
  238. # vertical_z = z[vertical_idx]
  239. # vertical_y = y[vertical_idx]
  240. # #print(f"垂直方向最近的点 (y, z): {list(zip(vertical_y, vertical_z))}")
  241. # else:
  242. # print("没有找到接近 x2 的垂直方向点")
  243. # # 检查水平和垂直方向是否找到点
  244. # if len(horizontal_x) > 0:
  245. # right_x = max(horizontal_x)
  246. # else:
  247. # print("找不到水平方向的点")
  248. # right_x = None
  249. # if len(vertical_y) > 0:
  250. # top_y = max(vertical_y)
  251. # else:
  252. # print("找不到垂直方向的点")
  253. # top_y = None
  254. top_x = x2
  255. right_y = y2
  256. top_x, top_y = x2, max(vertical_y)
  257. right_x, right_y = max(horizontal_x), y2
  258. #print(top_x, top_y)
  259. line_warpage_x, line_bow_x, z_values = get_mean_line_value(x, y, z, right_x, right_y)
  260. line_warpage_y, line_bow_y, z_values = get_mean_line_value(x, y, z, top_x, top_y)
  261. if debug:
  262. # 创建一个图
  263. plt.figure()
  264. # 绘制第一个列表
  265. plt.plot(horizontal_z_sorted.tolist(), label='List 1', color='blue', marker='o')
  266. # 绘制第二个列表
  267. plt.plot(vertical_z_sorted.tolist(), label='List 2', color='red', marker='s')
  268. # 添加标题和标签
  269. plt.title('Plot of List 1 and List 2')
  270. plt.xlabel('Index')
  271. plt.ylabel('Values')
  272. # 添加图例
  273. plt.legend()
  274. # 显示图形
  275. plt.show()
  276. with open(json_path, 'w') as file:
  277. result_data = {
  278. 'x':{
  279. 'height': horizontal_z_sorted.tolist(),
  280. 'bow':line_bow_x,
  281. 'warpage':line_warpage_x
  282. },
  283. 'y':{
  284. 'height':vertical_z_sorted.tolist(),
  285. 'bow':line_bow_y,
  286. 'warpage':line_warpage_y
  287. },
  288. 'max_bow':{
  289. 'angle':max_bow_theta,
  290. 'height':max_bow_z.tolist(),
  291. 'max_bow':max_line_bow
  292. },
  293. 'max_warpage':{
  294. 'angle':max_warpage_theta,
  295. 'height':max_warpage_z.tolist(),
  296. 'max_warpage':max_line_warpage
  297. },
  298. 'pv': pv,
  299. 'notch_theta':theta_notch
  300. }
  301. #print(result_data)
  302. json.dump(result_data, file, indent=4)
  303. if debug:
  304. plt.scatter(x, y, c=z, cmap='viridis')
  305. plt.colorbar()
  306. plt.title('3D Scatter Plot of Reconstructed Surface')
  307. plt.xlabel('X Axis')
  308. plt.ylabel('Y Axis')
  309. fig = plt.figure(figsize=(10, 7))
  310. ax = fig.add_subplot(111, projection='3d')
  311. ax.scatter(x_filtered, y_filtered, z_filtered, color='green', label='Filtered Data', alpha=0.1)
  312. ax.scatter(support_points[:, 0], support_points[:, 1], support_points[:, 2],
  313. color='red', marker='*', s=200, label='Support Points')
  314. ax.legend()
  315. ax.set_xlabel('X Axis')
  316. ax.set_ylabel('Y Axis')
  317. ax.set_zlabel('Z Axis')
  318. ax.set_title('3D Point Cloud with Uniformly Distributed Support Points')
  319. plt.show()