大规模优化问题,Scipy?Ceres?PyTorch!

news2024/11/26 4:37:03

背景:

优化问题一般通过scipy.optimize或者Ceres Solver优化器求解。但在参数量较大的优化问题上,scipy提供的BFGSL-BFGS-BCGSLSQP等梯度优化算法其复杂度和存储需求指数级上升,无法满足计算效率;而Ceres需要额外的语言来支持,当前算法工程师一般以python为主,学习成本较大。且项目环境需要多语言混编,配置与部署难度较大。

本文使用PyTorch/TensorFlow等机器学习框架,通过自动求导高效计算梯度,避免手动计算梯度复杂性,利用CPU/CPU来加速求解优化问题。

PyTorch优化四部曲:

# 优化迭代次数,设为1000次
for _ in range(1000):
    # 1. 清零梯度
    optimizer.zero_grad()
    # 通过调用 zero_grad(),我们清零了所有被优化参数的梯度。这是因为在 PyTorch 中,
    # 每次进行反向传播时,梯度默认是累积的(即不会自动清零)。
    # 如果不清理梯度,可能会导致累积误差,从而影响下一步的梯度计算。
    
    # 2. 计算损失函数
    loss = objective_function(params)
    # 这里我们计算当前参数下的目标函数值(损失函数)。
    # 损失函数的值将作为后续反向传播计算梯度的基础。
    # 根据参数(params)的当前值,求出损失值(即预测和实际的误差)。
    
    # 3. 反向传播计算梯度
    loss.backward()
    # 调用 loss.backward() 会进行反向传播,计算损失函数对所有优化参数的梯度。
    # 这些梯度将存储在各个参数的 .grad 属性中,以便在优化步骤中使用。
    
    # 4. 更新参数
    optimizer.step()
    # 调用 optimizer.step() 会应用优化器来更新参数。
    # 优化器会利用刚才计算的梯度(存储在 .grad 属性中),按照指定优化算法(如 Adam)
    # 的规则更新参数。这样一来,参数会朝着能最小化损失函数的方向一步步调整。

开胃小菜:

在三维空间中,给定初始点(0,0,0),求解优化使其距离点(2,3,4)最近。

import torch

# 定义目标函数,取任意数量优化变量
def objective_function(params):
    x, y, z = params
    return (x - 2)**2 + (y - 3)**2 + (z - 4)**2

# 初始化优化变量
params = [torch.tensor([0.0], requires_grad=True),
          torch.tensor([0.0], requires_grad=True),
          torch.tensor([0.0], requires_grad=True)]

# 优化器选择 Adam ,并传入列表参数
optimizer = torch.optim.Adam(params, lr=0.1)

# 迭代优化
for _ in range(1000):
    optimizer.zero_grad()
    loss = objective_function(params)
    loss.backward()
    optimizer.step()

# 输出优化结果
print(f"Optimized x: {params[0].item()}")
print(f"Optimized y: {params[1].item()}")
print(f"Optimized z: {params[2].item()}")

正餐:

假设场景:现有一张二维平面的游戏地图,并附带一份初始路网,请优化路网形状,使其平滑、美化、且与各个传送点保持挂接。

游戏地图如下,白色区域代表道路,黑色区域为地图外场景:

初始路网可视化见:

初始路网以link和node两张表表示:

node表

