Сопоставление изображений | Нормализованная кросс-корреляционная потеря NCC | Код + объяснение

искусственный интеллект
  • Статья воспроизведена из публичного аккаунта WeChat «Алхимия машинного обучения».
  • Автор: Alchemy Brother (авторизованный)
  • Контактная информация автора: WeChat cyx645016617 (Добро пожаловать, чтобы общаться и добиваться прогресса вместе)

На этот раз содержание в основном объясняет NCCNormalized cross-correlationНормализованная взаимная корреляция.

Независимо от того, являются ли две картинки одним и тем же содержанием, текущее решение для глубокого обучения, естественно, должно использовать нейронную сеть, например: архитектура двойной сети для распознавания лиц и т. д.;

В обычных непараметрических методах также существуют общие коэффициенты корреляции.При изучении воксельморфной модели в предыдущей статье я обнаружил, что в задаче регистрации медицинских изображений (не ограничиваясь медициной) есть метрика под названием NCC для измерения сходства двух изображений.

И этот NCC является нормализованным коэффициентом взаимной корреляции Normalized Cross-Correlation.

1 Коэффициент взаимной корреляции

Если вы знаете коэффициент взаимной корреляции, то вы хорошо понимаете нормализованный коэффициент взаимной корреляции.

Формула для расчета коэффициента корреляции выглядит следующим образом:r(X,Y)=Cov(X,Y)Var(X)Var(Y)r(X,Y) = \frac{Cov(X,Y)}{\sqrt{Var(X)Var(Y)}}

X и Y в формуле представляют две картинки соответственно,Cov(X,Y)Cov(X,Y)представляет ковариацию двух изображений,Var(X)Var(X)представляет дисперсию самого X;

2 Нормализованная кросс-корреляция NCC

Если картинку скользить по определенному пикселю, например, кадр 9х9, то картинку можно разделить на множество маленьких картинок 9х9, тогда НСС - это соотношение между соответствующими маленькими картинками в двух больших картинках Х и Y. среднее число.

Вот посмотрите, как рассчитывается ковариация:Cov(X,Y)=E[(XE(X))(YE(Y))]Cov(X,Y) = E[(X-E(X))(Y-E(Y))]

Дисперсия рассчитывается как:Var(X)=E[(XE(X))2]Var(X) = E[(X-E(X))^2]

На самом деле NCC понять несложно, но как его вычислить с помощью кода? Конечно, мы можем проходить решение построчно, но временная сложность слишком высока, поэтому мы все равно выбираем матричные операции, если делаем это хорошо.

3 Код для функции потерь NCC

class NCC:
    """
    Local (over window) normalized cross correlation loss.
    """

    def __init__(self, win=None):
        self.win = win

    def loss(self, y_true, y_pred):

        I = y_true
        J = y_pred

        # get dimension of volume
        # assumes I, J are sized [batch_size, *vol_shape, nb_feats]
        ndims = len(list(I.size())) - 2
        assert ndims in [1, 2, 3], "volumes should be 1 to 3 dimensions. found: %d" % ndims

        # set window size
        win = [9] * ndims if self.win is None else self.win

        # compute filters
        sum_filt = torch.ones([1, 1, *win]).to("cuda")

        pad_no = math.floor(win[0]/2)

        if ndims == 1:
            stride = (1)
            padding = (pad_no)
        elif ndims == 2:
            stride = (1,1)
            padding = (pad_no, pad_no)
        else:
            stride = (1,1,1)
            padding = (pad_no, pad_no, pad_no)

        # get convolution function
        conv_fn = getattr(F, 'conv%dd' % ndims)

        # compute CC squares
        I2 = I * I
        J2 = J * J
        IJ = I * J

        I_sum = conv_fn(I, sum_filt, stride=stride, padding=padding)
        J_sum = conv_fn(J, sum_filt, stride=stride, padding=padding)
        I2_sum = conv_fn(I2, sum_filt, stride=stride, padding=padding)
        J2_sum = conv_fn(J2, sum_filt, stride=stride, padding=padding)
        IJ_sum = conv_fn(IJ, sum_filt, stride=stride, padding=padding)

        win_size = np.prod(win)
        u_I = I_sum / win_size
        u_J = J_sum / win_size

        cross = IJ_sum - u_J * I_sum - u_I * J_sum + u_I * u_J * win_size
        I_var = I2_sum - 2 * u_I * I_sum + u_I * u_I * win_size
        J_var = J2_sum - 2 * u_J * J_sum + u_J * u_J * win_size

        cc = cross * cross / (I_var * J_var + 1e-5)

        return -torch.mean(cc)

Этот код на самом деле не очень прост для понимания, мне потребовалось много времени, чтобы разобраться. Тут главное понять:

# compute CC squares
        I2 = I * I
        J2 = J * J
        IJ = I * J

        I_sum = conv_fn(I, sum_filt, stride=stride, padding=padding)
        J_sum = conv_fn(J, sum_filt, stride=stride, padding=padding)
        I2_sum = conv_fn(I2, sum_filt, stride=stride, padding=padding)
        J2_sum = conv_fn(J2, sum_filt, stride=stride, padding=padding)
        IJ_sum = conv_fn(IJ, sum_filt, stride=stride, padding=padding)

        win_size = np.prod(win)
        u_I = I_sum / win_size
        u_J = J_sum / win_size

        cross = IJ_sum - u_J * I_sum - u_I * J_sum + u_I * u_J * win_size
        I_var = I2_sum - 2 * u_I * I_sum + u_I * u_I * win_size
        J_var = J2_sum - 2 * u_J * J_sum + u_J * u_J * win_size

Мы можем только прийти, этот крест должен быть ковариационной частью, а I_var и J_var — дисперсионной частью.

Выводим формулу ковариации:Cov(X,Y)=E[(XE(X))(YE(Y))]Cov(X,Y) = E[(X-E(X))(Y-E(Y))] =E[XYXE(Y)YE(X)+E(X)E(Y)]=E[XY-XE(Y)-YE(X)+E(X)E(Y)]

Это как раз соответствует кресту.

  • IJ_sum = E[XY]
  • u_J * I_sum = E[XE(Y)]
  • u_I * u_J * win_size = E[E(X)E(Y)]

Выведите формулу дисперсии:Var(X)=E[(XE(X))2]=E[X22XE(X)+E(X)2]Var(X) = E[(X-E(X))^2]=E[X^2-2XE(X)+E(X)^2]

  • J2_sum = E(X^2)
  • 2 * u_J * J_sum = E[2XE(X)]
  • u_J * u_J * win_size = E[E(X)^2]