You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Scipy实现所有点均在直线上方的约束直线拟合?

带约束的直线拟合(所有点位于直线上方)

问题描述

现有一张包含白色点的图像,需要对这些白色点进行直线拟合,且要求所有点均位于拟合直线的上方。目前已编写无约束条件的拟合脚本,请求协助修改代码以实现该约束。

原无约束拟合代码

import numpy as np
import matplotlib.pyplot as plt
import cv2
from scipy import optimize

def fit_line(img):

    def func_linear(x, x0, y0, k):
        # y=k(x−xo)+yo
        f = lambda x:k*(x-x0)+y0
        return f(x)

    points = np.where(img>0)
    points = np.array([points[1], points[0]]).T
    
    x = points[:,0]
    y = points[:,1]

    p0 = [1, 1, 1]
    p , e = optimize.curve_fit(func_linear, x, y, p0)

    pt1 = (np.min(x).astype(int), func_linear(np.min(x), *p).astype(int))
    pt2 = (np.max(x).astype(int), func_linear(np.max(x), *p).astype(int))

    cv2.line(img, pt1, pt2, (255,0,0), 3)


img = cv2.imread("toy_2.png")

fit_line(img)

plt.imshow(img)

修改后的约束拟合代码

要实现所有点在直线上方的约束,我们改用带约束的优化方法,用scipy.optimize.minimize自定义损失函数并添加不等式约束:

import numpy as np
import matplotlib.pyplot as plt
import cv2
from scipy import optimize

def fit_line(img):
    # 简化直线方程为y = k*x + b,更便于约束定义
    def line_func(params, x):
        k, b = params
        return k * x + b

    # 损失函数:最小化直线与点的y方向误差平方和
    def loss_func(params, x, y):
        y_pred = line_func(params, x)
        return np.sum((y - y_pred)**2)

    # 约束条件:所有点的y值 >= 直线预测的y值(确保点在直线上方)
    def constraint_func(params, x, y):
        y_pred = line_func(params, x)
        return y - y_pred  # 要求返回值全部 >= 0

    # 提取白色点的(x,y)坐标
    points = np.where(img > 0)
    points = np.array([points[1], points[0]]).T
    x = points[:, 0]
    y = points[:, 1]

    # 用无约束拟合结果作为初始参数,加快收敛
    p0_unconstrained, _ = optimize.curve_fit(lambda x, k, b: k*x + b, x, y)
    initial_guess = p0_unconstrained

    # 设置不等式约束
    constraints = {'type': 'ineq', 'fun': constraint_func, 'args': (x, y)}

    # 执行带约束的最小化优化
    result = optimize.minimize(loss_func, initial_guess, args=(x, y), constraints=constraints)
    k_opt, b_opt = result.x

    # 计算直线端点用于绘制
    x_min, x_max = np.min(x), np.max(x)
    y_min = k_opt * x_min + b_opt
    y_max = k_opt * x_max + b_opt
    pt1 = (int(x_min), int(y_min))
    pt2 = (int(x_max), int(y_max))

    # 绘制约束拟合后的直线
    cv2.line(img, pt1, pt2, (255, 0, 0), 3)

# 加载图像并执行拟合
img = cv2.imread("toy_2.png")
fit_line(img)
plt.imshow(img)
plt.show()

代码说明

  • 替换curve_fit为optimize.minimize,因为前者不支持直接添加不等式约束;
  • 约束条件明确要求每个点的y坐标大于等于直线对应x位置的y值,确保所有点在直线上方;
  • 用无约束拟合结果作为初始参数,提升优化的收敛效率;
  • 损失函数选用y方向误差平方和,在满足约束的前提下让直线尽可能贴近点集。

内容的提问来源于stack exchange,提问作者Ozcan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 18:25:30