{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "nodeid": "90cbaa72-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 586.0, 795.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaa9a-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 576.0, 788.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaaf4-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 621.0, 780.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaa18-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 592.0, 810.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba9f0-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 557.0, 822.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbacf2-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 769.0, 687.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb22e-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1002.0 }, "geometry": { "type": "Point", "coordinates": [ 1070.0, 343.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaf4a-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 812.0, 472.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb2b0-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1058.0, 325.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb27e-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1002.0 }, "geometry": { "type": "Point", "coordinates": [ 1097.0, 327.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb256-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1086.0, 336.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba626-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 372.0, 940.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba6a8-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 420.0, 901.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba5fe-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 390.0, 961.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba6da-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 396.0, 893.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba702-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 537.0, 847.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba5ae-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 370.0, 979.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba892-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1002.0 }, "geometry": { "type": "Point", "coordinates": [ 552.0, 917.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba93c-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 563.0, 887.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba554-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 412.0, 1042.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba96e-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 576.0, 878.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba5d6-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 503.0, 978.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb5e4-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 1149.0, 391.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb67a-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1200.0, 366.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb4e0-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1161.0, 413.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb5b2-3e8e-11ef-a98f-a13c99648c9a", "node_type": 250.0 }, "geometry": { "type": "Point", "coordinates": [ 1413.0, 392.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb1fc-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1117.0, 380.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb1d4-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1110.0, 426.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbadf6-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 899.0, 567.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbae78-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 936.0, 550.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbae50-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 892.0, 555.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb1a2-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1101.0, 436.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbadce-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 872.0, 568.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaa40-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 588.0, 799.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba72a-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 546.0, 835.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb652-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1138.0, 370.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbad9c-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 902.0, 570.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbad74-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 911.0, 587.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbad4c-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 886.0, 603.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba658-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 367.0, 935.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba680-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 357.0, 920.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba75c-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 340.0, 807.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba324-3e8e-11ef-a98f-a13c99648c9a", "node_type": 250.0 }, "geometry": { "type": "Point", "coordinates": [ 270.0, 1191.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb620-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 1145.0, 385.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba996-3e8e-11ef-a98f-a13c99648c9a", "node_type": 1001.0 }, "geometry": { "type": "Point", "coordinates": [ 581.0, 875.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb3dc-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1187.0, 556.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb04e-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 975.0, 767.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbad1a-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 862.0, 607.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbaef0-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 671.0, 527.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba57c-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 343.0, 985.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cba8ec-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 572.0, 902.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb0f8-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 956.0, 708.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbb404-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 1333.0, 551.0 ] } },
{ "type": "Feature", "properties": { "nodeid": "90cbac48-3e8e-11ef-a98f-a13c99648c9a", "node_type": null }, "geometry": { "type": "Point", "coordinates": [ 861.0, 841.0 ] } }
]
}

link表

