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

基于Gekko实现相机镜头畸变校准的可逆性约束优化

相机镜头畸变校准的Gekko建模:复杂可逆性约束实现问题

我正在用Gekko求解相机镜头畸变校准的最优解,变量组为K1、K2或K1、K2、CX、CY。此前用scipy.optimize.minimize得到了合理结果,但因最终结果依赖复杂的可逆性约束,无法在Scipy中实现,现寻求在Gekko中建模该约束的方法。

可逆性约束定义

约束逻辑由div_constraint_invertibility()实现,依赖以下辅助函数:

辅助函数1:计算归一化系数

def get_rad_norm_k(dc, w, h):
      dmi = get_max_dist(dc=dc, w=w, h=h)
      r2 = m.sqrt(dmi) / 2
      r2_2 = r2*r2
      r2_4 = r2_2*r2_2
      denk1 = -12 * r2_2
      denk2 = -12 * r2_4
      return denk1, denk2

辅助函数2:计算最大距离

def get_max_dist(dc, h, w):
    corner_distances = [(dc[0] - 0)**2 + (dc[1] - 0)**2,
                        (dc[0] - 0)**2 + (dc[1] - w)**2,
                        (dc[0] - h)**2 + (dc[1] - w)**2,
                        (dc[0] - h)**2 + (dc[1] - 0)**2]

    mx = corner_distances[0]
    for cd in corner_distances[1:]:
        mx = m.max3(mx, cd)
    return mx

核心约束函数

参数x为[P1,P2]或[P1,P2,CX,CY],其中P1、P2是K1、K2的归一化值:

def div_constraint_invertibility(x, ldm):

    p1 = x[0]
    p2 = x[1]
    dc = [ldm.Cx, ldm.Cy]
    
    w = ldm.width
    h = ldm.height
    denk1, denk2 = get_rad_norm_k(dc=dc, w=w, h=h)
    
    r1sq = get_max_dist(dc=dc, w=w, h=h)
    r1p4 = r1sq**2
    
    k1 = (((-p1) / (1 + p1)) + ((16 * p2) / (1 + p2))) / denk1
    k2 = (((-4 * p2) / (1 + p2)) + (p1 / (1 + p1))) / denk2
    
    if -2 < r1sq * k1 < 2:
        if -1. - r1sq * k1 < r1p4 * k2 < (1. - r1sq * k1) / 3:
            return 1  # 约束满足
    else:
        if r1sq * k1 >= 2:
            if -1. - r1sq * k1 < r1p4 * k2 < (-r1p4 * k1**2 / 12):
                return 1  # 约束满足
    
    return -1  # 约束不满足

我已尝试用Gekko函数实现相关逻辑,下一步打算用m.if3()实现上述条件判断,但嵌套m.if3()不够优雅,且不确定约束函数应返回何种值(不应是-1或1,需返回符合Gekko要求的约束函数值)。


编辑1:Scipy实现参考

以下是Scipy最小化的实现方式,目标函数cumulative_point_to_line_error提取畸变图像中的弧点,应用畸变模型计算去畸变后点的直线拟合误差。初始点示例:

dm0 = [-0.0, 0.0, input_image_center_X, input_image_center_Y]

同时包含对应弧的点数组(例如某图像可能有30组提取弧的点列表):

def model_estimation(ip, w, h, oc):
    ldm = ip.lens_model
    dc = [ldm.Cx, ldm.Cy]
    prevk1 = ldm.d[1]

    if len(ldm.d) != 3:
        ldm.d.append(0)
    prevk2 = ldm.d[2]
    if oc:
        dm0 = [prevp1, prevp2, dc[0], dc[1]]
    else:
        dm0 = [prevp1, prevp2]

    cons = []
    bnds = []
    if oc:
        dc_drift = 2
        bnds = [(-np.inf, np.inf), (-np.inf, np.inf),
                (float(ldm.width / 2 - dc_drift), float(ldm.width / 2 + dc_drift)),
                (float(ldm.height / 2 - dc_drift), float(ldm.height / 2 + dc_drift))]

    cons.append({'type': 'ineq', 'fun': div_constraint_invertibility, 'args': (ip.lens_model,)})

    print(f"Optimizing: {dm0}")
    if cons:
        print(f"Constraints:")
        for con in cons:
            print(f"Const: {con['fun'].__name__}")
    if bnds:
        print(f"Bounds:")
        for idx, bnd in enumerate(bnds):
            print(f"{bnd[0]} <= x{idx} <= {bnd[1]}")

    result = spo.minimize(fun=cumulative_point_to_line_error, x0=np.array(dm0), args=(ip,),
                          constraints=cons, bounds=bnds, method='SLSQP', options={"disp": True})

    print(f"Optimization Result: {result.x} | {result.fun}")

编辑2:Gekko目标函数转换尝试

