Поиск пересечений скелетированного изображения в python opencv

У меня есть скелетонизированное изображение (показано ниже).

Изображение скелета

Я хотел бы получить пересечения линий. Я пробовал следующий метод ниже, skeleton - это изображение openCV, и алгоритм возвращает список координат:

def getSkeletonIntersection(skeleton):
    image = skeleton.copy();
    image = image/255;
    intersections = list();
    for y in range(1,len(image)-1):
        for x in range(1,len(image[y])-1):
            if image[y][x] == 1:
                neighbourCount = 0;
                neighbours = neighbourCoords(x,y);
                for n in neighbours:
                    if (image[n[1]][n[0]] == 1):
                        neighbourCount += 1;
                if(neighbourCount > 2):
                    print(neighbourCount,x,y);
                    intersections.append((x,y));
    return intersections;

Он находит координаты белых пикселей, где есть более двух соседних пикселей. Я думал, что это вернет только углы, но это не так - он возвращает гораздо больше очков.

Скелет с отмеченными координатами

Это результат с отмеченными на изображении точками, которые он обнаруживает. Это связано с тем, что он обнаруживает некоторые из показанных ниже примеров, которые не являются пересечениями.

0 0 0    1 1 0    0 1 1
1 1 1    0 1 0    1 1 0
0 0 1    0 0 1    0 0 0

И еще много примеров. Есть ли другой метод, на который я должен обратить внимание, чтобы обнаружить пересечения. Все входные данные и идеи приветствуются, спасибо.


person James Paterson    schedule 17.01.2017    source источник
comment
возможно, увеличьте количество до 4 или 6, чтобы отфильтровать точки в этой строке if(neighbourCount > 2):.   -  person v.coder    schedule 17.01.2017


Ответы (4)


Я не уверен в функциях OpenCV, но, возможно, вам следует попробовать использовать морфологию Hit and Miss, которая описана здесь.

Прочтите Line Junctions и посмотрите 12 шаблонов, которые вам нужно протестировать:

введите описание изображения здесь

person Mark Setchell    schedule 17.01.2017
comment
Спасибо за это. Закончилось тестирование из большого списка линейных соединений. - person James Paterson; 19.01.2017
comment
@Mark Setchell, есть ли способ выбрать только перекресток с углом менее 75 градусов. - person ASLAN; 26.01.2021
comment
@ASLAN Думаю, вы могли бы найти все перекрестки, а затем запустить детектор Hough Lines. Для каждого перекрестка определите линии, пересекающиеся там, а затем вычислите угол между ними. Тем не менее, вам лучше задать новый вопрос, так как вопросы (и ответы) бесплатны, и гораздо больше людей увидят и, возможно, ответят на новые вопросы, чем просматривают этот пост 4-летней давности. - person Mark Setchell; 26.01.2021

Недавно я получил электронное письмо с просьбой о возможном решении проблемы. Он размещен ниже, чтобы он мог сообщить другим. Я не утверждаю, что этот код особенно быстр или стабилен — только то, что сработало для меня! Функция также включает фильтрацию дубликатов и пересечений, обнаруженных слишком близко друг к другу, что позволяет предположить, что они не являются реальными пересечениями, а вместо этого добавляет шум от процесса скелетирования.

def neighbours(x,y,image):
    """Return 8-neighbours of image point P1(x,y), in a clockwise order"""
    img = image
    x_1, y_1, x1, y1 = x-1, y-1, x+1, y+1;
    return [ img[x_1][y], img[x_1][y1], img[x][y1], img[x1][y1], img[x1][y], img[x1][y_1], img[x][y_1], img[x_1][y_1] ]   