{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "linkid": "7cba8b52-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbaa72-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaa9a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 586.0, 795.0 ], [ 576.0, 788.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bfa830-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaa9a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba9f0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 576.0, 788.0 ], [ 557.0, 822.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "918092ca-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaaf4-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaa9a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 621.0, 780.0 ], [ 605.0, 774.0 ], [ 578.0, 784.0 ], [ 576.0, 788.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bfa088-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaa18-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba9f0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 592.0, 810.0 ], [ 557.0, 822.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7e44c-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba9f0-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba72a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 557.0, 822.0 ], [ 546.0, 835.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7d84e-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaaf4-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaa18-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 621.0, 780.0 ], [ 607.0, 805.0 ], [ 592.0, 810.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7b4a4-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbacf2-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaaf4-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 769.0, 687.0 ], [ 729.0, 703.0 ], [ 621.0, 780.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cbb79a4-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1002.0, "enode_type": null, "snodeid": "90cbb22e-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb2b0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1070.0, 343.0 ], [ 1058.0, 325.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "91800daa-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaf4a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb2b0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 812.0, 472.0 ], [ 862.0, 441.0 ], [ 867.0, 444.0 ], [ 1058.0, 325.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "9180a2ce-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb2b0-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb256-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1058.0, 325.0 ], [ 1072.0, 316.0 ], [ 1086.0, 336.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb77728-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaf4a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbadce-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 812.0, 472.0 ], [ 872.0, 568.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "9180199e-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbaf4a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaef0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 812.0, 472.0 ], [ 702.0, 540.0 ], [ 681.0, 545.0 ], [ 671.0, 527.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc38e5a-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1002.0, "enode_type": null, "snodeid": "90cbb27e-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb256-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1097.0, 327.0 ], [ 1086.0, 336.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90beb5e2-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb256-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb1fc-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1086.0, 336.0 ], [ 1117.0, 380.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cbcdf4c-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cba626-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba5fe-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 372.0, 940.0 ], [ 390.0, 961.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb801ca-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba6a8-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba5fe-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 420.0, 901.0 ], [ 404.0, 947.0 ], [ 390.0, 961.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bff07e-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba5fe-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba5ae-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 390.0, 961.0 ], [ 370.0, 979.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7f6ee-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba6da-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba6a8-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 396.0, 893.0 ], [ 420.0, 901.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7f176-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba702-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba6a8-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 537.0, 847.0 ], [ 420.0, 901.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb80f3a-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba5ae-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba57c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 370.0, 979.0 ], [ 343.0, 985.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb812aa-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba5ae-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba554-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 370.0, 979.0 ], [ 391.0, 1000.0 ], [ 412.0, 1042.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cbdd14a-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1002.0, "enode_type": null, "snodeid": "90cba892-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba8ec-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 552.0, 917.0 ], [ 572.0, 902.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bfd47c-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba93c-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba8ec-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 563.0, 887.0 ], [ 572.0, 902.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "918066b0-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba554-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba8ec-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 412.0, 1042.0 ], [ 427.0, 1085.0 ], [ 593.0, 982.0 ], [ 585.0, 957.0 ], [ 591.0, 939.0 ], [ 579.0, 913.0 ], [ 572.0, 902.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc29266-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cba96e-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba93c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 576.0, 878.0 ], [ 563.0, 887.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7f4d2-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba702-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba93c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 537.0, 847.0 ], [ 563.0, 887.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb80d00-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba5d6-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba554-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 503.0, 978.0 ], [ 412.0, 1042.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cbebd44-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbb5e4-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb4e0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1149.0, 391.0 ], [ 1161.0, 413.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb74f5a-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb67a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb4e0-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1200.0, 366.0 ], [ 1180.0, 402.0 ], [ 1161.0, 413.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90becfe6-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb4e0-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb1d4-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1161.0, 413.0 ], [ 1150.0, 419.0 ], [ 1110.0, 426.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb74456-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb67a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb652-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1200.0, 366.0 ], [ 1172.0, 353.0 ], [ 1138.0, 370.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "9180030a-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske_solid", "snode_type": 250.0, "enode_type": null, "snodeid": "90cbb5b2-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb67a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1413.0, 392.0 ], [ 1410.0, 408.0 ], [ 1371.0, 401.0 ], [ 1370.0, 397.0 ], [ 1314.0, 375.0 ], [ 1200.0, 366.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb751f8-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb1fc-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb1d4-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1117.0, 380.0 ], [ 1110.0, 426.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb75cd4-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb1d4-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb1a2-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1110.0, 426.0 ], [ 1101.0, 436.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc01324-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbadf6-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbae50-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 899.0, 567.0 ], [ 892.0, 555.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb78cc2-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbae78-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbae50-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 936.0, 550.0 ], [ 901.0, 548.0 ], [ 892.0, 555.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bf31b6-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbae50-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbadce-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 892.0, 555.0 ], [ 872.0, 568.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7903c-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbae78-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbad74-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 936.0, 550.0 ], [ 928.0, 575.0 ], [ 911.0, 587.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7629c-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb1a2-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbae78-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1101.0, 436.0 ], [ 936.0, 550.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb797da-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbadce-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbad1a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 872.0, 568.0 ], [ 862.0, 607.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc1046e-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbaa40-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbaa18-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 588.0, 799.0 ], [ 592.0, 810.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7e604-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba72a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba702-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 546.0, 835.0 ], [ 537.0, 847.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bec2e4-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb652-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb1fc-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1138.0, 370.0 ], [ 1117.0, 380.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc47f36-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbad9c-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbad74-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 902.0, 570.0 ], [ 911.0, 587.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bf39b8-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbad74-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbad4c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 911.0, 587.0 ], [ 886.0, 603.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb799ec-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbad4c-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbad1a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 886.0, 603.0 ], [ 862.0, 607.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb79e38-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbad4c-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb0f8-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 886.0, 603.0 ], [ 956.0, 708.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc572b0-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cba658-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba680-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 367.0, 935.0 ], [ 357.0, 920.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7fbc6-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba6da-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba680-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 396.0, 893.0 ], [ 357.0, 920.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "90bfe46c-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba680-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba57c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 357.0, 920.0 ], [ 340.0, 932.0 ], [ 334.0, 967.0 ], [ 343.0, 985.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7e276-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cba75c-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba6da-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 340.0, 807.0 ], [ 396.0, 893.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "918079fc-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske_solid", "snode_type": 250.0, "enode_type": null, "snodeid": "90cba324-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba57c-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 270.0, 1191.0 ], [ 269.0, 1188.0 ], [ 278.0, 1153.0 ], [ 305.0, 1135.0 ], [ 343.0, 985.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cc693ca-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cbb620-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb652-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1145.0, 385.0 ], [ 1138.0, 370.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "917f7e30-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske_solid", "snode_type": 1001.0, "enode_type": null, "snodeid": "90cba996-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cba72a-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 581.0, 875.0 ], [ 569.0, 852.0 ], [ 546.0, 835.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7674c-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb1a2-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb3dc-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1101.0, 436.0 ], [ 1187.0, 556.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "918025ce-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb3dc-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb404-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1187.0, 556.0 ], [ 1290.0, 495.0 ], [ 1335.0, 549.0 ], [ 1333.0, 551.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "918089e2-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb3dc-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb0f8-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 1187.0, 556.0 ], [ 1092.0, 623.0 ], [ 1078.0, 652.0 ], [ 1043.0, 654.0 ], [ 956.0, 708.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "91803c76-3e8e-11ef-a98f-a13c99648c9a", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbb04e-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbb0f8-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 975.0, 767.0 ], [ 973.0, 761.0 ], [ 959.0, 754.0 ], [ 944.0, 722.0 ], [ 956.0, 708.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7a2b6-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbad1a-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbacf2-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 862.0, 607.0 ], [ 785.0, 658.0 ], [ 769.0, 687.0 ] ] } },
{ "type": "Feature", "properties": { "linkid": "7cb7bada-3e8e-11ef-bae9-7993d366a9e1", "kind": "ske", "snode_type": null, "enode_type": null, "snodeid": "90cbacf2-3e8e-11ef-a98f-a13c99648c9a", "enodeid": "90cbac48-3e8e-11ef-a98f-a13c99648c9a" }, "geometry": { "type": "LineString", "coordinates": [ [ 769.0, 687.0 ], [ 801.0, 719.0 ], [ 861.0, 841.0 ] ] } }
]
}
#自建link and node
import numpy as np
import cv2
from skimage.morphology import skeletonize
import torch
import torch.optim as optim
import torch.nn.functional as F
import geopandas as gpd 
import shapely.geometry as G 
from scipy.interpolate import interp1d


