Статья Автор: Деникина Наталья Владимировна

Свойства контуров

Свойства контуров

Исследуем возможности метода cv2.findContours()

Для нахождения контуров изображение преобразуется в оттенки серого: 

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).

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

Бинаризация изображения

Бинаризация — это процесс преобразования градаций серого изображения в черно-белое. Пиксели ниже определенного порога становятся черными (0), а выше — белыми (255). В OpenCV это можно сделать с помощью функции cv2.threshold() или cv2.inRange().

img_bin = cv2.inRange(img_gray, threshold_min, threshold_max)

Здесь threshold_min и threshold_max — это нижний и верхний пороги бинаризации соответственно.

  • threshold_min: Минимальное значение градации серого, при котором пиксели изображения будут белыми.
  • threshold_max: Максимальное значение градации серого, при котором пиксели изображения будут белыми.

Пиксели с значениями, попадающими в диапазон от threshold_min до threshold_max, становятся белыми, остальные — черными.

Нахождение контуров

Контуры — это границы объектов на изображении. OpenCV предоставляет функцию cv2.findContours() для нахождения контуров на бинаризованном изображении:

_, cont, h = cv2.findContours(img_bin, method, cv2.CHAIN_APPROX_SIMPLE)

Параметры:

  • img_bin: Входное бинарное изображение, на котором будут искаться контуры.
  • method: Режим извлечения контуров, который может принимать следующие значения:
    • cv2.RETR_EXTERNAL: Извлекает только внешние контуры.
    • cv2.RETR_LIST: Извлекает все контуры, не создавая иерархии.
    • cv2.RETR_CCOMP: Извлекает все контуры и организует их в двухуровневую иерархию: внешние и внутренние.
    • cv2.RETR_TREE: Извлекает все контуры и организует их в полноценное дерево иерархии.
  • cv2.CHAIN_APPROX_SIMPLE: Метод аппроксимации контуров, который удаляет все избыточные точки и сжимает контур, сохраняя его основную структуру.

Отображение контуров

Контуры могут быть нарисованы на изображении с помощью функции cv2.drawContours():

cv2.drawContours(img_contours, contours, -1, (0, 128, 255), 2)

Параметры:

  • img_contours: Изображение, на котором будут нарисованы контуры.
  • contours: Список контуров, которые необходимо нарисовать.
  • -1: Указывает, что нужно нарисовать все контуры.
  • (0, 128, 255): Цвет контуров в формате BGR (синий-зеленый-красный).
  • 2: Толщина линий контуров.

Для подбора диапазонов и настройки методов вручную можно использовать подготовленный скрипт:

import cv2
import numpy as np

def nothing(x):
    pass

# Переменные для хранения состояния
method_index = 0
methods = ["RETR_EXTERNAL", "RETR_LIST", "RETR_CCOMP", "RETR_TREE"]

# Функция для обработки событий мыши
def mouse_event(event, x, y, flags, param):
    global method_index
    if event == cv2.EVENT_LBUTTONDOWN:
        # Проверяем, попадает ли клик в одну из "кнопок"
        if 10 <= x <= 250:
            if 50 <= y <= 90:
                method_index = 0
            elif 100 <= y <= 140:
                method_index = 1
            elif 150 <= y <= 190:
                method_index = 2
            elif 200 <= y <= 240:
                method_index = 3
        print(methods[method_index])

# Создаем окна для отображения изображения и настройки параметров
cv2.namedWindow("result")
cv2.namedWindow("settings")
cv2.namedWindow("gray")
cv2.namedWindow("binary")
cv2.setMouseCallback("settings", mouse_event)

# Добавляем трекбары для настройки порогов бинаризации
cv2.createTrackbar('threshold_min', 'settings', 0, 255, nothing)
cv2.createTrackbar('threshold_max', 'settings', 255, 255, nothing)

# Создаем изображение для окна settings с увеличенным размером
settings_img = np.zeros((300, 300, 3), np.uint8)

while True:
    img = cv2.imread('image_simple.png')
    h, w, _ = img.shape

    # Преобразование в градации серого
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Считываем значения порогов
    threshold_min = cv2.getTrackbarPos('threshold_min', 'settings')
    threshold_max = cv2.getTrackbarPos('threshold_max', 'settings')

    # Бинаризация изображения с двумя порогами
    img_bin = cv2.inRange(img_gray, threshold_min, threshold_max)

    # Поиск контуров с выбранным методом
    method = getattr(cv2, methods[method_index])
    contours, _ = cv2.findContours(img_bin, method, cv2.CHAIN_APPROX_SIMPLE)

    # Рисование найденных контуров
    img_contours = img.copy()
    cv2.drawContours(img_contours, contours, -1, (0, 128, 255), 2)

    # Очистка изображения для settings
    settings_img[:] = (0, 0, 0)

    # Рисование "кнопок" на изображении settings
    cv2.rectangle(settings_img, (10, 50), (250, 90), (50, 50, 50), -1)
    cv2.putText(settings_img, methods[0], (15, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

    cv2.rectangle(settings_img, (10, 100), (250, 140), (50, 50, 50), -1)
    cv2.putText(settings_img, methods[1], (15, 125), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

    cv2.rectangle(settings_img, (10, 150), (250, 190), (50, 50, 50), -1)
    cv2.putText(settings_img, methods[2], (15, 175), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

    cv2.rectangle(settings_img, (10, 200), (250, 240), (50, 50, 50), -1)
    cv2.putText(settings_img, methods[3], (15, 225), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

    # Вывод окна с кнопками и трекбарами
    cv2.imshow('settings', settings_img)

    # Вывод серого изображения
    cv2.imshow('gray', img_gray)

    # Вывод результата бинаризации
    cv2.imshow('binary', img_bin)

    # Вывод изображения с контурами и оригинала
    cv2.imshow('result', img_contours)
    cv2.imshow('original', img)

    # Ожидание нажатия клавиши ESC для выхода
    ch = cv2.waitKey(5)
    if ch == 27:
        break

cv2.destroyAllWindows()

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

if ch == 32:
    for contour in contours:
        print(contour)

Однако, даже на этом простом изображении контуры состоят более чем из 100 точек и провести их анализ представляется мало возможным!
Для начала, можем исследовать их свойства - площадь и периметр:

for contour in contours:
    area = cv2.contourArea(contour)
    perimeter = cv2.arcLength(contour, True)
    print(f"Площадь: {area}, Периметр: {perimeter}")


# Площадь: 78881.0, Периметр: 1050.0874361991882
# Площадь: 1256.0, Периметр: 131.882248878479

И сейчас становится понятно, что фигуры можно отличить по площади!

Пропустить Навигационные Ссылки.
Чтобы оставить комментарий нужна авторизация
Печать