def getSkeletonIntersection(skeleton):
    """ Given a skeletonised image, it will give the coordinates of the intersections of the skeleton.

    Keyword arguments:
    skeleton -- the skeletonised image to detect the intersections of

    Returns: 
    List of 2-tuples (x,y) containing the intersection coordinates
    """
    # A biiiiiig list of valid intersections             2 3 4
    # These are in the format shown to the right         1 C 5
    #                                                    8 7 6 
    validIntersection = [[0,1,0,1,0,0,1,0],[0,0,1,0,1,0,0,1],[1,0,0,1,0,1,0,0],
                         [0,1,0,0,1,0,1,0],[0,0,1,0,0,1,0,1],[1,0,0,1,0,0,1,0],
                         [0,1,0,0,1,0,0,1],[1,0,1,0,0,1,0,0],[0,1,0,0,0,1,0,1],
                         [0,1,0,1,0,0,0,1],[0,1,0,1,0,1,0,0],[0,0,0,1,0,1,0,1],
                         [1,0,1,0,0,0,1,0],[1,0,1,0,1,0,0,0],[0,0,1,0,1,0,1,0],
                         [1,0,0,0,1,0,1,0],[1,0,0,1,1,1,0,0],[0,0,1,0,0,1,1,1],
                         [1,1,0,0,1,0,0,1],[0,1,1,1,0,0,1,0],[1,0,1,1,0,0,1,0],
                         [1,0,1,0,0,1,1,0],[1,0,1,1,0,1,1,0],[0,1,1,0,1,0,1,1],
                         [1,1,0,1,1,0,1,0],[1,1,0,0,1,0,1,0],[0,1,1,0,1,0,1,0],
                         [0,0,1,0,1,0,1,1],[1,0,0,1,1,0,1,0],[1,0,1,0,1,1,0,1],
                         [1,0,1,0,1,1,0,0],[1,0,1,0,1,0,0,1],[0,1,0,0,1,0,1,1],
                         [0,1,1,0,1,0,0,1],[1,1,0,1,0,0,1,0],[0,1,0,1,1,0,1,0],
                         [0,0,1,0,1,1,0,1],[1,0,1,0,0,1,0,1],[1,0,0,1,0,1,1,0],
                         [1,0,1,1,0,1,0,0]];
    image = skeleton.copy();
    image = image/255;
    intersections = list();
    for x in range(1,len(image)-1):
        for y in range(1,len(image[x])-1):
            # If we have a white pixel
            if image[x][y] == 1:
                neighbours = neighbours(x,y,image);
                valid = True;
                if neighbours in validIntersection:
                    intersections.append((y,x));
    # Filter intersections to make sure we don't count them twice or ones that are very close together
    for point1 in intersections:
        for point2 in intersections:
            if (((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) < 10**2) and (point1 != point2):
                intersections.remove(point2);
    # Remove duplicates
    intersections = list(set(intersections));
    return intersections;

Это также доступно на github здесь.

person James Paterson    schedule 22.07.2017
comment
Вы можете добавить ссылку на то, что такое zs. В противном случае код непригоден для использования. - person Grr; 21.09.2018
comment
Ой! По приглашению другого электронного письма я добавил определение. - person James Paterson; 04.11.2018

Это может помочь, если для данного пикселя вместо подсчета общего количества 8-соседей (= соседей со связностью 8) вы подсчитаете количество 8-соседей, которые не являются 4-соседями друг с другом< /сильный>

Итак, в вашем примере ложных срабатываний

0 0 0    1 1 0    0 1 1
1 1 1    0 1 0    1 1 0
0 0 1    0 0 1    0 0 0

Для каждого случая у вас есть 3 соседа, но каждый раз 2 из них 4-связны. (пиксели с пометкой «2» в следующем фрагменте)

0 0 0    2 2 0    0 2 2
1 1 2    0 1 0    1 1 0
0 0 2    0 0 1    0 0 0

Если вы рассматриваете только один из них для своих подсчетов (вместо их обоих в вашем коде прямо сейчас), у вас действительно есть только 2 недавно определенных «соседи», и рассматриваемые точки не считаются пересечениями. Другие «настоящие пересечения» все равно будут сохранены, например следующие

0 1 0    0 1 0    0 1 0
1 1 1    0 1 0    1 1 0
0 0 0    1 0 1    0 0 1

у которых все еще есть 3 вновь определенных соседа.

Я не проверял ваш образ, работает ли он идеально, но некоторое время назад я реализовал что-то подобное для этой проблемы...

person Soltius    schedule 18.01.2017

Вот мое решение:


    # Functions to generate kernels of curve intersection 

    def generate_nonadjacent_combination(input_list,take_n):
        """ 
        It generates combinations of m taken n at a time where there is no adjacent n.
        INPUT:
            input_list = (iterable) List of elements you want to extract the combination 
            take_n =     (integer) Number of elements that you are going to take at a time in
                         each combination
        OUTPUT:
            all_comb =   (np.array) with all the combinations
        """
        all_comb = []
        for comb in itertools.combinations(input_list, take_n):
            comb = np.array(comb)
            d = np.diff(comb)
            fd = np.diff(np.flip(comb))
            if len(d[d==1]) == 0 and comb[-1] - comb[0] != 7:
                all_comb.append(comb)        
                print(comb)
        return all_comb


    def populate_intersection_kernel(combinations):
        """
        Maps the numbers from 0-7 into the 8 pixels surrounding the center pixel in
        a 9 x 9 matrix clockwisely i.e. up_pixel = 0, right_pixel = 2, etc. And 
        generates a kernel that represents a line intersection, where the center 
        pixel is occupied and 3 or 4 pixels of the border are ocuppied too.
        INPUT:
            combinations = (np.array) matrix where every row is a vector of combinations
        OUTPUT:
            kernels =      (List) list of 9 x 9 kernels/masks. each element is a mask.
        """
        n = len(combinations[0])
        template = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1, -1, -1]), dtype="int")
        match = [(0,1),(0,2),(1,2),(2,2),(2,1),(2,0),(1,0),(0,0)]
        kernels = []
        for n in combinations:
            tmp = np.copy(template)
            for m in n:
                tmp[match[m][0],match[m][1]] = 1
            kernels.append(tmp)
        return kernels


    def give_intersection_kernels():
        """
        Generates all the intersection kernels in a 9x9 matrix.
        INPUT:
            None
        OUTPUT:
            kernels =      (List) list of 9 x 9 kernels/masks. each element is a mask.
        """
        input_list = np.arange(8)
        taken_n = [4,3]
        kernels = []
        for taken in taken_n:
            comb = generate_nonadjacent_combination(input_list,taken)
            tmp_ker = populate_intersection_kernel(comb)
            kernels.extend(tmp_ker)
        return kernels


    # Find the curve intersections
    def find_line_intersection(input_image, show=0):
        """
        Applies morphologyEx with parameter HitsMiss to look for all the curve 
        intersection kernels generated with give_intersection_kernels() function.
        INPUT:
            input_image =  (np.array dtype=np.uint8) binarized m x n image matrix
        OUTPUT:
            output_image = (np.array dtype=np.uint8) image where the nonzero pixels 
                           are the line intersection.
        """
        kernel = np.array(give_intersection_kernels())
        output_image = np.zeros(input_image.shape)
        for i in np.arange(len(kernel)):
            out = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel[i,:,:])
            output_image = output_image + out
        if show == 1:
            show_image = np.reshape(np.repeat(input_image, 3, axis=1),(input_image.shape[0],input_image.shape[1],3))*255
            show_image[:,:,1] = show_image[:,:,1] -  output_image *255
            show_image[:,:,2] = show_image[:,:,2] -  output_image *255
            plt.imshow(show_image)
        return output_image

    #  finding corners
    def find_endoflines(input_image, show=0):
        """
        """
        kernel_0 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1, 1, -1]), dtype="int")

        kernel_1 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [1,-1, -1]), dtype="int")

        kernel_2 = np.array((
                [-1, -1, -1],
                [1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_3 = np.array((
                [1, -1, -1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_4 = np.array((
                [-1, 1, -1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_5 = np.array((
                [-1, -1, 1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_6 = np.array((
                [-1, -1, -1],
                [-1, 1, 1],
                [-1,-1, -1]), dtype="int")

        kernel_7 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1,-1, 1]), dtype="int")

        kernel = np.array((kernel_0,kernel_1,kernel_2,kernel_3,kernel_4,kernel_5,kernel_6, kernel_7))
        output_image = np.zeros(input_image.shape)
        for i in np.arange(8):
            out = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel[i,:,:])
            output_image = output_image + out

        if show == 1:
            show_image = np.reshape(np.repeat(input_image, 3, axis=1),(input_image.shape[0],input_image.shape[1],3))*255
            show_image[:,:,1] = show_image[:,:,1] -  output_image *255
            show_image[:,:,2] = show_image[:,:,2] -  output_image *255
            plt.imshow(show_image)    

        return output_image#, np.where(output_image == 1)

    # 0- Find end of lines
    input_image = img.astype(np.uint8) # must be blaack and white thin network image
    eol_img = find_endoflines(input_image, 0)

    # 1- Find curve Intersections
    lint_img = find_line_intersection(input_image, 0)

    # 2- Put together all the nodes
    nodes = eol_img + lint_img
    plt.imshow(nodes)

person NinjasAtWork    schedule 09.01.2019