def read_binary_image(path):
    """读取二值图像"""
    img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
    _, binary_img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
    return binary_img 

#向量V单位化
def normed(v):
    return v/np.maximum(np.linalg.norm(v, axis=-1, keepdims=True), 1e-9)

#插值
def resample(xy, interval = 8.0, tol = 0.1):
    xy = np.asarray(G.LineString(xy).simplify(tol, True).coords, np.double)
    norms = np.linalg.norm(xy[1:] - xy[:-1], axis=1)
    offs = np.empty(len(xy), xy.dtype)
    offs[0] = 0
    offs[1:] = np.add.accumulate(norms)
    n = max(int(np.ceil(offs[-1]/interval)), 1)
    kwargs = dict(copy=False, fill_value='extrapolate', assume_sorted=True)
    xy = interp1d(offs, xy, axis=0, **kwargs)(
      np.linspace(0, offs[-1], n + 1, dtype=xy.dtype))
    return xy



def generate_distance_transform(road_mask):
    """生成距离变换图(反映每个像素到最近非路面像素的距离);
    为了保证修型后的线段不压盖边界,设计损失函数,当其里边界越近,损失越大;压盖边界时,损失极大"""
    grid = cv2.distanceTransform(255 - road_mask, cv2.DIST_L2, 5)
    # cv2.imwrite("grid.jpg",grid)

    grid2 = cv2.distanceTransform(road_mask, cv2.DIST_L2, 5)
    # cv2.imwrite("grid2.jpg",grid2)

    grid = np.where(grid2 > 0, -grid2, grid)
    grid = np.round(np.sqrt(np.tanh(grid/3) + 1)*45000).astype(np.int32)
    # cv2.imwrite("tmp2.jpg",grid.astype(np.uint8))
    # print(grid)
    # print(np.max(grid),np.min(grid))
    return grid

