Вычисление среднеквадратичной ошибки с помощью матричной арифметики на матрицах двоичных изображений - PullRequest
0 голосов
/ 31 октября 2018

У меня есть 2 бинарных изображения, одно - основополагающая истина, а другое - сегментация изображений, которую я создал.

Я пытаюсь вычислить среднеквадратичное расстояние ...

enter image description here

Let G = {g1, g2, . . . , gN} be the points in the ground truth boundary.
Let B = {b1, b2, . . . , bM} be the points in the segmented boundary.
Define d(p, p0) be a measure of distance between points p and p0 (e.g. Euclidean, city block, etc.)

между двумя изображениями с использованием следующего алгоритма.

def MSD(A,G):
    '''
    Takes a thresholded binary image, and a ground truth img(binary), and computes the mean squared absolute difference
    :param A: The thresholded binary image
    :param G: The ground truth img
    :return:
    '''
    sim = np.bitwise_xor(A,G)
    sum = 0
    for i in range(0,sim.shape[0]):
        for j in range(0,sim.shape[1]):
            if (sim[i,j] == True):
                min = 9999999
                for k in range(0,sim.shape[0]):
                    for l in range(0,sim.shape[1]):
                        if (sim[k, l] == True):
                            e = abs(i-k) + abs(j-l)
                            if e < min:
                                min = e
                                mink = k
                                minl = l
                sum += min
    return sum/(sim.shape[0]*sim.shape[1])

Этот алгоритм слишком медленный и никогда не завершается.

В этом примере и в этом примере (Ответ 3) может быть показан способ получения среднеквадратичной ошибки с использованием арифметики Matrix, но я не понимаю, как эти примеры имеют какой-либо смысл или почему они работают.

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Эффективный метод расчета этого расстояния - использование Преобразования расстояний. SciPy имеет реализацию в пакете ndimage: scipy.ndimage.morphology.distance_transform_edt .

Идея состоит в том, чтобы вычислить преобразование расстояния для фона изображения "земля-правда" G. Это приводит к созданию нового изображения D, которое равно 0 для каждого пикселя, отличного от нуля в G, и для каждого нулевого пикселя в G будет расстояние до ближайшего ненулевого пикселя.

Далее, для каждого ненулевого пикселя в B (или A в коде, который вы разместили), вы смотрите на соответствующий пиксель в D. Это расстояние до G для этого пикселя. Итак, просто усредните все значения в D, для которых B отлично от нуля, чтобы получить ваш результат.

import numpy as np
import scipy.ndimage as nd
import matplotlib.pyplot as pp

# Create some test data
img = pp.imread('erika.tif')         # a random image
G = img > 120                        # the ground truth
img = img + np.random.normal(0, 20, img.shape)
B = img > 120                        # the other image

D = nd.morphology.distance_transform_edt(~G)
msd = np.mean(D[B]**2)
0 голосов
/ 31 октября 2018

Так что, если я правильно понимаю вашу формулу и код, у вас есть одно (двоичное) изображение B и (основная истина) изображение G. «Точки» определяются позициями пикселей, где любое изображение имеет значение True (или, по крайней мере, ненулевое). Из вашего bitwise_xor я делаю вывод, что оба изображения имеют одинаковую форму (M,N).

Таким образом, величина d^2(b,g) в худшем случае представляет собой массив размером (M*N, M*N), связывающий каждый пиксель B с каждым пикселем G. Еще лучше: нам нужна форма (m,n), только если в B ненулевые m и G ненулевые n. Если ваши изображения не являются огромными, мы можем избежать отслеживания этого большого количества. Это будет стоить памяти, но мы выиграем много процессорного времени за счет векторизации. Таким образом, нам нужно только найти минимум этого расстояния по отношению к каждому n возможному значению для каждого m. Тогда просто суммируйте каждый минимум. Обратите внимание, что приведенное ниже решение использует крайнюю векторизацию, и оно может легко поглотить вашу память, если изображения большие.

Предполагая расстояние до Манхэттена (с квадратом в d^2, который, кажется, отсутствует в вашем коде):

import numpy as np

# generate dummy data
M,N = 100,100
B = np.random.rand(M,N) > 0.5
G = np.random.rand(M,N) > 0.5

def MSD(B, G):
    # get indices of nonzero pixels
    nnz_B = B.nonzero() # (x_inds, y_inds) tuple, x_inds and y_inds are shape (m,)
    nnz_G = G.nonzero() # (x_inds', y_inds') each with shape (n,)

    # np.array(nnz_B) has shape (2,m)
    # compute squared Manhattan distance
    dist2 = abs(np.array(nnz_B)[...,None] - np.array(nnz_G)[:,None,:]).sum(axis=0)**2 # shape (m,n)
    # alternatively: Euclidean for comparison:
    #dist2 = ((np.array(nnz_B)[...,None] - np.array(nnz_G)[:,None,:])**2).sum(axis=0)

    mindist2 = dist2.min(axis=-1) # shape (m,) of minimum square distances

    return mindist2.mean() # sum divided by m, i.e. the MSD itself

print(MSD(B, G))

Если приведенное выше использует слишком много памяти, мы можем ввести цикл над элементами nnz_B и векторизовать только элементы nnz_G. Это займет больше мощности процессора и меньше памяти. Этот компромисс типичен для векторизации.

...