Подсчет кривых, углов и прямых в бинарном изображении в openCV и python

Я хочу написать инструмент для определения количества углов, кривых и прямых линий внутри каждого ограниченного объекта на изображении. Все входные изображения будут черными на белом фоне, и все они будут представлять символы.

Как показано на изображении, для каждой ограниченной области отмечается каждое появление формы. Было бы предпочтительнее иметь порог того, насколько кривой должна быть кривая, чтобы считаться кривой, а не углом и т. д. И то же самое для прямых линий и углов.

Я использовал Hough Line Transform для обнаружения прямых линий на других изображениях, и я подумал, что это может работать в сочетании с чем-то здесь.

Я открыт для других библиотек, кроме opencv - это то, с чем у меня есть некоторый опыт.

заранее спасибо

Подсчет кривых, углов и прямых в бинарном изображении в openCV и python

Обновлено: Итак, основываясь на ответе Маркуса, я сделал программу, используя findContours() с CHAIN_APPROX_SIMPLE.

Он дает несколько странный результат при вводе «k», где он правильно определяет некоторые точки вокруг углов, но затем «нога» (нижняя диагональная часть) имеет много точек на ней. Я не уверен, как разделить это на сегменты, чтобы сгруппировать их в прямые, углы и кривые.

Код:

import numpy as np

img = cv2.imread('Helvetica-K.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
edges = cv2.Canny(blurred, 50, 150, apertureSize=3)
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY_INV)

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(img, contours, 0, (0,255,0), 1)

#Coordinates of each contour
for i in range(len(contours[0])):
    print(contours[0][i][0][0])
    print(contours[0][i][0][1])
    cv2.circle(img, (contours[0][i][0][0], contours[0][i][0][1]), 2, (0,0,255), -1)

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Пример изображения: Подсчет кривых, углов и прямых в бинарном изображении в openCV и python

Hough это неправильный подход ИМХО. это должно работать исключительно с контурами (списками точек). сегментация контура на прямые и изогнутые части нет включена в OpenCV, но другие программные пакеты (например, коммерческий «halcon») предлагают это. - не стесняйтесь писать предложения по функциям для OpenCV. открыть вопрос на его github

Christoph Rackwitz 03.05.2022 18:11

Спасибо, это имеет смысл. К сожалению, платные услуги выходят за рамки того, чем я сейчас занимаюсь.

YorkHvede 04.05.2022 12:10
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
2
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете использовать найтиКонтуры с опцией CHAIN_APPROX_SIMPLE.

  • Точка с углом меньше некоторого порога является углом.
  • Точка с углом больше некоторого порога находится на прямой и должна быть удалена.
  • Две соседние точки, расстояние между которыми больше некоторого порога, являются концами прямой.
  • Две соседние точки, определенные как углы, являются концами прямой линии.
  • Все остальные моменты относятся к какой-то извилистой детали.

Обновлять:

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

import numpy as np
import numpy.linalg as la
import cv2


def get_angle(p1, p2, p3):
    v1 = np.subtract(p2, p1)
    v2 = np.subtract(p2, p3)
    cos = np.inner(v1, v2) / la.norm(v1) / la.norm(v2)
    rad = np.arccos(np.clip(cos, -1.0, 1.0))
    return np.rad2deg(rad)


def get_angles(p, d):
    n = len(p)
    return [(p[i], get_angle(p[(i-d) % n], p[i], p[(i+d) % n])) for i in range(n)]


def remove_straight(p):
    angles = get_angles(p, 2)                     # approximate angles at points (two steps in path)
    return [p for (p, a) in angles if a < 170]    # remove points with almost straight angles


def max_corner(p):
    angles = get_angles(p, 1)                     # get angles at points
    j = 0

    while j < len(angles):                        # for each point
        k = (j + 1) % len(angles)                 # and its successor
        (pj, aj) = angles[j]
        (pk, ak) = angles[k]

        if la.norm(np.subtract(pj, pk)) <= 4:     # if points are close
            if aj > ak:                           # remove point with greater angle
                angles.pop(j)
            else:
                angles.pop(k)
        else:
            j += 1

    return [p for (p, a) in angles]


def main():
    img = cv2.imread('abc.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY_INV)

    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    for c in contours:                  # for each contour
        pts = [v[0] for v in c]         # get pts from contour
        pts = remove_straight(pts)      # remove almost straight angles
        pts = max_corner(pts)           # remove nearby points with greater angle
        angles = get_angles(pts, 1)     # get angles at points

        # draw result
        for (p, a) in angles:
            if a < 120:
                cv2.circle(img, p, 3, (0, 0, 255), -1)
            else:
                cv2.circle(img, p, 3, (0, 255, 0), -1)

    cv2.imwrite('out.png', img)
    cv2.destroyAllWindows()


main()

Спасибо. Я вижу, что findContours может быть полезен. Однако я не могу надежно заставить findContours обнаруживать только точки контуров. т.е. применяя его к «k», он правильно обнаружит точки на углах стержня и «плеча». Однако «нога» (нижняя диагональная линия будет иметь много точек, идущих вдоль ее направления. Как мне различить их, поскольку они в основном просто проходят по прямой диагональной линии? Также я не совсем уверен в вашем предложении о как сегментировать контуры. Я не думаю, что совсем понимаю, что вы имеете в виду. Я не из математики.

YorkHvede 04.05.2022 12:09

Опция CHAIN_APPROX_SIMPLE убирает лишние точки на прямых.

Markus 04.05.2022 12:40

Спасибо. Я отредактировал исходный пост, чтобы проиллюстрировать, как он по-прежнему имеет много точек на прямых линиях, хотя CHAIN_APPROX_SIMPLE.

YorkHvede 04.05.2022 12:56

Я добавил код, как работать с контурами OpenCV.

Markus 04.05.2022 17:59

Большое спасибо! Это определенно кажется полезным. Я постараюсь поработать с ним, чтобы увидеть, смогу ли я добиться чего-то в соответствии с тем, чего я хочу.

YorkHvede 04.05.2022 18:35

Другие вопросы по теме