def smooth_skeleton(nodes, links_dict, nodes_dict, dist_transform, fi, follow,lambda1=1.0, lambda2=1.0, lambda3=1.0, lambda4=1.0, lambda5=1.0, lambda6=1.0, max_iterations=50):
    """使用 PyTorch 优化骨架节点,使骨架尽量平滑且不与非路面相交"""
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    nodes_tensor = torch.tensor(nodes, dtype=torch.float32, requires_grad=True, device=device)
    dist_transform_tensor = torch.tensor(dist_transform, dtype=torch.float32, device=device).unsqueeze(0).unsqueeze(0)
    print(nodes_tensor.shape)
    optimizer = optim.Adam([nodes_tensor], lr=0.03)
    H, W = dist_transform.shape

    #同一link相邻两个点
    links_cons1 = [] 
    for d in links_dict.values(): 
        (b, e) = d['rng']
        links_cons1.extend([(fi(i), fi(i + 1))
                         for i in range(b, e - 1)])
    links_cons1 = np.array(links_cons1) 
    initial_diffs = nodes[links_cons1[:, 0]] - nodes[links_cons1[:, 1]]
    initial_distances = np.linalg.norm(initial_diffs, axis=1)
    initial_distances = np.maximum(initial_distances, 1)

    #同一link相邻三个点
    links_cons2 = [] 
    for d in links_dict.values(): 
        (b, e) = d['rng']
        if e-b <= 2:
            continue
        links_cons2.extend([(fi(i), fi(i + 1), fi(i + 2))
                         for i in range(b, e - 2)])
    links_cons2 = np.array(links_cons2) 

    #固定点 
    nods_cons1 = [] 
    for d in links_dict.values(): 
        mode = d["mode"] 
        (b, e) = d['rng'] 
        if mode < 2 : 
            nods_cons1.append([fi(b)]) 
    nods_cons1 = np.array(nods_cons1) 
    initial_solid = nodes[nods_cons1[:, 0]]  

    #固定点射出方向
    vecs_cons1 = [] 
    for d in links_dict.values(): 
        mode = d["mode"] 
        (b, e) = d['rng'] 
        if mode == 1: 
            vecs_cons1.append([fi(b), fi(b+1)]) 
    vecs_cons1 = np.array(vecs_cons1) 
    initial_vecs1 = nodes[vecs_cons1[:, 0]] - nodes[vecs_cons1[:, 1]] 

    #多link挂接时,角度均匀
    vecs_cons2 = [] 
    #多link挂接时,近似180度的两个射出角保持直线
    vecs_cons3 = [] 
    for d in nodes_dict.values(): 
        node_couple_list = d["vecs"] 
        if len(node_couple_list) >= 2: 
            for idx in range(len(node_couple_list)):
                (s,e) = node_couple_list[idx] 
                if len(node_couple_list) == idx+1:
                    (ss,ee) = node_couple_list[0] 
                else:
                    (ss,ee) = node_couple_list[idx+1] 
                vecs_cons2.append([fi(s), fi(e), fi(ss), fi(ee)]) 
                if abs(np.arctan2(np.cross(nodes[fi(s)] - nodes[fi(e)] , nodes[fi(ss)] - nodes[fi(ee)]), (nodes[fi(s)] - nodes[fi(e)] )@(nodes[fi(ss)] - nodes[fi(ee)])))>2.8:
                    vecs_cons3.append([fi(e), fi(s), fi(ee)])
    vecs_cons2 = np.array(vecs_cons2) 
    vecs_cons3 = np.array(vecs_cons3) 
    links_cons2 = np.concatenate([links_cons2,vecs_cons3],axis=0)

    
    for _ in range(max_iterations):
        optimizer.zero_grad()

        # 平滑项
        # smoothness_term = torch.sum(torch.norm(nodes_tensor[links[:, 0]] - nodes_tensor[links[:, 1]], dim=1))
        # 保持与初始距离的比例,且最小长度为1个像素
        length__start_points = nodes_tensor[links_cons1[:, 0]]
        length__end_points = nodes_tensor[links_cons1[:, 1]]
        length__distances = torch.norm(length__start_points - length__end_points, dim=1)
        smoothness_term = torch.sum((length__distances - torch.tensor(initial_distances, device=device))**2)  
        
        # 平滑项,角度均匀
        first_points = nodes_tensor[links_cons2[:, 0]]
        second_points = nodes_tensor[links_cons2[:, 1]]
        third_points = nodes_tensor[links_cons2[:, 2]]
        smoothness_term2 = (1 - F.cosine_similarity((second_points-first_points).unsqueeze(0), (third_points-second_points).unsqueeze(0), dim=1)).sum()
        # print("debug",smoothness_term2)

        #固定点
        solid_points = nodes_tensor[nods_cons1[:, 0]]
        solid_term = torch.sum(torch.norm(solid_points - torch.tensor(initial_solid, device=device), dim=1))

        #固定射出角度
        solid_first_points = nodes_tensor[vecs_cons1[:, 0]]
        solid_second_points = nodes_tensor[vecs_cons1[:, 1]]
        solid_vecs_term = (1 - F.cosine_similarity((solid_first_points-solid_second_points).unsqueeze(0), torch.tensor(initial_vecs1, device=device).unsqueeze(0), dim=1)).sum()

        # 距离项
        # 将节点坐标转换为插值中的标准化坐标并取样
        grid = nodes_tensor.unsqueeze(0).unsqueeze(0)  # [1, 1, N, 2]
        grid = torch.cat((grid[:, :, :, 0:1] * 2 / (W - 1) - 1, grid[:, :, :, 1:2] * 2 / (H - 1) - 1), dim=3)
        sampled_dist = F.grid_sample(dist_transform_tensor, grid, mode='bilinear', padding_mode='border', align_corners=True)
        distance_term = sampled_dist.view(-1).sum() 

        #多度点均匀挂接 
        one_vec = nodes_tensor[vecs_cons2[:, 0]] - nodes_tensor[vecs_cons2[:, 1]] 
        the_other_vec = nodes_tensor[vecs_cons2[:, 2]] - nodes_tensor[vecs_cons2[:, 3]] 
        # 计算点积 
        dot_products = torch.sum(one_vec * the_other_vec, dim=1)
        cross_products = one_vec[:, 0] * the_other_vec[:, 1] - one_vec[:, 1] * the_other_vec[:, 0]
        # 计算夹角 
        nodes_angles = torch.atan2(cross_products, dot_products)
        # 将结果张量的形状调整为 (100, 1)
        nodes_angles = nodes_angles.view(-1, 1) 
        nodes_vecs_term = torch.sum(torch.max(- nodes_angles + torch.full_like(nodes_angles, torch.tensor(torch.pi / 2)),torch.full_like(nodes_angles, torch.tensor(0))))

        # #近似180度挂接的保持直线
        # vecs_cons3
        
        
        loss = (lambda1 * smoothness_term +  #保持原有长度损失
               lambda2 * distance_term +  #grid损失
               lambda3 * smoothness_term2 +  #角度平滑损失
               lambda4 * solid_term +  #固定点不动约束
               lambda5 * solid_vecs_term +#固定点射出角约束
               lambda6 * nodes_vecs_term #多度挂接角度均匀 
               )
        loss.backward() 
        
        optimizer.step()
        print(loss)

    optimized_coords = nodes_tensor.cpu().detach().numpy()

    for (i, j) in follow.items():
        optimized_coords[i] = optimized_coords[j]
    return optimized_coords

