簡體   English   中英

哪個損失 function 計算兩個輪廓之間的距離

[英]Which loss function calculates the distance between two contours

在我的輪廓生成網絡中,我使用nn.L1Loss()來計算有多少像素是錯誤的。 這適用於訓練,但真實輪廓和假輪廓之間的 2D 距離會更好。 我的目標是之后測量生成輪廓的長度。 這個兩個二進制圖像的代碼示例顯示了nn.L1Loss()失敗的地方。

import cv2
import torch
from torch import nn

p1 = [(15, 15),(45,45)]
p2 = [(16, 15),(46,45)]

real = cv2.rectangle(np.ones((60,60)), p1[0], p1[1], color=0, thickness=1)
fake = cv2.rectangle(np.ones((60,60)), p2[0], p2[1], color=0, thickness=1)

cv2.imshow('image',np.hstack((real,fake)))
cv2.waitKey(0)

real = torch.tensor(real)
fake = torch.tensor(fake)

losss = [nn.L1Loss(), nn.MSELoss(), nn.BCELoss(), nn.HingeEmbeddingLoss(), nn.SmoothL1Loss()]
print(my_loss(real, fake))

for k, loss in enumerate(losss):
    err = loss(real, fake)
    print(err*60)

如果我將矩形向右移動 1 個像素:

-> L1 損失為 0.0333 * 60 = 2

如果我將矩形向右移動 1 個像素,向左移動 1 個像素:

-> L1 損失為 0.0656 * 60 = 3.933

如果我將矩形向右移動 10 像素,向左移動 10 像素:

-> L1 損失為 0.0656 * 60 = 3.933
還是一樣,這不足為奇。 錯誤像素的數量是相同的。 但是到它們的距離改變了 10 * 2**1/2。

我還考慮了兩個中心之間的距離:

    M = cv2.moments(c)
    cX = int(M['m10'] /M['m00'])
    cY = int(M['m01'] /M['m00'])
    centers.append([cX,cY])

這里的問題是生成的輪廓與真實的輪廓不同,因此具有不同的中心。

這個答案接近我正在尋找的,但是計算非常昂貴?!

https://stackoverflow.com/a/36505073/12337147

是否有自定義損失函數來確定我描述的距離?

這是你想要的方程嗎

方程

與下一個方程給出的曲線之間的面積相反,如果輪廓彼此足夠相似

L1區

這意味着從一個輪廓中的點到另一個輪廓中最近點的累積平方距離?

成對最小距離點

給定兩個 arrays 和輪廓上的點,我可以直接在 GPU 上計算復雜度 O(M * N),其中 C1 有 M 個點,C2 有 N 個點。 或者,它可以在 O(W * H) 中計算,其中 W * H 是圖像的維度。

如果這正是您想要的,我可以發布解決方案。

解決方案

首先讓我們創建一些示例數據。

import torch
import math
from torch import nn
import matplotlib.pyplot as plt;

# Number of points in each contour
M, N = 1000, 1500
t1 = torch.linspace(0, 2*math.pi, M).view(1, -1)
t2 = torch.linspace(0, 2*math.pi, N).view(1, -1)

c1 = torch.stack([torch.sin(t1),torch.cos(t1)], dim=2) # (1 x M x 2)
c2 = 1 - 2* torch.sigmoid(torch.stack([torch.sin(t2)*3 + 1, torch.cos(t2)*3 + 2], dim=2)) # (1 x N x 2)

有了這個,我們可以使用torch.cdist計算每對點之間的距離。 這里我使用了torch.argmin來查找數組中每一列的最小值的position。 為了計算損失 function 重要的是距離本身,可以使用torch.amin計算。

distances = torch.cdist(c1, c2); # (1 x M x N)
plt.imshow(distances[0]);
plt.xlabel('index in countor 1');
plt.ylabel('index in countor 2');
plt.plot(torch.argmin(distances[0], axis=0), '.r')

最小距離點指數

不過現在基本上,你積累的不是距離,而是距離的一個function。 這可以通過torch.min(f(distances))輕松獲得,並且假設f(.)是單調的可以簡化為f(torch.min(distances))

為了近似積分,我們可以使用梯形規則,它集成了采樣 function 的線性插值,在我們的例子中是在您給出的點處采樣的輪廓。

這給你一個損失 function

def contour_divergence(c1, c2, func = lambda x: x**2):
    c1 = torch.atleast_3d(c1);
    c2 = torch.atleast_3d(c2);
    f = func(torch.amin(torch.cdist(c1, c2), dim=2));
    # this computes the length of each segment connecting two consecutive points
    df = torch.sum((c1[:, 1:, :] - c1[:, :-1, :])**2, axis=2)**0.5;
    # here is the trapesoid rule
    return torch.sum((f[:, :-1] + f[:, 1:]) * df[:, :], axis=1) / 4.0;
def contour_dist(c1, c2, func = lambda x: x**2):
    return contour_divergence(c1, c2, func) + contour_divergence(c2, c1, func)

對於連接最近點的線始終垂直於軌跡的情況contour_dist(c1, c2, lambda x: x)給出面積。

這將給出半徑為 1 的圓的面積(第二個圓的所有點都在原點上)。

print(contour_dist(c1, c1*0, lambda x: x) / math.pi) # should print 1

現在考慮半徑為 1 的圓和半徑為 1 的圓之間的距離(它將是 pi * (1 - 1/4) = 0.75*pi)

print(contour_dist(c1, c1*0.5, lambda x: x)  / math.pi) # should print 0.75

如果您想要累積平方距離的任何損失,您只需使用contour_dist(c1, c2) ,您可以將任意 function 作為參數傳遞給 function。 只要您可以反向傳播傳遞的函數,您就可以反向傳播損失。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM