|
@@ -19,7 +19,12 @@ from sklearn.linear_model import LinearRegression
|
19
|
19
|
from scipy.interpolate import Rbf
|
20
|
20
|
from numpy.linalg import lstsq
|
21
|
21
|
from scipy.optimize import curve_fit
|
22
|
|
-
|
|
22
|
+from aprilgrid import Detector
|
|
23
|
+from scipy.sparse.linalg import spsolve
|
|
24
|
+from scipy.spatial import Delaunay
|
|
25
|
+from scipy.sparse.linalg import lsqr
|
|
26
|
+from collections import defaultdict
|
|
27
|
+from scipy.sparse import lil_matrix
|
23
|
28
|
|
24
|
29
|
|
25
|
30
|
|
|
@@ -179,6 +184,7 @@ def fit_sector(contour_points, grid_spacing, erosion_pixels):
|
179
|
184
|
kernel = np.ones((erosion_pixels, erosion_pixels), np.uint8)
|
180
|
185
|
eroded_img = cv2.erode(img, kernel, iterations=1)
|
181
|
186
|
|
|
187
|
+
|
182
|
188
|
# 提取腐蚀后的轮廓
|
183
|
189
|
contours, _ = cv2.findContours(eroded_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
184
|
190
|
if len(contours) == 0:
|
|
@@ -576,9 +582,7 @@ def get_white_mask(white_path, bin_thresh=15, debug=False):
|
576
|
582
|
max_index, max_contour = max(enumerate(valid_contours), key=lambda x: cv2.boundingRect(x[1])[2] * cv2.boundingRect(x[1])[3])
|
577
|
583
|
cv2.drawContours(mask, [max_contour], -1, (255, 255, 255), thickness=cv2.FILLED)
|
578
|
584
|
if debug:
|
579
|
|
- cv2.namedWindow('erode_thresh_img',0)
|
580
|
|
- cv2.imshow('erode_thresh_img', erode_thresh_img)
|
581
|
|
- cv2.waitKey(0)
|
|
585
|
+
|
582
|
586
|
|
583
|
587
|
cv2.namedWindow('mask',0)
|
584
|
588
|
cv2.imshow('mask', mask)
|
|
@@ -788,23 +792,58 @@ def curve_fit_gaussian(points):
|
788
|
792
|
y = points[:, 1]
|
789
|
793
|
z = points[:, 2]
|
790
|
794
|
|
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
|
|
- # 拟合的参数
|
|
795
|
+ # 初始参数猜测:A=max(z), x0=mean(x), y0=mean(y), sigma=较大初始值
|
|
796
|
+ initial_guess = [np.max(z), np.mean(x), np.mean(y), np.std(x) / 2]
|
|
797
|
+
|
|
798
|
+ # 进行非线性最小二乘拟合
|
|
799
|
+ popt, _ = curve_fit(gaussian_surface, (x, y), z, p0=initial_guess)
|
|
800
|
+
|
|
801
|
+ # 提取拟合参数
|
799
|
802
|
A, x0, y0, sigma = popt
|
800
|
|
- print(f"拟合参数: A={A}, x0={x0}, y0={y0}, sigma={sigma}")
|
801
|
803
|
|
802
|
|
- # 根据拟合的高斯曲面计算新的 z 值
|
803
|
|
- z_fitted = gaussian_surface((x, y), A, x0, y0, sigma)
|
|
804
|
+ #print(f"拟合参数: A={A}, x0={x0}, y0={y0}, sigma={sigma}")
|
|
805
|
+ x_mean = np.mean(x)
|
|
806
|
+ y_mean = np.mean(y)
|
|
807
|
+ x0 = x_mean
|
|
808
|
+ y0 = y_mean
|
|
809
|
+ sigma = 2 * sigma
|
|
810
|
+ #print(f"拟合参数1: A={A}, x0={x0}, y0={y0}, sigma={sigma}")
|
804
|
811
|
|
|
812
|
+
|
|
813
|
+ # 生成平滑的 z_fitted
|
|
814
|
+ z_fitted = gaussian_surface((x, y), A, x0, y0, sigma)
|
|
815
|
+
|
|
816
|
+ # 调整 z_fitted,使其最大值与原始 z 的最大值一致
|
|
817
|
+ z_fitted = z_fitted / np.max(z_fitted) * np.max(z)
|
|
818
|
+ #print('np.max(z_fitted) = ', np.max(z_fitted))
|
|
819
|
+
|
805
|
820
|
# 生成新的点云
|
806
|
821
|
new_points = np.column_stack((x, y, z_fitted))
|
807
|
822
|
|
|
823
|
+ return new_points
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+ # # 使用curve_fit来拟合高斯曲面
|
|
829
|
+ # # 初始猜测参数 A, x0, y0, sigma
|
|
830
|
+ # initial_guess = [1, np.mean(x), np.mean(y), 1]
|
|
831
|
+
|
|
832
|
+ # # 使用 curve_fit 进行非线性最小二乘拟合
|
|
833
|
+ # popt, pcov = curve_fit(gaussian_surface, (x, y), z, p0=initial_guess)
|
|
834
|
+
|
|
835
|
+ # # 拟合的参数
|
|
836
|
+ # A, x0, y0, sigma = popt
|
|
837
|
+ # print(f"拟合参数: A={A}, x0={x0}, y0={y0}, sigma={sigma}")
|
|
838
|
+
|
|
839
|
+ # # 根据拟合的高斯曲面计算新的 z 值
|
|
840
|
+ # z_fitted = gaussian_surface((x, y), A, x0, y0, sigma)
|
|
841
|
+
|
|
842
|
+ # # 生成新的点云
|
|
843
|
+ # new_points = np.column_stack((x, y, z_fitted))
|
|
844
|
+
|
|
845
|
+ # return new_points
|
|
846
|
+
|
808
|
847
|
# 可视化原始点云和拟合的高斯曲面
|
809
|
848
|
# fig = plt.figure()
|
810
|
849
|
# ax = fig.add_subplot(111, projection='3d')
|
|
@@ -822,8 +861,9 @@ def curve_fit_gaussian(points):
|
822
|
861
|
# ax.legend()
|
823
|
862
|
|
824
|
863
|
# plt.show()
|
|
864
|
+ #return new_points
|
|
865
|
+
|
825
|
866
|
|
826
|
|
- return new_points
|
827
|
867
|
|
828
|
868
|
|
829
|
869
|
def curve_fit_2(points):
|
|
@@ -921,6 +961,7 @@ def remove_duplicates(points):
|
921
|
961
|
return new_points
|
922
|
962
|
|
923
|
963
|
def post_process(points, debug):
|
|
964
|
+
|
924
|
965
|
uniq_points = remove_duplicates(points)
|
925
|
966
|
|
926
|
967
|
x = uniq_points[:,0]
|
|
@@ -935,111 +976,331 @@ def post_process(points, debug):
|
935
|
976
|
|
936
|
977
|
return fitted_points
|
937
|
978
|
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+def get_neighbors(tri, index):
|
|
982
|
+ """Helper function to get the neighbors of a point in the Delaunay triangulation."""
|
|
983
|
+ neighbors = set()
|
|
984
|
+ for simplex in tri.simplices:
|
|
985
|
+ if index in simplex:
|
|
986
|
+ neighbors.update(simplex)
|
|
987
|
+ neighbors.remove(index) # Remove the point itself from its neighbors
|
|
988
|
+ return list(neighbors)
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+def expand_to_rectangle_with_unit_spacing(x, y, x_gradient, y_gradient):
|
|
992
|
+ # 1. 计算最小外接矩形的边界
|
|
993
|
+ x_min, x_max = np.min(x), np.max(x)
|
|
994
|
+ y_min, y_max = np.min(y), np.max(y)
|
|
995
|
+
|
|
996
|
+ # 2. 根据边界计算矩形的宽度和高度,间隔为1
|
|
997
|
+ width = int(np.ceil(x_max - x_min)) + 1 # 确保包含所有点,并加1保证整数
|
|
998
|
+ height = int(np.ceil(y_max - y_min)) + 1
|
|
999
|
+
|
|
1000
|
+ # 3. 创建规则网格,间隔为1
|
|
1001
|
+ x_grid = np.arange(x_min, x_max + 1, 1) # x坐标从x_min到x_max,间隔为1
|
|
1002
|
+ y_grid = np.arange(y_min, y_max + 1, 1) # y坐标从y_min到y_max,间隔为1
|
|
1003
|
+ x_expand, y_expand = np.meshgrid(x_grid, y_grid)
|
|
1004
|
+
|
|
1005
|
+ # 4. 初始化梯度矩阵,填充为 0
|
|
1006
|
+ x_grad_expand = np.zeros_like(x_expand)
|
|
1007
|
+ y_grad_expand = np.zeros_like(y_expand)
|
|
1008
|
+
|
|
1009
|
+ # 5. 找到每个点在网格中的位置,插入对应的梯度
|
|
1010
|
+ for i in range(len(x)):
|
|
1011
|
+ # 找到对应的网格索引
|
|
1012
|
+ x_idx = int(x[i] - x_min)
|
|
1013
|
+ y_idx = int(y[i] - y_min)
|
|
1014
|
+
|
|
1015
|
+ # 将对应点的梯度填入矩阵中
|
|
1016
|
+ x_grad_expand[y_idx, x_idx] = x_gradient[i]
|
|
1017
|
+ y_grad_expand[y_idx, x_idx] = y_gradient[i]
|
|
1018
|
+
|
|
1019
|
+ return x_grad_expand, y_grad_expand, x_expand, y_expand
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+def remove_duplicates_4d_with_std_neighbor(points, std_multiplier=2):
|
|
1024
|
+ """
|
|
1025
|
+ 去重并处理z1和z2值,如果z1或z2与全局均值差异大于标准差的倍数,则替换为相邻点的值。
|
|
1026
|
+
|
|
1027
|
+ Parameters:
|
|
1028
|
+ points : np.ndarray
|
|
1029
|
+ 四维点集 (x, y, z1, z2)。
|
|
1030
|
+ std_multiplier : float
|
|
1031
|
+ 用于判断异常值的标准差倍数,默认为2倍。
|
|
1032
|
+
|
|
1033
|
+ Returns:
|
|
1034
|
+ new_points : np.ndarray
|
|
1035
|
+ 处理后的去重点集。
|
|
1036
|
+ """
|
938
|
1037
|
|
939
|
|
- plane_points = fit_plane_and_adjust(close_point, 10)
|
|
1038
|
+ # 使用 defaultdict 来保存 (x, y) 键,并存储 z1 和 z2 的列表
|
|
1039
|
+ unique_points = defaultdict(lambda: [[], []]) # 第一个列表存 z1,第二个列表存 z2
|
940
|
1040
|
|
941
|
|
- z_outliers_removed = remove_outliers(smoothed_z, threshold=30.0)
|
942
|
|
- z_final = smooth_z_with_std(z_outliers_removed, 5)
|
|
1041
|
+ # 遍历点,按 (x, y) 键存储 z1 和 z2 的值
|
|
1042
|
+ for point in points:
|
|
1043
|
+ x, y, z1, z2 = point
|
|
1044
|
+ unique_points[(x, y)][0].append(z1) # 将 z1 加入列表
|
|
1045
|
+ unique_points[(x, y)][1].append(z2) # 将 z2 加入列表
|
|
1046
|
+
|
|
1047
|
+ # 计算全局 z1 和 z2 的均值和标准差
|
|
1048
|
+ all_z1 = points[:, 2]
|
|
1049
|
+ all_z2 = points[:, 3]
|
|
1050
|
+ z1_mean_global = np.mean(all_z1)
|
|
1051
|
+ z2_mean_global = np.mean(all_z2)
|
|
1052
|
+ z1_std_global = np.std(all_z1)
|
|
1053
|
+ z2_std_global = np.std(all_z2)
|
|
1054
|
+
|
|
1055
|
+ # 准备存储新点
|
|
1056
|
+ new_points = []
|
|
1057
|
+
|
|
1058
|
+ # 初始化相邻点值为全局均值,保证第一次有默认值
|
|
1059
|
+ previous_z1 = z1_mean_global
|
|
1060
|
+ previous_z2 = z2_mean_global
|
|
1061
|
+
|
|
1062
|
+ # 对每个 (x, y) 进行处理
|
|
1063
|
+ for (x, y), (z1_list, z2_list) in unique_points.items():
|
|
1064
|
+ # 初始化最小距离和对应的 z1, z2
|
|
1065
|
+ min_distance = float('inf')
|
|
1066
|
+ best_z1 = None
|
|
1067
|
+ best_z2 = None
|
|
1068
|
+
|
|
1069
|
+ # 遍历该 (x, y) 的所有 z1 和 z2 值
|
|
1070
|
+ for z1, z2 in zip(z1_list, z2_list):
|
|
1071
|
+ # 检查 z1 和 z2 是否超出标准差的倍数,若超出则替换为相邻点的值
|
|
1072
|
+ if abs(z1 - z1_mean_global) > std_multiplier * z1_std_global:
|
|
1073
|
+ z1 = previous_z1 # 用相邻点的 z1 值替换
|
|
1074
|
+ if abs(z2 - z2_mean_global) > std_multiplier * z2_std_global:
|
|
1075
|
+ z2 = previous_z2 # 用相邻点的 z2 值替换
|
|
1076
|
+
|
|
1077
|
+ # 计算与全局均值的距离
|
|
1078
|
+ distance = np.sqrt((z1 - z1_mean_global)**2 + (z2 - z2_mean_global)**2)
|
|
1079
|
+
|
|
1080
|
+ # 找到与全局均值最接近的点
|
|
1081
|
+ if distance < min_distance:
|
|
1082
|
+ min_distance = distance
|
|
1083
|
+ best_z1 = z1
|
|
1084
|
+ best_z2 = z2
|
|
1085
|
+
|
|
1086
|
+ # 更新相邻点值
|
|
1087
|
+ previous_z1 = best_z1
|
|
1088
|
+ previous_z2 = best_z2
|
|
1089
|
+
|
|
1090
|
+ # 添加该点到新点列表
|
|
1091
|
+ new_points.append([x, y, best_z1, best_z2])
|
|
1092
|
+
|
|
1093
|
+ # 转换为 numpy 数组
|
|
1094
|
+ new_points = np.array(new_points)
|
|
1095
|
+
|
|
1096
|
+ return new_points
|
943
|
1097
|
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+def southwell_integrate_smooth(x_grad_expand, y_grad_expand, x_expand, y_expand, reference_point=(0, 0)):
|
|
1101
|
+ """
|
|
1102
|
+ Southwell 积分方法,用于根据梯度场重建标量场,加入平滑处理以保证结果连续。
|
|
1103
|
+
|
|
1104
|
+ Parameters:
|
|
1105
|
+ x_grad_expand: numpy.ndarray
|
|
1106
|
+ x方向的梯度矩阵。
|
|
1107
|
+ y_grad_expand: numpy.ndarray
|
|
1108
|
+ y方向的梯度矩阵。
|
|
1109
|
+ x_expand: numpy.ndarray
|
|
1110
|
+ 网格的x坐标矩阵。
|
|
1111
|
+ y_expand: numpy.ndarray
|
|
1112
|
+ 网格的y坐标矩阵。
|
|
1113
|
+ reference_point: tuple
|
|
1114
|
+ 起始积分点的坐标索引,默认为(0, 0)。
|
|
1115
|
+
|
|
1116
|
+ Returns:
|
|
1117
|
+ Z_reconstructed: numpy.ndarray
|
|
1118
|
+ 重建的标量场。
|
|
1119
|
+ """
|
|
1120
|
+
|
|
1121
|
+ # 检查是否为二维数组
|
|
1122
|
+ H, W = x_grad_expand.shape
|
944
|
1123
|
|
945
|
|
- if debug:
|
946
|
|
- # 绘制3D点云
|
947
|
|
- fig = plt.figure()
|
948
|
|
- ax = fig.add_subplot(111, projection='3d')
|
949
|
|
- ax.scatter(x, y, z, c=z, cmap='viridis', marker='o', s=2)
|
950
|
|
- ax.set_title('3D Point Cloud with raw Points')
|
|
1124
|
+ # 初始化标量场,设为零
|
|
1125
|
+ Z_reconstructed = np.zeros((H, W))
|
|
1126
|
+
|
|
1127
|
+ # 创建一个布尔矩阵,跟踪已处理的点
|
|
1128
|
+ visited = np.zeros((H, W), dtype=bool)
|
|
1129
|
+
|
|
1130
|
+ # 从参考点开始,标量值为0
|
|
1131
|
+ ref_y, ref_x = reference_point
|
|
1132
|
+ visited[ref_y, ref_x] = True
|
|
1133
|
+
|
|
1134
|
+ # 创建队列,用于遍历
|
|
1135
|
+ queue = [(ref_y, ref_x)]
|
|
1136
|
+
|
|
1137
|
+ # 遍历队列中的点
|
|
1138
|
+ while queue:
|
|
1139
|
+ current_y, current_x = queue.pop(0)
|
|
1140
|
+ current_z = Z_reconstructed[current_y, current_x]
|
|
1141
|
+
|
|
1142
|
+ # 处理四个方向的邻居 (上,下,左,右)
|
|
1143
|
+ neighbors = [
|
|
1144
|
+ (current_y - 1, current_x, y_grad_expand[current_y - 1, current_x] if current_y > 0 else 0), # 上
|
|
1145
|
+ (current_y + 1, current_x, y_grad_expand[current_y, current_x] if current_y < H - 1 else 0), # 下
|
|
1146
|
+ (current_y, current_x - 1, x_grad_expand[current_y, current_x - 1] if current_x > 0 else 0), # 左
|
|
1147
|
+ (current_y, current_x + 1, x_grad_expand[current_y, current_x] if current_x < W - 1 else 0) # 右
|
|
1148
|
+ ]
|
|
1149
|
+
|
|
1150
|
+ # 遍历邻居点
|
|
1151
|
+ for ny, nx, grad in neighbors:
|
|
1152
|
+ if 0 <= ny < H and 0 <= nx < W and not visited[ny, nx]:
|
|
1153
|
+ # 通过邻居梯度方向的平均值来平滑积分
|
|
1154
|
+ if ny != current_y: # y方向积分
|
|
1155
|
+ neighbor_grad = (y_grad_expand[current_y, current_x] + grad) / 2 # 平滑处理
|
|
1156
|
+ Z_reconstructed[ny, nx] = current_z + neighbor_grad * (y_expand[ny, nx] - y_expand[current_y, current_x])
|
|
1157
|
+ else: # x方向积分
|
|
1158
|
+ neighbor_grad = (x_grad_expand[current_y, current_x] + grad) / 2 # 平滑处理
|
|
1159
|
+ Z_reconstructed[ny, nx] = current_z + neighbor_grad * (x_expand[ny, nx] - x_expand[current_y, current_x])
|
|
1160
|
+
|
|
1161
|
+ # 标记为已访问
|
|
1162
|
+ visited[ny, nx] = True
|
|
1163
|
+
|
|
1164
|
+ # 将邻居点加入队列
|
|
1165
|
+ queue.append((ny, nx))
|
|
1166
|
+
|
|
1167
|
+ return Z_reconstructed
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+def compute_divergence(x_grad_expand, y_grad_expand):
|
|
1172
|
+ """
|
|
1173
|
+ 计算梯度场的散度(div_G),用于泊松重建。
|
|
1174
|
+
|
|
1175
|
+ Parameters:
|
|
1176
|
+ x_grad_expand: numpy.ndarray
|
|
1177
|
+ x方向的梯度矩阵。
|
|
1178
|
+ y_grad_expand: numpy.ndarray
|
|
1179
|
+ y方向的梯度矩阵。
|
|
1180
|
+
|
|
1181
|
+ Returns:
|
|
1182
|
+ div_G: numpy.ndarray
|
|
1183
|
+ 梯度场的散度矩阵。
|
|
1184
|
+ """
|
|
1185
|
+ H, W = x_grad_expand.shape
|
|
1186
|
+ div_G = np.zeros((H, W))
|
951
|
1187
|
|
952
|
|
- fig = plt.figure()
|
953
|
|
- ax = fig.add_subplot(111, projection='3d')
|
954
|
|
- ax.scatter(x, y, smoothed_z, c=smoothed_z, cmap='viridis', marker='o', s=2)
|
955
|
|
- ax.set_title('3D Point Cloud with smoothed_z Points')
|
|
1188
|
+ # 计算 x 方向的梯度差分
|
|
1189
|
+ div_G[:, 1:] += x_grad_expand[:, 1:] - x_grad_expand[:, :-1]
|
956
|
1190
|
|
957
|
|
- fig = plt.figure()
|
958
|
|
- ax = fig.add_subplot(111, projection='3d')
|
959
|
|
- ax.scatter(x, y, z_outliers_removed, c=z_outliers_removed, cmap='viridis')
|
960
|
|
- ax.set_title('3D Point Cloud with remove_outliers Points')
|
|
1191
|
+ # 计算 y 方向的梯度差分
|
|
1192
|
+ div_G[1:, :] += y_grad_expand[1:, :] - y_grad_expand[:-1, :]
|
|
1193
|
+
|
|
1194
|
+ return div_G
|
961
|
1195
|
|
962
|
|
- fig = plt.figure()
|
963
|
|
- ax = fig.add_subplot(111, projection='3d')
|
964
|
|
- ax.scatter(x, y, z_final, c=z_final, cmap='viridis')
|
965
|
|
- ax.set_title('3D Point Cloud with z_final Points')
|
966
|
|
- ax.set_zlim(0,10)
|
967
|
|
-
|
|
1196
|
+def poisson_reconstruct(x_grad_expand, y_grad_expand):
|
|
1197
|
+ """
|
|
1198
|
+ 使用泊松重建方法重建标量场。
|
968
|
1199
|
|
969
|
|
- plt.show()
|
|
1200
|
+ Parameters:
|
|
1201
|
+ x_grad_expand: numpy.ndarray
|
|
1202
|
+ x方向的梯度矩阵。
|
|
1203
|
+ y_grad_expand: numpy.ndarray
|
|
1204
|
+ y方向的梯度矩阵。
|
|
1205
|
+
|
|
1206
|
+ Returns:
|
|
1207
|
+ Z_reconstructed: numpy.ndarray
|
|
1208
|
+ 重建的标量场。
|
|
1209
|
+ """
|
|
1210
|
+ H, W = x_grad_expand.shape
|
|
1211
|
+
|
|
1212
|
+ # 计算散度
|
|
1213
|
+ div_G = compute_divergence(x_grad_expand, y_grad_expand)
|
|
1214
|
+
|
|
1215
|
+ # 构建泊松方程的离散化矩阵(稀疏矩阵)
|
|
1216
|
+ A = lil_matrix((H * W, H * W))
|
|
1217
|
+ b = div_G.flatten()
|
|
1218
|
+
|
|
1219
|
+ for y in range(H):
|
|
1220
|
+ for x in range(W):
|
|
1221
|
+ index = x + y * W
|
|
1222
|
+
|
|
1223
|
+ if x == 0 or x == W - 1 or y == 0 or y == H - 1:
|
|
1224
|
+ # 边界点,直接固定为 0(Dirichlet 边界条件)
|
|
1225
|
+ A[index, index] = 1
|
|
1226
|
+ b[index] = 0
|
|
1227
|
+ else:
|
|
1228
|
+ # 内部点,应用泊松方程的离散形式
|
|
1229
|
+ A[index, index] = -4
|
|
1230
|
+ A[index, index - 1] = 1 # 左边的点
|
|
1231
|
+ A[index, index + 1] = 1 # 右边的点
|
|
1232
|
+ A[index, index - W] = 1 # 上边的点
|
|
1233
|
+ A[index, index + W] = 1 # 下边的点
|
970
|
1234
|
|
971
|
|
- return z_final
|
|
1235
|
+ # 使用 scipy 的稀疏求解器来解泊松方程
|
|
1236
|
+ Z_flat = spsolve(A.tocsr(), b)
|
|
1237
|
+ Z_reconstructed = Z_flat.reshape((H, W))
|
972
|
1238
|
|
|
1239
|
+ return Z_reconstructed
|
973
|
1240
|
|
974
|
1241
|
|
975
|
1242
|
|
976
|
|
-def plot_camera(ax, rotation_matrix, translation_vector, camera_id, scale=1.0, color='r'):
|
977
|
|
- """
|
978
|
|
- 绘制单个相机的三维表示:位置、方向和视野
|
979
|
|
- R: 3x3旋转矩阵 (从世界坐标系到相机坐标系)
|
980
|
|
- t: 3x1平移向量 (相机位置)
|
981
|
|
- scale: 控制相机大小的比例因子
|
982
|
|
- color: 相机视野的颜色
|
983
|
|
- """
|
984
|
|
- # 相机的中心
|
985
|
|
- camera_center = -rotation_matrix.T @ translation_vector
|
986
|
|
- print('camera_center = ', camera_center)
|
987
|
|
- camera_center = np.squeeze(camera_center) # 确保 camera_center 是 1D 数组
|
988
|
|
- #print('camera_center = ', camera_center)
|
|
1243
|
+def post_process_with_grad(img_folder, debug=False):
|
|
1244
|
+ total_point_grad = np.empty((0, 4))
|
|
1245
|
+ for i in range(4):
|
|
1246
|
+ txt_file = img_folder + str(i) + '_gradient.txt'
|
|
1247
|
+ total_point_grad = np.vstack([total_point_grad, np.loadtxt(os.path.join(txt_file), delimiter=',')])
|
|
1248
|
+
|
|
1249
|
+ print(total_point_grad.shape)
|
989
|
1250
|
|
990
|
|
- # 定义相机的四个角点在相机坐标系中的位置
|
991
|
|
- camera_points = np.array([
|
992
|
|
- [0, 0, 0], # 相机中心
|
993
|
|
- [1, 1, 2], # 视野的左上角
|
994
|
|
- [1, -1, 2], # 视野的左下角
|
995
|
|
- [-1, -1, 2], # 视野的右下角
|
996
|
|
- [-1, 1, 2] # 视野的右上角
|
997
|
|
- ]) * scale
|
|
1251
|
+ total_point_grad = remove_duplicates_4d_with_std_neighbor(total_point_grad, 3)
|
998
|
1252
|
|
999
|
|
- # 将相机坐标系下的点转换到世界坐标系
|
1000
|
|
- camera_points_world = (rotation_matrix.T @ camera_points.T).T + camera_center.T
|
1001
|
|
- # 绘制相机位置(红色圆点表示相机中心)
|
1002
|
|
- ax.scatter(camera_center[0], camera_center[1], camera_center[2], color=color, marker='o', s=100)
|
|
1253
|
+ print(total_point_grad.shape)
|
1003
|
1254
|
|
1004
|
|
- # 绘制相机的 X、Y、Z 坐标轴
|
1005
|
|
- axis_length = scale * 3 # 坐标轴箭头长度
|
1006
|
|
- x_axis = rotation_matrix[:, 0] # X 轴方向
|
1007
|
|
- y_axis = rotation_matrix[:, 1] # Y 轴方向
|
1008
|
|
- z_axis = rotation_matrix[:, 2] # Z 轴方向
|
|
1255
|
+ x = total_point_grad[:,0]
|
|
1256
|
+ y = total_point_grad[:,1]
|
1009
|
1257
|
|
1010
|
|
- # X 轴箭头(红色)
|
1011
|
|
- ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
1012
|
|
- x_axis[0], x_axis[1], x_axis[2],
|
1013
|
|
- length=axis_length, color='r', arrow_length_ratio=0.5, label='X axis')
|
|
1258
|
+ x_gradient = total_point_grad[:,2]
|
|
1259
|
+ y_gradient = total_point_grad[:,3]
|
1014
|
1260
|
|
1015
|
|
- # Y 轴箭头(绿色)
|
1016
|
|
- ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
1017
|
|
- y_axis[0], y_axis[1], y_axis[2],
|
1018
|
|
- length=axis_length, color='g', arrow_length_ratio=0.5, label='Y axis')
|
|
1261
|
+ mean_x_grad = np.mean(x_gradient)
|
|
1262
|
+ mean_y_grad = np.mean(y_gradient)
|
1019
|
1263
|
|
1020
|
|
- # Z 轴箭头(蓝色)
|
1021
|
|
- ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
1022
|
|
- z_axis[0], z_axis[1], z_axis[2],
|
1023
|
|
- length=axis_length, color='b', arrow_length_ratio=0.5, label='Z axis')
|
|
1264
|
+ x_gradient = (x_gradient - mean_x_grad)
|
|
1265
|
+ y_gradient = (y_gradient - mean_y_grad)
|
|
1266
|
+
|
|
1267
|
+ # 使用最小外接矩形,并填充梯度
|
|
1268
|
+ x_grad_expand, y_grad_expand, x_expand, y_expand = expand_to_rectangle_with_unit_spacing(x, y, x_gradient, y_gradient)
|
|
1269
|
+ #print(x_grad_expand.shape, y_grad_expand.shape, x_expand.shape, y_expand.shape)
|
|
1270
|
+ # 调用 Southwell 重建函数
|
|
1271
|
+ #Z_reconstructed = southwell_integrate_smooth(x_grad_expand, y_grad_expand, x_expand, y_expand, (0, 0))
|
1024
|
1272
|
|
1025
|
|
- # 绘制相机中心
|
1026
|
|
- ax.scatter(camera_center[0], camera_center[1], camera_center[2], color=color, marker='o', s=100, label="Camera")
|
1027
|
|
- ax.text(camera_center[0], camera_center[1], camera_center[2], f'Cam {camera_id}', color='black', fontsize=12)
|
|
1273
|
+ Z_reconstructed = poisson_reconstruct(x_grad_expand, y_grad_expand)
|
|
1274
|
+
|
|
1275
|
+ z1_lookup = {(x1, y1): z1 for x1, y1, z1 in np.column_stack((x_expand.reshape(-1,1), y_expand.reshape(-1,1), Z_reconstructed.reshape(-1,1)))}
|
1028
|
1276
|
|
1029
|
|
- #绘制相机视野四个角点与相机中心的连线
|
1030
|
|
- for i in range(1, 5):
|
1031
|
|
- ax.plot([camera_center[0], camera_points_world[i, 0]],
|
1032
|
|
- [camera_center[1], camera_points_world[i, 1]],
|
1033
|
|
- [camera_center[2], camera_points_world[i, 2]], color=color)
|
|
1277
|
+ z1_values = np.array([z1_lookup.get((x, y), np.nan) for x, y in np.column_stack((x, y))])
|
|
1278
|
+ fitted_points = post_process(np.column_stack((x,y,z1_values)), debug=0)
|
1034
|
1279
|
|
1035
|
|
- # 绘制相机的四边形视野
|
1036
|
|
- ax.plot([camera_points_world[1, 0], camera_points_world[2, 0], camera_points_world[3, 0], camera_points_world[4, 0], camera_points_world[1, 0]],
|
1037
|
|
- [camera_points_world[1, 1], camera_points_world[2, 1], camera_points_world[3, 1], camera_points_world[4, 1], camera_points_world[1, 1]],
|
1038
|
|
- [camera_points_world[1, 2], camera_points_world[2, 2], camera_points_world[3, 2], camera_points_world[4, 2], camera_points_world[1, 2]], color=color)
|
|
1280
|
+ # 可视化梯度场
|
|
1281
|
+ if debug:
|
|
1282
|
+ fig = plt.figure()
|
|
1283
|
+ ax = fig.add_subplot(111, projection='3d')
|
|
1284
|
+
|
|
1285
|
+ #提取 x, y, z 坐标
|
|
1286
|
+ x_vals = fitted_points[:,0]
|
|
1287
|
+ y_vals = fitted_points[:,1]
|
|
1288
|
+ z_vals = fitted_points[:,2]
|
|
1289
|
+ # x_vals = x
|
|
1290
|
+ # y_vals = y
|
|
1291
|
+ # z_vals = z1_values
|
|
1292
|
+ # 绘制3D点云
|
|
1293
|
+ ax.scatter(x_vals, y_vals, z_vals, c=z_vals, cmap='viridis', marker='o')
|
|
1294
|
+
|
|
1295
|
+ # 设置轴标签和标题
|
|
1296
|
+ ax.set_xlabel('X (mm)')
|
|
1297
|
+ ax.set_ylabel('Y (mm)')
|
|
1298
|
+ ax.set_zlabel('Z (mm)')
|
|
1299
|
+ ax.set_title('post_process 3D Point Cloud Visualization')
|
|
1300
|
+ plt.show()
|
|
1301
|
+
|
|
1302
|
+ return fitted_points
|
1039
|
1303
|
|
1040
|
|
- # # 添加相机方向箭头
|
1041
|
|
- # ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
1042
|
|
- # rotation_matrix[0, 2], rotation_matrix[1, 2], rotation_matrix[2, 2], length=scale, color='g', arrow_length_ratio=1, label="Direction")
|
1043
|
1304
|
|
1044
|
1305
|
|
1045
|
1306
|
def transform_image_to_cam0(img, K_cam_i, R_cam_i, T_cam_i, K_cam0, R_cam0, T_cam0, depth_map):
|
|
@@ -1089,60 +1350,6 @@ def transform_image_to_cam0(img, K_cam_i, R_cam_i, T_cam_i, K_cam0, R_cam0, T_ca
|
1089
|
1350
|
return output_img
|
1090
|
1351
|
|
1091
|
1352
|
|
1092
|
|
-
|
1093
|
|
-def camera_calibrate_vis():
|
1094
|
|
- img_folder = 'D:\\data\\four_cam\\0927_storage\\20240927161124014'
|
1095
|
|
- config_path = 'D:\\code\\pmd-python\\config\\cfg_3freq_wafer.json'
|
1096
|
|
- cfg = json.load(open(config_path, 'r'))
|
1097
|
|
- with open(os.path.join('D:\\code\\pmd-python\\config\\', cfg['cam_params']), 'rb') as pkl_file:
|
1098
|
|
- cam_params = pickle.load(pkl_file)
|
1099
|
|
- fig = plt.figure()
|
1100
|
|
- ax = fig.add_subplot(111, projection='3d')
|
1101
|
|
-
|
1102
|
|
- # 绘制每个相机
|
1103
|
|
- for ii in range(4):
|
1104
|
|
- plot_camera(ax, cam_params[ii]['rotation_matrix'], cam_params[ii]['translation_vector'], ii, scale=40, color='r')
|
1105
|
|
-
|
1106
|
|
- # 绘制物体 (假设是一个简单的平面物体,位于 z = 0 平面上)
|
1107
|
|
- plane_x, plane_y = np.meshgrid(np.linspace(-500, 500, 10), np.linspace(-500, 500, 10))
|
1108
|
|
- plane_z = np.zeros_like(plane_x) # z = 0 的平面
|
1109
|
|
- ax.plot_surface(plane_x, plane_y, plane_z, color='blue', alpha=0.3)
|
1110
|
|
-
|
1111
|
|
- # 设置轴的范围和标签
|
1112
|
|
- ax.set_xlabel('X axis')
|
1113
|
|
- ax.set_ylabel('Y axis')
|
1114
|
|
- ax.set_zlabel('Z axis')
|
1115
|
|
- ax.set_xlim([-50, 300])
|
1116
|
|
- ax.set_ylim([-50, 300])
|
1117
|
|
- ax.set_zlim([0, 500])
|
1118
|
|
-
|
1119
|
|
- K_cam0 = cam_params[0]['camera_matrix']
|
1120
|
|
- R_cam0 = cam_params[0]['rotation_matrix']
|
1121
|
|
- T_cam0 = cam_params[0]['translation_vector']
|
1122
|
|
-
|
1123
|
|
- for i in range(4):
|
1124
|
|
- img_path = img_folder + '\\' + str(i) + '_frame_24.bmp'
|
1125
|
|
- img = cv2.imread(img_path, 0)
|
1126
|
|
- print('max gray = ', np.max(img))
|
1127
|
|
- if i > 0:
|
1128
|
|
- img = cv2.imread(img_path, 0)
|
1129
|
|
- K_cam_i = cam_params[i]['camera_matrix']
|
1130
|
|
- R_cam_i = cam_params[i]['rotation_matrix']
|
1131
|
|
- T_cam_i = cam_params[i]['translation_vector']
|
1132
|
|
- # 示例图像和深度图
|
1133
|
|
- depth_map = img
|
1134
|
|
-
|
1135
|
|
- # 将图像转换到 cam0 坐标系下
|
1136
|
|
- #aligned_img = transform_image_to_cam0(img, K_cam_i, R_cam_i, T_cam_i, K_cam0, R_cam0, T_cam0, depth_map)
|
1137
|
|
-
|
1138
|
|
- # cv2.namedWindow('Aligned Image', 0)
|
1139
|
|
- # cv2.imshow('Aligned Image', aligned_img)
|
1140
|
|
- # cv2.waitKey(0)
|
1141
|
|
- # cv2.destroyAllWindows()
|
1142
|
|
-
|
1143
|
|
- plt.show()
|
1144
|
|
-
|
1145
|
|
-
|
1146
|
1353
|
def rename_gamma():
|
1147
|
1354
|
img_path = 'D:\\code\\Gamma\\Gamma\\cam3_original_pictures\\'
|
1148
|
1355
|
i = 0
|
|
@@ -1303,8 +1510,6 @@ def apply_local_average_smoothing(z_reconstructed, window_size=3):
|
1303
|
1510
|
|
1304
|
1511
|
|
1305
|
1512
|
|
1306
|
|
-
|
1307
|
|
-
|
1308
|
1513
|
def average_duplicate_points(points):
|
1309
|
1514
|
"""
|
1310
|
1515
|
处理点云,计算每个 (x, y) 坐标中所有 z 值的均值。
|
|
@@ -1323,6 +1528,7 @@ def average_duplicate_points(points):
|
1323
|
1528
|
|
1324
|
1529
|
return unique_points[['x', 'y', 'z']].to_numpy()
|
1325
|
1530
|
|
|
1531
|
+
|
1326
|
1532
|
def remove_duplicate_points(points):
|
1327
|
1533
|
"""
|
1328
|
1534
|
处理点云,保留每个 (x, y) 坐标中与全局 z 值均值最近的一个 z 值。
|
|
@@ -1446,6 +1652,135 @@ def laplacian_smoothing(points, z_values, iterations=10, alpha=0.1):
|
1446
|
1652
|
|
1447
|
1653
|
|
1448
|
1654
|
|
|
1655
|
+def project_points_to_camera(P_world, K, R, t):
|
|
1656
|
+ """
|
|
1657
|
+ 将世界坐标系中的 3D 点投影到相机的图像平面。
|
|
1658
|
+
|
|
1659
|
+ Parameters:
|
|
1660
|
+ P_world: numpy.ndarray
|
|
1661
|
+ 世界坐标系中的 3D 点,形状为 (N, 3)。
|
|
1662
|
+ K: numpy.ndarray
|
|
1663
|
+ 相机内参矩阵,形状为 (3, 3)。
|
|
1664
|
+ R: numpy.ndarray
|
|
1665
|
+ 相机外参的旋转矩阵,形状为 (3, 3)。
|
|
1666
|
+ t: numpy.ndarray
|
|
1667
|
+ 相机外参的平移向量,形状为 (3, 1)。
|
|
1668
|
+
|
|
1669
|
+ Returns:
|
|
1670
|
+ p_image: numpy.ndarray
|
|
1671
|
+ 投影到图像平面的 2D 点,形状为 (N, 2)。
|
|
1672
|
+ """
|
|
1673
|
+ # 将世界坐标系中的点转换为相机坐标系
|
|
1674
|
+ P_cam = R @ P_world.T + t
|
|
1675
|
+ P_cam = P_cam.T
|
|
1676
|
+
|
|
1677
|
+ # 将相机坐标系中的点投影到图像平面
|
|
1678
|
+ P_image_homogeneous = K @ P_cam.T
|
|
1679
|
+ P_image_homogeneous = P_image_homogeneous.T
|
|
1680
|
+
|
|
1681
|
+ # 转换为 2D 图像坐标
|
|
1682
|
+ p_image = P_image_homogeneous[:, :2] / P_image_homogeneous[:, 2][:, np.newaxis]
|
|
1683
|
+
|
|
1684
|
+ return p_image
|
|
1685
|
+
|
|
1686
|
+def cross_validate_camera_pair(P_world, K_A, R_A, t_A, K_B, R_B, t_B, p_B_measured):
|
|
1687
|
+ """
|
|
1688
|
+ 验证相机 A 的标定点在相机 B 中的投影误差。
|
|
1689
|
+
|
|
1690
|
+ Parameters:
|
|
1691
|
+ P_world: numpy.ndarray
|
|
1692
|
+ 标定板上的 3D 点的世界坐标,形状为 (N, 3)。
|
|
1693
|
+ K_A, R_A, t_A: numpy.ndarray
|
|
1694
|
+ 相机 A 的内参矩阵、旋转矩阵和平移向量。
|
|
1695
|
+ K_B, R_B, t_B: numpy.ndarray
|
|
1696
|
+ 相机 B 的内参矩阵、旋转矩阵和平移向量。
|
|
1697
|
+ p_B_measured: numpy.ndarray
|
|
1698
|
+ 相机 B 实际测量到的标定点,形状为 (N, 2)。
|
|
1699
|
+
|
|
1700
|
+ Returns:
|
|
1701
|
+ reprojection_error: float
|
|
1702
|
+ 计算得到的投影误差。
|
|
1703
|
+ """
|
|
1704
|
+ # 将 P_world 投影到相机 B 的图像平面
|
|
1705
|
+ R_BA = R_B @ np.linalg.inv(R_A) # 相机 A 和相机 B 的旋转矩阵关系
|
|
1706
|
+ t_BA = t_B - R_B @ np.linalg.inv(R_A) @ t_A # 相机 A 和相机 B 的平移向量关系
|
|
1707
|
+ p_B_projected = project_points_to_camera(P_world, K_B, R_BA, t_BA)
|
|
1708
|
+
|
|
1709
|
+ # 计算投影误差
|
|
1710
|
+ reprojection_error = np.mean(np.linalg.norm(p_B_projected - p_B_measured, axis=1))
|
|
1711
|
+
|
|
1712
|
+ return reprojection_error
|
|
1713
|
+
|
|
1714
|
+def plot_camera(ax, rotation_matrix, translation_vector, camera_id, scale=1.0, color='r'):
|
|
1715
|
+ """
|
|
1716
|
+ 绘制单个相机的三维表示:位置、方向和视野
|
|
1717
|
+ R: 3x3旋转矩阵 (从世界坐标系到相机坐标系)
|
|
1718
|
+ t: 3x1平移向量 (相机位置)
|
|
1719
|
+ scale: 控制相机大小的比例因子
|
|
1720
|
+ color: 相机视野的颜色
|
|
1721
|
+ """
|
|
1722
|
+ # 相机的中心
|
|
1723
|
+ camera_center = -rotation_matrix.T @ translation_vector
|
|
1724
|
+ #print('camera_center = ', camera_center)
|
|
1725
|
+ camera_center = np.squeeze(camera_center) # 确保 camera_center 是 1D 数组
|
|
1726
|
+ #print('camera_center = ', camera_center)
|
|
1727
|
+
|
|
1728
|
+ # 定义相机的四个角点在相机坐标系中的位置
|
|
1729
|
+ camera_points = np.array([
|
|
1730
|
+ [0, 0, 0], # 相机中心
|
|
1731
|
+ [1, 1, 2], # 视野的左上角
|
|
1732
|
+ [1, -1, 2], # 视野的左下角
|
|
1733
|
+ [-1, -1, 2], # 视野的右下角
|
|
1734
|
+ [-1, 1, 2] # 视野的右上角
|
|
1735
|
+ ]) * scale
|
|
1736
|
+
|
|
1737
|
+ # 将相机坐标系下的点转换到世界坐标系
|
|
1738
|
+ camera_points_world = (rotation_matrix.T @ camera_points.T).T + camera_center.T
|
|
1739
|
+ # 绘制相机位置(红色圆点表示相机中心)
|
|
1740
|
+ ax.scatter(camera_center[0], camera_center[1], camera_center[2], color=color, marker='o', s=100)
|
|
1741
|
+
|
|
1742
|
+ # 绘制相机的 X、Y、Z 坐标轴
|
|
1743
|
+ axis_length = scale * 3 # 坐标轴箭头长度
|
|
1744
|
+ x_axis = rotation_matrix[:, 0] # X 轴方向
|
|
1745
|
+ y_axis = rotation_matrix[:, 1] # Y 轴方向
|
|
1746
|
+ z_axis = rotation_matrix[:, 2] # Z 轴方向
|
|
1747
|
+
|
|
1748
|
+ # X 轴箭头(红色)
|
|
1749
|
+ ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
|
1750
|
+ x_axis[0], x_axis[1], x_axis[2],
|
|
1751
|
+ length=axis_length, color='r', arrow_length_ratio=0.5, label='X axis')
|
|
1752
|
+
|
|
1753
|
+ # Y 轴箭头(绿色)
|
|
1754
|
+ ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
|
1755
|
+ y_axis[0], y_axis[1], y_axis[2],
|
|
1756
|
+ length=axis_length, color='g', arrow_length_ratio=0.5, label='Y axis')
|
|
1757
|
+
|
|
1758
|
+ # Z 轴箭头(蓝色)
|
|
1759
|
+ ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
|
1760
|
+ z_axis[0], z_axis[1], z_axis[2],
|
|
1761
|
+ length=axis_length, color='b', arrow_length_ratio=0.5, label='Z axis')
|
|
1762
|
+
|
|
1763
|
+ # 绘制相机中心
|
|
1764
|
+ ax.scatter(camera_center[0], camera_center[1], camera_center[2], color=color, marker='o', s=100, label="Camera")
|
|
1765
|
+ ax.text(camera_center[0], camera_center[1], camera_center[2], f'Cam {camera_id}', color='black', fontsize=12)
|
|
1766
|
+
|
|
1767
|
+ #绘制相机视野四个角点与相机中心的连线
|
|
1768
|
+ for i in range(1, 5):
|
|
1769
|
+ ax.plot([camera_center[0], camera_points_world[i, 0]],
|
|
1770
|
+ [camera_center[1], camera_points_world[i, 1]],
|
|
1771
|
+ [camera_center[2], camera_points_world[i, 2]], color=color)
|
|
1772
|
+
|
|
1773
|
+ # 绘制相机的四边形视野
|
|
1774
|
+ ax.plot([camera_points_world[1, 0], camera_points_world[2, 0], camera_points_world[3, 0], camera_points_world[4, 0], camera_points_world[1, 0]],
|
|
1775
|
+ [camera_points_world[1, 1], camera_points_world[2, 1], camera_points_world[3, 1], camera_points_world[4, 1], camera_points_world[1, 1]],
|
|
1776
|
+ [camera_points_world[1, 2], camera_points_world[2, 2], camera_points_world[3, 2], camera_points_world[4, 2], camera_points_world[1, 2]], color=color)
|
|
1777
|
+
|
|
1778
|
+ # # 添加相机方向箭头
|
|
1779
|
+ # ax.quiver(camera_center[0], camera_center[1], camera_center[2],
|
|
1780
|
+ # rotation_matrix[0, 2], rotation_matrix[1, 2], rotation_matrix[2, 2], length=scale, color='g', arrow_length_ratio=1, label="Direction")
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
|
1449
|
1784
|
def bilinear_interpolation(points, z_values, grid_size=(100, 100)):
|
1450
|
1785
|
"""
|
1451
|
1786
|
使用双线性插值对点云 z 值进行平滑。
|
|
@@ -1515,49 +1850,40 @@ def moving_average_filter_z(points, window_size=3):
|
1515
|
1850
|
return np.column_stack((points[:,0], points[:,1], smoothed_z))
|
1516
|
1851
|
|
1517
|
1852
|
|
1518
|
|
-
|
1519
|
|
-def rbf_interpolation(points, z_values, grid_size=(100, 100)):
|
1520
|
|
- """
|
1521
|
|
- 使用径向基函数 (RBF) 对点云 z 值进行插值和平滑。
|
1522
|
|
-
|
1523
|
|
- 参数:
|
1524
|
|
- points: (n, 2) 形状的 NumPy 数组,表示 (x, y) 坐标的点
|
1525
|
|
- z_values: 点云的 z 值数组
|
1526
|
|
- grid_size: 定义插值后生成的网格大小
|
1527
|
|
-
|
1528
|
|
- 返回:
|
1529
|
|
- grid_z: 插值后的平滑 z 值
|
1530
|
|
- """
|
1531
|
|
- # 定义插值网格
|
1532
|
|
- x_min, x_max = points[:, 0].min(), points[:, 0].max()
|
1533
|
|
- y_min, y_max = points[:, 1].min(), points[:, 1].max()
|
1534
|
|
- grid_x, grid_y = np.mgrid[x_min:x_max:grid_size[0]*1j, y_min:y_max:grid_size[1]*1j]
|
1535
|
|
-
|
1536
|
|
- # 使用径向基函数进行插值
|
1537
|
|
- rbf = Rbf(points[:, 0], points[:, 1], z_values, function='linear')
|
1538
|
|
- grid_z = rbf(grid_x, grid_y)
|
1539
|
|
-
|
1540
|
|
- return grid_x, grid_y, grid_z
|
1541
|
|
-
|
1542
|
|
-
|
1543
|
1853
|
def show_cloud_point(pointcloud_path):
|
1544
|
1854
|
points = np.loadtxt(os.path.join(pointcloud_path), delimiter=',')
|
1545
|
1855
|
|
1546
|
|
-
|
1547
|
1856
|
|
1548
|
1857
|
|
|
1858
|
+def img_diff():
|
|
1859
|
+ img_dir1 = 'D:\\code\\Gamma\\Gamma\\3_pattern\\'
|
|
1860
|
+ img_dir2 = 'D:\\code\\Gamma\\Gamma\\patterns\\'
|
|
1861
|
+ for img in os.listdir(img_dir1):
|
|
1862
|
+ img_path1 = img_dir1 + img
|
|
1863
|
+ img_path2 = img_dir2 + img
|
|
1864
|
+ src1 = cv2.imread(img_path1)
|
|
1865
|
+ src2 = cv2.imread(img_path2)
|
|
1866
|
+ diff = 50 * cv2.absdiff(src1, src2)
|
|
1867
|
+ print(np.mean(diff))
|
|
1868
|
+ cv2.namedWindow('diff', 0)
|
|
1869
|
+ cv2.imshow('diff', diff)
|
|
1870
|
+ cv2.waitKey(0)
|
|
1871
|
+
|
|
1872
|
+
|
|
1873
|
+
|
1549
|
1874
|
|
1550
|
1875
|
|
1551
|
1876
|
|
1552
|
1877
|
if __name__ == '__main__':
|
|
1878
|
+
|
1553
|
1879
|
# white_path = 'D:\\data\\four_cam\\1008_storage\\'
|
1554
|
1880
|
# cam_num = 1
|
1555
|
|
- # #find_notch(white_path, cam_num)
|
|
1881
|
+ #find_notch(white_path, cam_num)
|
1556
|
1882
|
# for img in os.listdir(white_path):
|
1557
|
1883
|
# if img.endswith('.bmp'):
|
1558
|
1884
|
# get_white_mask(white_path + img, bin_thresh=12)
|
1559
|
|
- #camera_calibrate_vis()
|
1560
|
|
- img_folder = 'D:\\data\\four_cam\\1008_storage\\20241008210412543'
|
1561
|
|
- point_cloud_path = os.path.join(img_folder, 'cloudpoint.txt')
|
1562
|
|
- show_cloud_point(point_cloud_path)
|
|
1885
|
+ # img_folder = 'D:\\data\\four_cam\\1008_storage\\20241008210412543'
|
|
1886
|
+ # point_cloud_path = os.path.join(img_folder, 'cloudpoint.txt')
|
|
1887
|
+ # show_cloud_point(point_cloud_path)
|
|
1888
|
+ img_diff()
|
1563
|
1889
|
|