Browse Source

update gaussian surface

zhangkang 9 months ago
parent
commit
04c779278e
3 changed files with 270 additions and 45 deletions
  1. 32 6
      pmd.py
  2. 74 35
      src/eval.py
  3. 164 4
      src/utils.py

+ 32 - 6
pmd.py

@@ -156,13 +156,13 @@ def main(config_path, img_folder):
156 156
         # 使用掩码过滤出非边界点
157 157
         non_boundary_points = z_raw[mask]
158 158
         z_raw_aligned = non_boundary_points @ rotation_matrix.T
159
-        z_raw_aligned[:,2] = z_raw_aligned[:,2] - np.mean(z_raw_aligned[:, 2])
159
+        #z_raw_aligned[:,2] = z_raw_aligned[:,2] - np.mean(z_raw_aligned[:, 2])
160 160
 
161 161
         #non_boundary_points = smoothed
162
-        write_point_cloud(os.path.join(img_folder, str(i) + '_cloudpoint.txt'), z_raw_aligned[:, 0], z_raw_aligned[:, 1],  1000*z_raw_aligned[:, 2])
162
+        write_point_cloud(os.path.join(img_folder, str(i) + '_cloudpoint.txt'), np.round(z_raw_aligned[:, 0]), np.round(z_raw_aligned[:, 1]),  1000*z_raw_aligned[:, 2])
163 163
         total_cloud_point = np.vstack([total_cloud_point, np.column_stack((z_raw_aligned[:, 0], z_raw_aligned[:, 1], 1000*z_raw_aligned[:, 2]))])
164 164
     
165
-    if 0:
165
+    if debug:
166 166
         fig = plt.figure()
167 167
         ax = fig.add_subplot(111, projection='3d')
168 168
 
@@ -197,7 +197,7 @@ def main(config_path, img_folder):
197 197
         # ax.set_ylabel('Y (mm)')
198 198
         # ax.set_zlabel('Z (mm)')
199 199
         # ax.set_title('smoothed 3D Point Cloud Visualization')
200
-        plt.show()
200
+       
201 201
 
202 202
     get_point_end = time.time()