def visualize_skeleton(binary_img, nodes, links, optimized_nodes,fi):
    """可视化优化前后的骨架"""
    img = cv2.cvtColor(binary_img, cv2.COLOR_GRAY2BGR) 

    view_links = []
    for d in links.values(): 
        (b, e) = d['rng']
        view_links.extend([(fi(i), fi(i + 1)) 
                         for i in range(b, e - 1)]) 
    links = np.array(view_links) 
    # 绘制原始骨架(红色)
    for x, y in nodes: 
        cv2.circle(img, (x, y), 2, (0, 0, 255), -1)
    for i, j in links: 
        cv2.line(img, tuple(nodes[i]), tuple(nodes[j]), (0, 0, 255), 1)

    # # 绘制优化后的骨架(蓝色)
    # for x, y in optimized_nodes:
    #     cv2.circle(img, (int(x), int(y)), 2, (255, 0, 0), -1)
    # for i, j in links:
    #     cv2.line(img, tuple(optimized_nodes[i].astype(int)), tuple(optimized_nodes[j].astype(int)), (255, 0, 0), 1)

    cv2.imwrite("res0.jpg",img)

def out_gpd(links_gpdf,nodes_gpdf,links,pts,nodes):
    links_gpdf = links_gpdf.set_geometry([
          G.LineString(pts[b:e]).simplify(0.5, True)
          for (b, e) in [links[k]['rng'] for k in links_gpdf['linkid']]])
    nodes_gpdf = nodes_gpdf.set_geometry([
          G.Point(pts[nodes[k]['vecs'][0][0]])
          for k in nodes_gpdf['nodeid']])
    gpd.GeoDataFrame(links_gpdf).to_file('./link2_out.geojson')
    gpd.GeoDataFrame(nodes_gpdf).to_file('./node2_out.geojson')
    return 

def main():
    """主函数"""
    # 读取二值图像
    binary_img = read_binary_image('./1.jpg')
    link_path = "./link.geojson"
    node_path = "./node.geojson"
    
    link_gpd = gpd.read_file(link_path) 
    node_gpd = gpd.read_file(node_path) 
    
    # 提取节点和连边
    # 初始化节点字典,保存节点及其连出的向量信息
    nodes = {}
    for r in node_gpd.itertuples():
        fixed = (not np.isnan(r.node_type))
        nodes[r.nodeid] = {'vecs': []}
    # 初始化点集列表和线段字典
    pts = []
    e = 0 #记录pts的idx
    links = {} 
    
    for r in link_gpd.itertuples(): 
        #按照固定点类别区分模式
        mode = (0 if (r.snode_type in [250]) else
                1 if (r.snode_type in [1001, 1002, 1003]) else 2)
        xy = np.array(r.geometry.coords, np.double)
        if mode == 1:
            if len(xy) == 2:
                #如果固定点挂接的link是两个形点,那么添加一个靠近重点的四等分点,组成三个形点的link。
                xy = np.array([xy[0], (xy[0] + xy[1]*3)/4, xy[1]])
                #在插值的时候保留第一个点
            xy = np.concatenate([xy[:1], resample(xy[1:])])
        else:
            xy = resample(xy)
        pts.append(xy)
        b = e
        e += len(xy) 
        #当前link对应的pts点序从b到e-1
        links[r.linkid] = {'rng': (b, e), 'mode': mode}
        nodes[r.snodeid]['vecs'].append((b + 0, b + 1))
        nodes[r.enodeid]['vecs'].append((e - 1, e - 2))

    #三维展开成二维,即将所有的line坐标平铺,取消line维度  
    pts = np.concatenate(pts)
    # 用于记录同一个点的信息,对于每个节点,如果它连出多条边,将这些边的起点合并成一个。
    follow = {} 
    for n in nodes.values():
        l = n['vecs']
        if len(l) > 2:
            #计算node节点连的所有边的角度,并按照顺/逆时针调整顺序
            v = np.array([pts[i2] - pts[i1] for (i1, i2) in l]).T
            l[:] = [l[i] for i in np.argsort(np.arctan2(v[1], v[0]))]
        #follow 字典,代表同一个点
        if len(l) > 1:
            for (i, _) in l[1:]:
                follow[i] = l[0][0]
    def fi(i): 
        return follow.get(i, i)
    print(pts.shape)
    print(links["7cba8b52-3e8e-11ef-bae9-7993d366a9e1"])
    
    # 生成距离变换图
    dist_transform = generate_distance_transform(binary_img)


