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.

185 lines
6.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import numpy as np
from sklearn.linear_model import RANSACRegressor, LinearRegression
import matplotlib.pyplot as plt
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):
"""
保留数组中间80%的数据删除首尾各10%)。
参数:
data (np.ndarray): 输入数组(可以是一维或多维,但会先展平)。
返回:
np.ndarray: 中间80%的数据。
"""
# 展平数组(确保处理的是所有数据点)
# 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()
# # 计算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
# print(f'data_range: {data_range}, data_min: {data_min}, data_max: {data_max}')
# 计算中间80%的上下界
lower_bound = data_min + cameraModel.filt_percent * data_range
upper_bound = data_max - cameraModel.filt_percent * data_range
# 筛选数据
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)
if cameraModel.grid_downsample:
int_data = data.astype(int)
grid_sampled = grid_downsample(int_data, cell_size=cameraModel.cell_size)
data = grid_sampled
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
if cameraModel.filter:
mask = filter_middle_percent(x, y, cameraModel)
x_clean = x[mask]
y_clean = y[mask]
else:
x_clean = x
y_clean = y
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)
# print(f"文件数据点个数为:{len(x)}\n")
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
# print(f"第一次拟合内点数量:{len(x_inliers1)}\n外点数量{len(x_outliers1)}\n")
if len(x_outliers1) > cameraModel.outlier_num: # 确保有足够的外点进行第二次拟合
model2, inlier_mask2, outlier_mask2 = ransac_fit(x_outliers1, y_outliers1,
residual_threshold=cameraModel.ransac_residual_threshold)
else:
print(f"外点数量不足\n第一次拟合内点数量:{len(x_inliers1)}\n外点数量:{len(x_outliers1)}\n")
return 0, None, None, None, None, None, None, None, None
x_inliers2 = x_outliers1[inlier_mask2]
y_inliers2 = y_outliers1[inlier_mask2]
# 获取第二次拟合的外点
x_outliers2 = x_outliers1[outlier_mask2]
y_outliers2 = y_outliers1[outlier_mask2]
print(f"第二次拟合内点数量:{len(x_inliers2)}\n外点数量:{len(x_outliers2)}\n")
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))
#
# # 绘制原始内点
# plt.scatter(x_inliers1, y_inliers1,
# color='limegreen', marker='o', s=30, alpha=0.7,
# 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')
#
# 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)
# plt.tight_layout()
# plt.show()
return 1, x_bot, y_bot, slope_bot, intercept_bot, x_top, y_top, slope_top, intercept_top