使用OpenCV与NumPy poly1d测量物体尺寸的曲线拟合异常问题
电线杆高度测量多项式拟合异常问题
问题背景
我想用NumPy生成拟合曲线,通过图像中标尺的已知点预测未知点的高度测量值。此前用其他图像测试时,Excel的多项式拟合完全正常,工作中同类软件也能正常使用,但更换新的校准图像后,测量结果偏差极大——尝试测量18'11"(最低导线)的高度时,输出数值瞬间变成数万。
已知条件
- 电线杆前的标尺刻度(从下到上):
- 3'6" → 42英寸
- 6'6" → 78英寸
- 9'8" → 116英寸
- 13' → 156英寸
- 图像已通过OpenCV的
undistort完成相机畸变校正
现有代码
核心拟合逻辑代码
dist = self.firstClick - y self.yData.append(dist) if len(self.yData) > 4: print(self.poly(dist)) if len(self.yData) == 4: array = np.array(self.xData) array = np.expand_dims(array, axis=0) print(self.xData) print(self.yData) array = np.append(array, [self.yData], axis=0) print(array) x = array[:,0] y = array[:,1] self.poly = np.poly1d(np.polyfit(x, y, 2)) poly1d = np.poly1d(self.poly) xp = np.linspace(-2, 20, 1) _ = plt.plot(x, y, '.', xp, self.poly(xp), '-', xp, self.poly(xp), '--') plt.ylim(0,200) plt.show()
图像显示与缩放代码
self.img = cv2.imread(imagePath, cv2.IMREAD_ANYCOLOR) self.scale_percent = 30 self.width = int(self.img.shape[1] * self.scale_percent/100) self.height = int(self.img.shape[0] * self.scale_percent/100) dsize = (self.width, self.height) self.output = cv2.resize(self.img, dsize) img = self.output cv2.imshow('image', img) cv2.setMouseCallback('image', self.click_event) cv2.waitKey()
数值校准代码
if self.firstClick == 0: self.firstClick = y cv2.putText(self.output, "Pole Base", (x, y), font, 1, (255, 255, 0), 2) cv2.imshow('image', self.output) elif self.firstClick != 0 and self.secondClick == 0: self.secondClick = y print("The difference in first and second clicks is", self.firstClick - self.secondClick) first = self.firstClick - self.secondClick inch = first/42 foot = inch*12 self.foot = foot print("One foot is currently: ", foot) self.firstLine = 3.5*12 self.secondLine = 6.5*12 self.thirdLine = 9.67*12 self.fourthLine = 13*12 self.xData = np.array([self.firstLine, self.secondLine, self.thirdLine, self.fourthLine]) self.yData.append(self.firstLine) print(self.firstLine) print(self.secondLine) print(self.thirdLine) print(self.fourthLine)
问题根源分析
- 数据对应关系完全错误:拟合时
x = array[:,0]和y = array[:,1]取的是xData的前两个元素,完全没有用到yData的像素距离值,相当于用错误的数据集做拟合。 - 拟合因果颠倒:需要用像素距离预测实际高度,但现有代码用实际高度作为x,像素距离作为y拟合,预测时输入像素距离会导致数值爆炸。
- yData初始化错误:校准代码中把实际高度值
self.firstLine加入yData,但后续yData应该存储像素距离,导致数据类型混合,拟合基础错误。 - 绘图参数无效:
xp = np.linspace(-2, 20, 1)只生成1个点,无法验证拟合曲线是否合理。
修正方案
1. 修正yData初始化
在校准代码中删除self.yData.append(self.firstLine),改为在点击标尺的四个刻度点时,依次将对应的像素距离(dist = self.firstClick - y)加入yData。
2. 修正核心拟合逻辑
dist = self.firstClick - y self.yData.append(dist) # 收集完4个标尺点后执行拟合 if len(self.yData) == 4: print("实际高度(英寸):", self.xData) print("像素距离:", self.yData) # 用像素距离预测实际高度,x为像素距离,y为实际高度 self.poly = np.poly1d(np.polyfit(self.yData, self.xData, 2)) # 生成合理的绘图区间,覆盖已知点及可能的未知点范围 xp = np.linspace(0, max(self.yData)*1.5, 100) plt.plot(self.yData, self.xData, '.', xp, self.poly(xp), '-') plt.xlabel("像素距离") plt.ylabel("实际高度(英寸)") plt.ylim(0, 250) # 覆盖18'11"=227英寸的测量需求 plt.show() # 预测未知点高度 if len(self.yData) > 4: predicted_inch = self.poly(dist) predicted_foot = predicted_inch / 12 print(f"预测高度: {predicted_foot:.2f} 英尺 ({predicted_inch:.2f} 英寸)")
3. 额外注意事项
- 确保点击标尺四个刻度点时,
yData中存储的是每个刻度到电线杆底部的像素距离,xData对应每个刻度的实际高度(42、78、116、156英寸)。 - 若二次多项式拟合仍有偏差,可以尝试一次多项式(线性拟合),因为图像经过畸变校正后,高度与像素距离大概率呈线性关系。
内容的提问来源于stack exchange,提问作者ABloode
相关产品推荐
相关产品推荐