# lambda1  #保持原有长度损失
# lambda2  #grid损失
# lambda3  #角度平滑损失
# lambda4 #固定点不动约束
# lambda5 #固定点射出角约束
# lambda6  #多度挂接角度均匀 
    
    # 优化整个骨架
    optimized_nodes = smooth_skeleton(pts, links, nodes, dist_transform, fi, follow, lambda1=1, lambda2=0.01, lambda3 = 2000000, lambda4 = 10000000,lambda5 = 10000000,lambda6=1400000, max_iterations=5000)

    # 可视化优化前后的骨架 
    visualize_skeleton(binary_img, pts.astype(np.int32), links, optimized_nodes,fi)

    # out gpd
    out_gpd(link_gpd,node_gpd,links,optimized_nodes,nodes)

if __name__ == '__main__': 
    main() 

优化后的结果见(蓝色)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1938716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科普文:百度交易中台之系统对账篇

百度交易中台作为集团移动生态战略的基础设施&#xff0c;面向收银交易与清分结算场景&#xff0c;赋能业务、提供高效交易生态搭建。目前支持百度体系内多个产品线&#xff0c;主要包括&#xff1a;度小店、小程序、地图打车、文心一言等。本文主要介绍了百度交易中台的交易链…

如何让主机显示Docker容器的程序界面,同时支持声音播放

系统中如果安装各种应用软件&#xff0c;很容易会因为版本冲刺引发异常。一个好的办法就是用容器来隔离系统环境&#xff0c;确保主机环境不变。对于一些有界面的程序&#xff0c;可以在容器内运行&#xff0c;让其界面显示在主机上。下面以安装和使用视频剪辑软件shotcut为例&…

【一刷《剑指Offer》】面试题 42:翻转单词顺序 VS 左旋转字符串

力扣对应题目链接&#xff1a;151. 反转字符串中的单词 - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;翻转单词序列_牛客题霸_牛客网 (nowcoder.com) 核心考点 &#xff1a;子串划分&#xff0c;子串逆置。 一、题目一 1、《剑指Offer》对应内容 2、…

Delphi5实现加密程序

效果图 平面效果图 实现“确认按钮”和“加密” //点击确认输入按钮 procedure TForm1.btn1Click(Sender: TObject); //加密部分 varpasswd_2,passwd_3:string;beginpasswd_2:edt1.Text;Delete(passwd_2,3,2);passwd_3:mima;Delete(passwd_3,3,2);if(passwd_2passwd_3) thenM…

MAE(论文阅读):Masked Autoencoders are scalable vision learners

Masked Autoencoders Are Scalable Vision Learners 研究问题&#xff1a; 本文主要介绍了掩码自编码器( MAE, Masked autoencoders)是视觉领域中可扩展的自监督学习算法。MAE具体操作为随机屏蔽输入image中的patchs&#xff0c;再重建丢失的像素。其基于两个核心操作。第…

HTML5大作业三农有机,农产品,农庄,农旅网站源码

文章目录 1.设计来源1.1 轮播图页面头部效果1.2 栏目列表页面效果1.3 页面底部导航效果 2.效果和源码2.1 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_4…

浅谈Canal原理

canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据 订阅 和 消费。应该是阿里云DTS&#xff08;Data Transfer Service&#xff09;的开源版本。 Canal与DTS提供的功能基本相似&#xff1a; 基于…

python Requests库7种主要方法及13个控制参数(实例实验)

文章目录 一、Requests库的7种主要方法二、kwargs:控制访问的13个参数 一、Requests库的7种主要方法 序号方法说明1requests.request()&#xff1a;提交一个request请求&#xff0c;作为其他请求的基础2requests.get()&#xff1a;获取HTML网页代码的方法3requests.head()&…

