You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.8 KiB

3 months ago
import numpy as np
from sklearn.linear_model import RANSACRegressor, LinearRegression
import matplotlib.pyplot as plt
3 months ago
import pandas as pd
import calc_way
def grid_downsample(points, cell_size=15):
"""网格化降采样,保持空间结构"""
df = pd.DataFrame(points, columns=['x', 'y'])
df['x_grid'] = (df['x'] // cell_size) * cell_size
df['y_grid'] = (df['y'] // cell_size) * cell_size
sampled = df.groupby(['x_grid', 'y_grid']).first().reset_index()
return sampled[['x', 'y']].values
def filter_middle_percent(data_x, data_y, cameraModel):
3 months ago
"""
保留数组中间80%的数据删除首尾各10%
参数:
data (np.ndarray): 输入数组可以是一维或多维但会先展平
返回:
np.ndarray: 中间80%的数据
"""
# 展平数组(确保处理的是所有数据点)
3 months ago
# Xw_bot = []
# for i in range(len(data_x)):
# Xw, Yw = calc_way.calc_distance(cameraModel, data_x[i], data_y[i])
# Xw_bot.append(Xw)
# Xw_bot = np.array(Xw_bot)
# flattened_data = Xw_bot.flatten()
flattened_data = data_x.flatten()
3 months ago
# # 计算10%和90%分位数
# lower_bound = np.percentile(flattened_data, 15)
# upper_bound = np.percentile(flattened_data, 75)
#
# # 筛选中间80%的数据
# mask = (data >= lower_bound) & (data <= upper_bound)
# 计算最大值、最小值和总范围
data_min = np.min(flattened_data)
data_max = np.max(flattened_data)
data_range = data_max - data_min
3 months ago
# print(f'data_range: {data_range}, data_min: {data_min}, data_max: {data_max}')
3 months ago
# 计算中间80%的上下界
3 months ago
lower_bound = data_min + cameraModel.filt_percent * data_range
upper_bound = data_max - cameraModel.filt_percent * data_range
3 months ago
# 筛选数据
mask = (flattened_data >= lower_bound) & (flattened_data <= upper_bound)
return mask
def load_data(cameraModel, txt_name):
"""从用户输入的文件路径加载二维XY数据"""
while True:
filepath = txt_name.strip()
if filepath.lower() == 'q':
return None, None
try:
data = np.loadtxt(filepath)
3 months ago
if cameraModel.grid_downsample:
int_data = data.astype(int)
grid_sampled = grid_downsample(int_data, cell_size=cameraModel.cell_size)
data = grid_sampled
3 months ago
if data.shape[1] != 2:
print("错误文件必须包含两列数据X和Y")
continue
x = data[:, 0].reshape(-1, 1)
y = data[:, 1].reshape(-1, 1)
y = cameraModel.camera_height - y
3 months ago
if cameraModel.filter:
mask = filter_middle_percent(x, y, cameraModel)
else:
mask = filter_middle_percent(x, y, cameraModel)
3 months ago
x_clean = x[mask]
y_clean = y[mask]
return x_clean.reshape(-1, 1), y_clean.reshape(-1, 1)
except Exception as e:
print(f"加载文件出错: {e}")
def ransac_fit(x, y, residual_threshold=2.5):
"""执行RANSAC拟合并返回模型和内点/外点"""
ransac = RANSACRegressor(
LinearRegression(),
residual_threshold=residual_threshold,
random_state=42
)
ransac.fit(x, y)
inlier_mask = ransac.inlier_mask_
outlier_mask = ~inlier_mask
return ransac.estimator_, inlier_mask, outlier_mask
def get_data(cameraModel, txt_name):
# 加载数据
x, y = load_data(cameraModel, txt_name)
3 months ago
# print(f"文件数据点个数为:{len(x)}\n")
3 months ago
if x is None:
return 0, None, None, None, None, None, None, None, None
# 第一次RANSAC拟合
model1, inlier_mask1, outlier_mask1 = ransac_fit(x, y, residual_threshold=cameraModel.ransac_residual_threshold)
x_inliers1 = x[inlier_mask1]
y_inliers1 = y[inlier_mask1]
# 获取第一次拟合的外点
x_outliers1 = x[outlier_mask1]
y_outliers1 = y[outlier_mask1]
# 第二次RANSAC拟合在外点上
model2, inlier_mask2, outlier_mask2 = None, None, None
3 months ago
# print(f"第一次拟合内点数量:{len(x_inliers1)}\n外点数量{len(x_outliers1)}\n")
if len(x_outliers1) > cameraModel.outlier_num: # 确保有足够的外点进行第二次拟合
3 months ago
model2, inlier_mask2, outlier_mask2 = ransac_fit(x_outliers1, y_outliers1,
residual_threshold=cameraModel.ransac_residual_threshold)
3 months ago
else:
print(f"外点数量不足\n第一次拟合内点数量:{len(x_inliers1)}\n外点数量:{len(x_outliers1)}\n")
return 0, None, None, None, None, None, None, None, None
3 months ago
x_inliers2 = x_outliers1[inlier_mask2]
y_inliers2 = y_outliers1[inlier_mask2]
# 获取第二次拟合的外点
x_outliers2 = x_outliers1[outlier_mask2]
y_outliers2 = y_outliers1[outlier_mask2]
3 months ago
print(f"第二次拟合内点数量:{len(x_inliers2)}\n外点数量:{len(x_outliers2)}\n")
3 months ago
m1 = model1.predict(np.array([600]).reshape(-1, 1))
m2 = model2.predict(np.array([600]).reshape(-1, 1))
# 判断上下沿
if m1 > m2:
model_top, model_bot = model1, model2
x_top, x_bot = x_inliers1, x_inliers2
y_top, y_bot = y_inliers1, y_inliers2
else:
model_top, model_bot = model2, model1
x_top, x_bot = x_inliers2, x_inliers1
y_top, y_bot = y_inliers2, y_inliers1
# 统一提取斜率和截距
slope_top = model_top.coef_[0][0]
intercept_top = model_top.intercept_[0]
slope_bot = model_bot.coef_[0][0]
intercept_bot = model_bot.intercept_[0]
# plt.figure(figsize=(14, 7))
#
# # 绘制原始内点
3 months ago
# plt.scatter(x_inliers1, y_inliers1,
3 months ago
# color='limegreen', marker='o', s=30, alpha=0.7,
3 months ago
# label='first_inliers')
#
# # 绘制第一次拟合的直线
# line_x = np.array([x.min(), x.max()]).reshape(-1, 1)
# line_y_1 = model1.predict(line_x)
# plt.plot(line_x, line_y_1, color='darkgreen', linewidth=3,
# label='first RANSAC')
#
# plt.scatter(x_inliers2, y_inliers2,
# color='yellow', marker='o', s=100, edgecolor='black',
# label='second_inliers')
#
# # 绘制第二次拟合的直线
# line_x = np.array([x.min(), x.max()]).reshape(-1, 1)
# line_y_1 = model2.predict(line_x)
# plt.plot(line_x, line_y_1, color='darkgreen', linewidth=3,
# label='second RANSAC')
#
# plt.scatter(x_outliers2, y_outliers2,
# color='gray', marker='*', s=100, edgecolor='black',
# label='second outliner')
#
3 months ago
# plt.xlabel('X', fontsize=12)
# plt.ylabel('Y', fontsize=12)
# plt.title(f'{txt_name}', fontsize=14)
# plt.legend(fontsize=10, loc='best')
# plt.grid(True, alpha=0.3)
3 months ago
# plt.tight_layout()
3 months ago
# plt.show()
return 1, x_bot, y_bot, slope_bot, intercept_bot, x_top, y_top, slope_top, intercept_top