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.

140 lines
4.6 KiB

3 months ago
import numpy as np
from sklearn.linear_model import RANSACRegressor, LinearRegression
import matplotlib.pyplot as plt
def filter_middle_percent(data, percent):
"""
保留数组中间80%的数据删除首尾各10%
参数:
data (np.ndarray): 输入数组可以是一维或多维但会先展平
返回:
np.ndarray: 中间80%的数据
"""
# 展平数组(确保处理的是所有数据点)
flattened_data = data.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
# 计算中间80%的上下界
lower_bound = data_min + percent * data_range
upper_bound = data_max - 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 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
mask = filter_middle_percent(x, cameraModel.filt_percent)
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)
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
if len(x_outliers1) > 10: # 确保有足够的外点进行第二次拟合
model2, inlier_mask2, outlier_mask2 = ransac_fit(x_outliers1, y_outliers1,
residual_threshold=cameraModel.ransac_residual_threshold)
x_inliers2 = x_outliers1[inlier_mask2]
y_inliers2 = y_outliers1[inlier_mask2]
# 获取第二次拟合的外点
x_outliers2 = x_outliers1[outlier_mask2]
y_outliers2 = y_outliers1[outlier_mask2]
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_bot, y_bot,
# color='limegreen', marker='o', s=30, alpha=0.7,
# label='bot')
# plt.scatter(x_top, y_top,
# color='red', marker='*', s=100, edgecolor='black',
# label='top')
# 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