203 203
     print(f"   完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
@@ -205,8 +205,31 @@ def main(config_path, img_folder):
205 205
     
206 206
     print("\n5. 后处理")
207 207
     post_process_start = time.time()
208
-    z_filtered = post_process(total_cloud_point, debug=0)
209
-    write_point_cloud(os.path.join(img_folder, 'cloudpoint.txt'), np.round(total_cloud_point[:, 0]-np.mean(total_cloud_point[:, 0])), np.round(total_cloud_point[:, 1]-np.mean(total_cloud_point[:, 1])), z_filtered)
208
+    total_cloud_point[:,0] = np.round(total_cloud_point[:,0])
209
+    total_cloud_point[:,1] = np.round(total_cloud_point[:,1])
210
+    fitted_points = post_process(total_cloud_point, debug=0)
211
+    align_fitted, _= align2ref(fitted_points)
212
+    
213
+    write_point_cloud(os.path.join(img_folder, 'cloudpoint.txt'), np.round(align_fitted[:, 0]-np.mean(align_fitted[:, 0])), np.round(align_fitted[:, 1]-np.mean(align_fitted[:, 1])), align_fitted[:,2]-np.min(align_fitted[:,2]))
214
+    if debug:
215
+        fig = plt.figure()
216
+        ax = fig.add_subplot(111, projection='3d')
217
+
218
+        # 提取 x, y, z 坐标
219
+        x_vals = np.round(fitted_points[:, 0]-np.mean(fitted_points[:, 0]))
220
+        y_vals = np.round(fitted_points[:, 1]-np.mean(fitted_points[:, 1]))
221
+        z_vals = align_fitted[:,2]-np.min(align_fitted[:,2])
222
+
223
+        # 绘制3D点云
224
+        ax.scatter(x_vals, y_vals, z_vals, c=z_vals, cmap='viridis', marker='o')
225
+
226
+        # 设置轴标签和标题
227
+        ax.set_xlabel('X (mm)')
228
+        ax.set_ylabel('Y (mm)')
229
+        ax.set_zlabel('Z (mm)')
230
+        ax.set_title('3D Point Cloud Visualization')
231
+        plt.show()
232
+    
210 233
     post_process_end = time.time()
211 234
     print(f"   完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
212 235
     print(f"   耗时: {post_process_end - post_process_start:.2f} 秒")
@@ -228,7 +251,10 @@ def main(config_path, img_folder):
228 251
 if __name__ == '__main__':
229 252
     config_path = 'config\\cfg_3freq_wafer.json'
230 253
     img_folder = 'D:\\data\\four_cam\\1008_storage\\20241009163255262'
254
+    json_path = os.path.join(img_folder, 'result.json')
231 255
 
232 256
     pmdstart(config_path, img_folder)
233 257
     point_cloud_path = os.path.join(img_folder, 'cloudpoint.txt')
258
+    theta_notch = 0
259
+    #get_eval_result(point_cloud_path, json_path, theta_notch, 0)
234 260
     

+ 74 - 35
src/eval.py

@@ -262,39 +262,58 @@ def get_eval_result(pointcloud_path, json_path, theta_notch, debug):
262 262
     tree = cKDTree(points)
263 263
 
264 264
     # 定义要查询的中心点
265
-    x2 = np.mean(x)
266
-    y2 = np.mean(y)
267
-
268
-    ## 查找水平方向上 (y 接近 y2) 的最近点
269
-    horizontal_idx = np.where(np.abs(y - y2) < 1)[0]  # 使用一个小容差判断 y 是否接近 y2
270
-    if len(horizontal_idx) > 0:
271
-        horizontal_z = z[horizontal_idx]
272
-        horizontal_x = x[horizontal_idx]
273
-        #print(f"水平方向最近的点 (x, z): {list(zip(horizontal_x, horizontal_z))}")
274
-    else:
275
-        print("没有找到接近 y2 的水平方向点")
276
-
277
-    # 查找垂直方向上 (x 接近 x2) 的最近点
278
-    vertical_idx = np.where(np.abs(x - x2) < 1)[0]  # 使用一个小容差判断 x 是否接近 x2
279
-    if len(vertical_idx) > 0:
280
-        vertical_z = z[vertical_idx]
281
-        vertical_y = y[vertical_idx]
282
-        #print(f"垂直方向最近的点 (y, z): {list(zip(vertical_y, vertical_z))}")
283
-    else:
284
-        print("没有找到接近 x2 的垂直方向点")
285
-
286
-        # 检查水平和垂直方向是否找到点
287
-        if len(horizontal_x) > 0:
288
-            right_x = max(horizontal_x)
289
-        else:
290
-            print("找不到水平方向的点")
291
-            right_x = None
292
-
293
-        if len(vertical_y) > 0:
294
-            top_y = max(vertical_y)
295
-        else:
296
-            print("找不到垂直方向的点")
297
-            top_y = None
265
+    x2 = round(np.mean(x))
266
+    y2 = round(np.mean(y))
267
+
268
+    horizontal_indices = np.where(y == y2)[0]  # 找到 y == y_center 的索引
269
+    horizontal_x = x[horizontal_indices]  # 提取 x 值
270
+    horizontal_z = z[horizontal_indices]  # 提取对应的 z 值
271
+
272
+    sorted_horizontal_indices = np.argsort(horizontal_x)  # 对 x 排序,返回索引
273
+    #print('sorted_horizontal_indices = ', sorted_horizontal_indices)
274
+    horizontal_x_sorted = horizontal_x[sorted_horizontal_indices]
275
+    horizontal_z_sorted = horizontal_z[sorted_horizontal_indices]
276
+
277
+    # 提取经过中心点的垂直方向 (x = x_center) 的所有点
278
+    vertical_indices = np.where(x == x2)[0]  # 找到 x == x2 的索引
279
+    vertical_y = y[vertical_indices]  # 提取 y 值
280
+    vertical_z = z[vertical_indices]  # 提取对应的 z 值
281
+
282
+    # 按照 y 从小到大排序
283
+    sorted_vertical_indices = np.argsort(vertical_y)  # 对 y 排序,返回索引
284
+    vertical_y_sorted = vertical_y[sorted_vertical_indices]
285
+    vertical_z_sorted = vertical_z[sorted_vertical_indices]
286
+
287
+    # ## 查找水平方向上 (y 接近 y2) 的最近点
288
+    # horizontal_idx = np.where(np.abs(y - y2) == 0)[0]  # 使用一个小容差判断 y 是否接近 y2
289
+    # if len(horizontal_idx) > 0:
290
+    #     horizontal_z = z[horizontal_idx]
291
+    #     horizontal_x = x[horizontal_idx]
292
+    #     #print(f"水平方向最近的点 (x, z): {list(zip(horizontal_x, horizontal_z))}")
293
+    # else:
294
+    #     print("没有找到接近 y2 的水平方向点")
295
+
296
+    # # 查找垂直方向上 (x 接近 x2) 的最近点
297
+    # vertical_idx = np.where(np.abs(x - x2) == 0)[0]  # 使用一个小容差判断 x 是否接近 x2
298
+    # if len(vertical_idx) > 0:
299
+    #     vertical_z = z[vertical_idx]
300
+    #     vertical_y = y[vertical_idx]
301
+    #     #print(f"垂直方向最近的点 (y, z): {list(zip(vertical_y, vertical_z))}")
302
+    # else:
303
+    #     print("没有找到接近 x2 的垂直方向点")
304
+
305
+    #     # 检查水平和垂直方向是否找到点
306
+    #     if len(horizontal_x) > 0:
307
+    #         right_x = max(horizontal_x)
308
+    #     else:
309
+    #         print("找不到水平方向的点")
310
+    #         right_x = None
311
+
312
+    #     if len(vertical_y) > 0:
313
+    #         top_y = max(vertical_y)
314
+    #     else:
315
+    #         print("找不到垂直方向的点")
316
+    #         top_y = None
298 317
 
299 318
     top_x = x2
300 319
     right_y = y2
@@ -304,16 +323,36 @@ def get_eval_result(pointcloud_path, json_path, theta_notch, debug):
304 323
     #print(top_x, top_y)
305 324
     line_warpage_x, line_bow_x, z_values = get_mean_line_value(x, y, z, right_x, right_y)
306 325
     line_warpage_y, line_bow_y, z_values = get_mean_line_value(x, y, z, top_x, top_y)
326
+    if debug:
327
+        # 创建一个图
328
+        plt.figure()
329
+
330
+        # 绘制第一个列表
331
+        plt.plot(horizontal_z_sorted.tolist(), label='List 1', color='blue', marker='o')
332
+
333
+        # 绘制第二个列表
334
+        plt.plot(vertical_z_sorted.tolist(), label='List 2', color='red', marker='s')
335
+
336
+        # 添加标题和标签
337
+        plt.title('Plot of List 1 and List 2')
338
+        plt.xlabel('Index')
339
+        plt.ylabel('Values')
340
+
341
+        # 添加图例
342
+        plt.legend()
343
+
344
+        # 显示图形
345
+        plt.show()
307 346
 
308 347
     with open(json_path, 'w') as file:
309 348
         result_data = {
310 349
                 'x':{
311
-                    'height': horizontal_z.tolist(),
350
+                    'height': horizontal_z_sorted.tolist(),
312 351
                     'bow':line_bow_x,
313 352
                     'warpage':line_warpage_x
314 353
                 },
315 354
                 'y':{
316
-                    'height':vertical_z.tolist(),
355
+                    'height':vertical_z_sorted.tolist(),
317 356
                     'bow':line_bow_y,
318 357
                     'warpage':line_warpage_y
319 358
                 },

+ 164 - 4
src/utils.py

@@ -17,6 +17,10 @@ import shutil
17 17
 import pandas as pd
18 18
 from sklearn.linear_model import LinearRegression
19 19
 from scipy.interpolate import Rbf
20
+from numpy.linalg import lstsq
21
+from scipy.optimize import curve_fit
22
+
23
+
20 24
 
21 25
 
22 26
 
@@ -772,16 +776,171 @@ def point_filter(data):
772 776
     return smoothed_z
773 777
 
774 778
 
775
-def post_process(points, debug):
779
+
780
+# 定义高斯曲面方程
781
+def gaussian_surface(X, A, x0, y0, sigma):
782
+    x, y = X
783
+    return A * np.exp(-((x - x0)**2 + (y - y0)**2) / (2 * sigma**2))
784
+
785
+
786
+def curve_fit_gaussian(points):
787
+    x = points[:, 0]
788
+    y = points[:, 1]
789
+    z = points[:, 2]
790
+
791
+    # 使用curve_fit来拟合高斯曲面
792
+    # 初始猜测参数 A, x0, y0, sigma
793
+    initial_guess = [1, np.mean(x), np.mean(y), 1]
794
+    
795
+    # 使用 curve_fit 进行非线性最小二乘拟合
796
+    popt, pcov = curve_fit(gaussian_surface, (x, y), z, p0=initial_guess)
797
+    
798
+    # 拟合的参数
799
+    A, x0, y0, sigma = popt
800
+    print(f"拟合参数: A={A}, x0={x0}, y0={y0}, sigma={sigma}")
801
+    
802
+    # 根据拟合的高斯曲面计算新的 z 值
803
+    z_fitted = gaussian_surface((x, y), A, x0, y0, sigma)
804
+    
805
+    # 生成新的点云
806
+    new_points = np.column_stack((x, y, z_fitted))
807
+
808
+    # 可视化原始点云和拟合的高斯曲面
809
+    # fig = plt.figure()
810
+    # ax = fig.add_subplot(111, projection='3d')
811
+
812
+    # # 绘制原始点云
813
+    # #ax.scatter(x, y, z, c='r', label='Original Points')
814
+
815
+    # # 绘制拟合的高斯曲面点云
816
+    # ax.scatter(x, y, z_fitted, c='b', label='Fitted Gaussian Surface')
817
+
818
+    # ax.set_title('3D Point Cloud and Fitted Gaussian Surface')
819
+    # ax.set_xlabel('X')
820
+    # ax.set_ylabel('Y')
821
+    # ax.set_zlabel('Z')
822
+    # ax.legend()
823
+
824
+    # plt.show()
825
+    
826
+    return new_points
827
+
828
+
829
+def curve_fit_2(points):
776 830
     x = points[:,0]
777 831
     y = points[:,1]
778 832
     z = points[:,2]
833
+    A = np.column_stack((x**2, y**2, x*y, x, y, np.ones_like(x)))
834
+
835
+    # 使用最小二乘法拟合二次曲面的系数
836
+    coefficients, _, _, _ = lstsq(A, z, rcond=None)
837
+
838
+    # 拟合的系数
839
+    a, b, c, d, e, f = coefficients
840
+    print("拟合的系数:", coefficients)
841
+
842
+    # 根据拟合的曲面方程计算新的 z 值
843
+    z_fitted = a * x**2 + b * y**2 + c * x * y + d * x + e * y + f
844
+
845
+    # 生成新的点云
846
+    new_points = np.column_stack((x, y, z_fitted))
847
+
848
+
849
+    # # 可视化原始点云和拟合的曲面
850
+    fig = plt.figure()
851
+    ax = fig.add_subplot(111, projection='3d')
852
+
853
+    # 绘制原始点云
854
+    #ax.scatter(x, y, z, c='r', label='Original Points')
855
+
856
+    # 绘制拟合的曲面点云
857
+    ax.scatter(x, y, z_fitted, c='b', label='Fitted Surface')
858
+
859
+    ax.set_title('3D Point Cloud and Fitted Quadratic Surface')
860
+    ax.set_xlabel('X')
861
+    ax.set_ylabel('Y')
862
+    ax.set_zlabel('Z')
863
+    ax.legend()
864
+
865
+    plt.show()
866
+    return new_points
867
+
868
+def curve_fit_3(points):
869
+    x = points[:,0]
870
+    y = points[:,1]
871
+    z = points[:,2]
872
+    # 构建三次曲面的设计矩阵
873
+    # 曲面模型:z = ax^3 + by^3 + cx^2y + dxy^2 + ex^2 + fy^2 + gxy + hx + iy + j
874
+    A = np.column_stack((x**3, y**3, x**2*y, x*y**2, x**2, y**2, x*y, x, y, np.ones_like(x)))
875
+
876
+    # 使用最小二乘法拟合三次曲面的系数
877
+    coefficients, _, _, _ = lstsq(A, z, rcond=None)
878
+
879
+    # 拟合的系数
880
+    a, b, c, d, e, f, g, h, i, j = coefficients
881
+    #print("拟合的系数:", coefficients)
882
+
883
+    # 根据拟合的曲面方程计算新的 z 值
884
+    z_fitted = a * x**3 + b * y**3 + c * x**2 * y + d * x * y**2 + e * x**2 + f * y**2 + g * x * y + h * x + i * y + j
885
+    # 生成新的点云
886
+    new_points = np.column_stack((x, y, z_fitted))
887
+
888
+
889
+    # # # 可视化原始点云和拟合的曲面
890
+    # fig = plt.figure()
891
+    # ax = fig.add_subplot(111, projection='3d')
892
+
893
+    # # 绘制原始点云
894
+    # #ax.scatter(x, y, z, c='r', label='Original Points')
895
+
896
+    # # 绘制拟合的曲面点云
897
+    # ax.scatter(x, y, z_fitted, c='b', label='Fitted Surface')
898
+
899
+    # ax.set_title('3D Point Cloud and Fitted Quadratic Surface')
900
+    # ax.set_xlabel('X')
901
+    # ax.set_ylabel('Y')
902
+    # ax.set_zlabel('Z')
903
+    # ax.legend()
904
+
905
+    # plt.show()
906
+    return new_points
907
+
908
+def remove_duplicates(points):
909
+    # 使用字典来去重,键为 (x, y),值为 z
910
+    unique_points = {}
911
+    
912
+    for point in points:
913
+        x, y, z = point
914
+        
915
+        if (x, y) not in unique_points:
916
+            unique_points[(x, y)] = z  # 如果 x, y 坐标未出现过,保留该点
917
+    
918
+    # 将去重后的点云转换为 numpy 数组
919
+    new_points = np.array([[x, y, z] for (x, y), z in unique_points.items()])
920
+    
921
+    return new_points
922
+
923
+def post_process(points, debug):
924
+    uniq_points = remove_duplicates(points)
925
+    
926
+    x = uniq_points[:,0]
927
+    y = uniq_points[:,1]
928
+    z = uniq_points[:,2]
779 929
 
780 930
     close_point = remove_duplicate_points(points)
931
+
932
+    smoothed_point = moving_average_filter_z(close_point, 20)
933
+
934
+    fitted_points = curve_fit_gaussian(smoothed_point)
935
+   
936
+    return fitted_points
937
+
938
+    
781 939
     plane_points = fit_plane_and_adjust(close_point, 10)
782
-    smoothed_z = moving_average_filter_z(plane_points[:,2], 20)
940
+    
783 941
     z_outliers_removed = remove_outliers(smoothed_z, threshold=30.0)
784 942
     z_final = smooth_z_with_std(z_outliers_removed, 5)
943
+
785 944
     
786 945
     if debug:
787 946
         # 绘制3D点云
@@ -1334,7 +1493,7 @@ def cubic_interpolation(points, z_values, grid_size=(100, 100)):
1334 1493
     return grid_x, grid_y, grid_z
1335 1494
 
1336 1495
 
1337
-def moving_average_filter_z(z_values, window_size=3):
1496
+def moving_average_filter_z(points, window_size=3):
1338 1497
     """
1339 1498
     使用移动平均法对 z 值进行平滑,减少异常值。
1340 1499
     
@@ -1345,6 +1504,7 @@ def moving_average_filter_z(z_values, window_size=3):
1345 1504
     返回:
1346 1505
     smoothed_z: 平滑后的 z 值
1347 1506
     """
1507
+    z_values = points[:,2]
1348 1508
     smoothed_z = np.copy(z_values)
1349 1509
     for i in range(len(z_values)):
1350 1510
         # 找到窗口内的邻居
@@ -1352,7 +1512,7 @@ def moving_average_filter_z(z_values, window_size=3):
1352 1512
         end_idx = min(len(z_values), i + window_size // 2 + 1)
1353 1513
         smoothed_z[i] = np.mean(z_values[start_idx:end_idx])
1354 1514
     
1355
-    return smoothed_z
1515
+    return np.column_stack((points[:,0], points[:,1], smoothed_z))
1356 1516
 
1357 1517
 
1358 1518