我正在将目标函数转换为Gekko可用形式,疑问是否可对cumulative_point_to_line_error调用m.Minimize()。目前正在处理div_update_point中的除零检查,以及dist_points_to_equation中的条件判断:

def dist_points_to_equation(m, k1, k2, dc_x, dc_y, hl: Line):
    pnts_o = hl.pnts
    err = 0
    zero = 10e-100
    num_pnts = len(pnts_o)
    um = vm = 0
    suu = suv = svv = 0

    pnts_e = []
    for pnt in pnts_o:
        x, y = div_update_point(m=m, px=pnt[0], py=pnt[1], K1=k1, K2=k2, dc_x=dc_x, dc_y=dc_y)
        pnts_e.append([x, y])

    for pnt in pnts_e:
        x = pnt[0]
        y = pnt[1]
        um += x
        vm += y
    um = um / num_pnts
    vm = vm / num_pnts

    for pnt in pnts_e:
        x = pnt[0]
        y = pnt[1]
        suu += (x - um) ** 2
        suv += (x - um) * (y - vm)
        svv += (y - vm) ** 2
    suu = suu / num_pnts
    suv = suv / num_pnts
    svv = svv / num_pnts

    if np.abs(suv) <= zero:
        # print('XY <= ZERO*')
        if suu < svv and svv > zero:
            hl.a = 1.
            hl.b = 0.
            hl.c = -um
            err = 0.
            # print(f'Iter. Method Line: {self.a}x + {self.b}y + {self.c} = 0')
            # print(f'Error == {err}')
            return hl, err
        if svv < suu and suu > zero:
            hl.a = 0.
            hl.b = 1.
            hl.c = -vm
            err = 0
            # print(f'Iter. Method Line: {self.a}x + {self.b}y + {self.c} = 0')
            # print(f'Error == {err}')
            return hl, err
        # print('Failed to recalculate!')
        return None, err

    r = np.array([[1, 0, 0],
                  [1, 0, 0],
                  [0, 1, 0],
                  [0, 1, 0]], dtype=float)

    h = 0.5 * (suu - svv) / suv
    if h > 0:
        r[0, 1] = -h - np.sqrt(1. + np.square(h))
        r[0, 2] = -(um + r[0, 1] * vm)
        r[1, 1] = -1. / r[0, 1]
        r[1, 2] = -(um + r[1, 1] * vm)
        r[2, 0] = h + np.sqrt(1. + np.square(h))
        r[2, 2] = -(r[2, 0] * um + vm)
        r[3, 0] = -1. / r[2, 0]
        r[3, 2] = -(r[3, 0] * um + vm)
    else:
        r[0, 1] = -h + np.sqrt(1. + np.square(h))
        r[0, 2] = -(um + r[0, 1] * vm)
        r[1, 1] = -1. / r[0, 1]
        r[1, 2] = -(um + r[1, 1] * vm)
        r[2, 0] = h - np.sqrt(1. + np.square(h))
        r[2, 2] = -(r[2, 0] * um + vm)
        r[3, 0] = -1. / r[2, 0]
        r[3, 2] = -(r[3, 0] * um + vm)

    for row in range(0, r.shape[0]):
        norm = np.sqrt(np.square(r[row, 0]) + np.square(r[row, 1]))
        for col in range(0, r.shape[1]):
            r[row, col] /= norm

    min_err = 0
    k = 0
    for pnt in pnts_e:
        x = pnt[0]
        y = pnt[1]
        sol = r[0, 0] * x + r[0, 1] * y + r[0, 2]
        min_err += np.square(sol)

    for row in range(1, r.shape[0]):
        h = 0
        for pnt in pnts_e:
            x = pnt[0]
            y = pnt[1]
            sol = r[row, 0] * x + r[row, 1] * y + r[row, 2]
            h += np.square(sol)
        if h < min_err:
            k = row
            min_err = h

    hl.a = r[k, 0]
    hl.b = r[k, 1]
    hl.c = r[k, 2]
    # err = min_err / num_pnts
    # print(min_err, num_pnts)
    # print(err)

    return hl, min_err


def cumulative_point_to_line_error(m, p1, p2, dc_x, dc_y, w, h, houghlines):

    denk1, denk2 = get_rad_norm_k(m, dc_x=dc_x, dc_y=dc_y, w=w, h=h)

    k1 = m.Intermediate(((-p1) / (1 + p1)) + (16 * p2 / (1 + p2))/denk1)
    k2 = m.Intermediate(((-4 * p2) / (1 + p2)) + (p1 / (1 + p1))/denk2)

    cumulative_err = 0
    for idx, hl in enumerate(houghlines):
        new_hl, err = dist_points_to_equation(m, k1, k2, dc_x, dc_y, hl)
        houghlines[idx] = new_hl
        cumulative_err += err
    return cumulative_err

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 05:55:55