内网隧道——隧道技术基础

文章目录 一、正向连接与反向连接1.1 正向连接1.2 反向连接 二、端口转发三、端口映射四、端口复用五、代理和隧道的区别六、常见隧道穿透分类 环境&#xff1a; kali&#xff1a;192.168.92.6&#xff0c;MSF v6.3.25 win7&#xff1a;192.168.92.7 一、正向连接与反向连接 1…

python实现误差扩散、Floyd-Steinberg 抖动、有序抖动、Riemersma 抖动算法

误差扩散、Floyd-Steinberg 抖动、有序抖动、Riemersma 抖动算法 1.误差扩散算法详解算法步骤Floyd-Steinberg 算法公式Python 实现详细解释优缺点 2.有序抖动算法详解算法步骤Bayer矩阵公式Python 实现详细解释优缺点 3.Riemersma 抖动算法详解算法步骤公式Python 实现详细解释…

网络编程中的TCP和UDP

什么是TCP协议 TCP( Transmission control protocol )即传输控制协议&#xff0c;是一种面向连接、可靠的数据传输协议&#xff0c;它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。 面向连接 &#xff1a;数据传输之前客户端和服务器端必须建立连…

人工智能与语音识别:技术进步与应用前景

引言 人工智能&#xff08;AI&#xff09;作为当今科技进步的核心驱动力&#xff0c;正在各个领域展现其变革力量。其中&#xff0c;语音识别技术作为人工智能的重要应用之一&#xff0c;已经深入到我们的日常生活和工作中。从智能助手如Siri、Google Assistant&#xff0c;到智…

最新版kubeadm搭建k8s(已成功搭建)

kubeadm搭建k8s&#xff08;已成功搭建&#xff09; 环境配置 主节点 k8s-master&#xff1a;4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10.16.64.67&#xff09; 从节点 k8s-node1&#xff1a; 4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10…

n5.Nginx 常见模块

Nginx 常见模块 4.1 Nginx 四层访问控制4.2 Nginx 账户认证功能4.3 自定义错误页面4.4 自定义错误日志4.5 检测文件是否存在4.6 长连接配置4.7 作为下载服务器配置4.8 作为上传服务器4.9 限流限速4.10 Nginx 状态页 4.1 Nginx 四层访问控制 访问控制基于模块ngx_http_access_m…

【一刷《剑指Offer》】面试题 37:两个链表的第一个公共结点

力扣对应题目链接&#xff1a;160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;两个链表的第一个公共结点_牛客题霸_牛客网 (nowcoder.com) 核心考点 &#xff1a;单链表理解&#xff0c;临界条件判定。 一、《剑指Offer》对应内容 二、分…

Python酷库之旅-第三方库Pandas(036)

目录 一、用法精讲 111、pandas.Series.item方法 111-1、语法 111-2、参数 111-3、功能 111-4、返回值 111-5、说明 111-6、用法 111-6-1、数据准备 111-6-2、代码示例 111-6-3、结果输出 112、pandas.Series.xs方法 112-1、语法 112-2、参数 112-3、功能 112-…

快速了解死锁及如何解决死锁问题

目录 什么是死锁&#xff1f; 死锁代码示例 产生死锁的条件&#xff1a; 死锁的危害&#xff1a; 如何解决死锁问题&#xff1f; 1、预防死锁&#xff08;破坏上述4个产生死锁的条件&#xff09;&#xff1a; 2、银行家算法 3、死锁的检测、解除 4、采用超时机制 什么…

C语言 ——— 写一个函数,调整 整型数组 中 奇数偶数的顺序

目录 题目要求 代码实现 题目要求 创建一个整型数组 自定义函数实现&#xff1a;调整该数组中数字的顺序&#xff0c;使得数组中所有的奇数位于数组的前半部分&#xff0c;数组中所有的偶数位于数组的后半部分 举例&#xff1a; 输入的整型数组为&#xff1a;[234,24,45,…

计算机网络知识点面试总结3

#来自ウルトラマンゼロ&#xff08;赛罗&#xff09; 1 数据链路层功能 数据链路层在物理层提供的服务的基础上向网络层提供服务&#xff0c;其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层&#xff0c;其主要作用是加强物理层传输原始比特流的功能。…

嵌入式面试总结

C语言中struct和union的区别 struct和union都是常见的复合结构。 结构体和联合体虽然都是由多个不同的数据类型成员组成的&#xff0c;但不同之处在于联合体中所有成员共用一块地址空间&#xff0c;即联合体只存放了一个被选中的成员&#xff0c;结构体中所有成员占用空间是累…