Ich sage euch: man muß noch Chaos in sich haben, um einen tanzenden Stern gebären zu können (“Also sprach Zarathustra”, Friedrich Nietzsche)
Обнаружение конгуров с использованием OpenCV
При обнаружении контуров, можно обнаруживать границы объектов и легко их локализовать на изображении. Часто это первый шаг для многих интересных приложений, таких как извлечение изображения на переднем плане, сегментация простого изображения, обнаружение и распознавание.
Итак, давайте узнаем о контурах и обнаружении контуров с помощью OpenCV, как их можно использовать для создания различных приложений.
Есть несколько действительно крутых приложений, использующих контуры для обнаружения движения или сегментации. Вот некоторые примеры:
Обнаружение движения: в видеонаблюдении технология обнаружения движения имеет множество приложений, начиная от внутренней и внешней безопасности, контроля дорожного движения, обнаружения поведения во время занятий спортом, поиск оставленных без присмотра объектов и даже сжатие видео. На рисунке ниже показано, как обнаружение движения людей в видеопотоке может быть полезно в приложении для наблюдения. Обратите внимание, как группа людей, стоящих в левой части изображения, не обнаруживается. Захватываются только те, кто находится в движении. Обратитесь к этой статье для подробного изучения этого подхода.
Поиск подозрительных объектов. Любой объект, оставленный без присмотра в общественных местах, обычно, считается подозрительным. Эффективным и безопасным решением может быть: (Автоматическое обнаружение объекта посредством формирования контура с использованием вычитания фона).
Изображение из процитированного документа — фоновая рамка без и с оставленным без присмотра предметом — идентификация и маркировка оставленного без присмотра предмета
Сегментация фона/переднего плана: чтобы заменить фон изображения другим, необходимо выполнить извлечение изображения переднего плана (аналогично сегментации изображения). Использование контуров — это один из подходов, который можно использовать для сегментации. Обратитесь к этому сообщению для получения более подробной информации. На следующих изображениях показаны простые примеры такого приложения:
Когда на границе объекта соединяются все точки, то получается контур. Как правило, конкретный контур относится к граничным пикселям, имеющим одинаковый цвет и интенсивность. OpenCV позволяет легко находить и рисовать контуры на изображениях. Есть две простые функции:
findContours()
drawContours()
Также, у них есть два разных алгоритма обнаружения контура:
CHAIN_APPROX_SIMPLE
CHAIN_APPROX_NONE
Рассмотрим их подробно в приведенных ниже примерах. На следующем рисунке показано, как эти алгоритмы могут обнаруживать контуры простых объектов.
Теперь, когда вы знаете, что такое контуры, обсудим последовательность их обнаружения.
Последовательность поиска и отрисовки контуров с помощью OpenCV ↑
OpenCV делает это довольно простой задачей. Просто выполните следующие действия:
1. Прочтите изображение и преобразуйте его в формат оттенков серого
Прочтите изображение и преобразуйте изображение в формат оттенков серого. Преобразование изображения в оттенки серого очень важно, поскольку оно подготавливает изображение к следующему шагу. Преобразование изображения в одноканальное изображение в оттенках серого важно для определения порога, что, в свою очередь, необходимо для правильной работы алгоритма определения контура.
2. Применить двоичный порог
При нахождении контуров сначала всегда применяйте двоичное пороговое значение или обнаружение границ Canny к изображению в градациях серого. Здесь мы применим двоичное пороговое значение. Такое преобразование изображения в черно-белое, выделяет интересующие объекты и упрощает алгоритм обнаружения контуров. При установке порога граница объекта на изображении становится полностью белой, при этом все пиксели имеют одинаковую интенсивность. Теперь алгоритм может определять границы объектов по этим белым пикселям.
Примечание. Черные пиксели, имеющие значение 0, воспринимаются как пиксели фона и игнорируются.
Здесь может возникнуть один вопрос. Что, если мы будем использовать отдельные каналы, такие как R (красный), G (зеленый) или B (синий), вместо изображений в градациях серого (с пороговыми значениями)? В таком случае алгоритм определения контура не сработает. Как мы обсуждали ранее, алгоритм ищет границы и пиксели аналогичной интенсивности для обнаружения контуров. Двоичное изображение предоставляет эту информацию намного лучше, чем изображение с одним цветовым каналом (RGB). Далее будет показаны результирующие изображения при использовании только одного канала R, G или B вместо изображений в оттенках серого и изображений с пороговыми значениями.
3. Найдите контуры
Используйте функцию findContours() для обнаружения контуров на изображении.
4. Нарисуйте контуры на исходном изображении RGB
После определения контуров используйте функцию drawContours(), чтобы наложить контуры на исходное изображение RGB.
Все эти шаги будут иметь гораздо больше смысла и станут понятнее, когда вы начнёте писать код.
Начните с импорта OpenCV и чтения входного изображения.
import cv2
# прочитать изображение
image = cv2.imread('input/image_1.jpg')
Мы предполагаем, что изображение находится во входной папке текущего каталога проекта. Следующим шагом является преобразование изображения в изображение в оттенках серого (одноканальный формат).
# преобразовать изображение в формат оттенков серого
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Теперь используйте функцию threshold() для применения бинарного порога к изображению. Любому пикселю со значением больше 150 будет присвоено значение 255 (белый). Все оставшиеся пиксели в результирующем изображении будут установлены в 0 (черный). Пороговое значение 150 — это настраиваемый параметр, поэтому вы можете с ним поэкспериментировать.
После установления порога визуализируйте двоичное изображение, используя функцию imshow(), как показано ниже.
Посмотрите на изображение ниже! Это двоичное представление исходного изображения RGB. Можно ясно видеть, как перо, границы планшета и телефона все белые. Алгоритм контура будет рассматривать их как объекты и находить точки контура вокруг границ этих белых объектов.
Обратите внимание, что фон полностью черный, включая заднюю часть телефона. Такие области будут игнорироваться алгоритмом. Принимая белые пиксели по периметру каждого объекта как пиксели одинаковой интенсивности, алгоритм объединяет их, чтобы сформировать контур на основе меры сходства.
Теперь давайте найдем и нарисуем контуры с помощью метода CHAIN_APPROX_NONE.
Начнем с функции findContours(). У неё, как показано ниже, есть три обязательных аргумента. Дополнительные аргументы см. На странице документации здесь.
image: двоичное входное изображение, полученное на предыдущем шаге.
mode: это режим поиска контура. Мы предоставили его как RETR_TREE и это означает, что алгоритм извлечет все возможные контуры из двоичного изображения. Доступны и другие режимы извлечения контуров и их обсудим тоже. Вы можете узнать больше об этих вариантах здесь.
method: определяет метод аппроксимации контура. В этом примере мы будем использовать CHAIN_APPROX_NONE. Хотя он немного медленнее, чем CHAIN_APPROX_SIMPLE, здесь для хранения ВСЕХ точек контура будем использовать этот метод.
Здесь надо подчеркнуть, что mode относится к типу извлекаемых контуров, а method относится к тому, какие точки внутри контура сохраняются. Ниже обсудим оба более подробно.
Легко визуализировать и понять результаты различных методов на одном и том же изображении.
Поэтому в примерах кода ниже обязательно делаем копию исходного изображения, а затем демонстрируем методы (не изменяя оригинал).
Затем используйте функцию drawContours(), чтобы наложить контуры на изображение RGB. Эта функция имеет четыре обязательных и несколько необязательных аргументов. Первые четыре аргумента ниже обязательны. Для необязательных аргументов пожалуйста, обратитесь к странице документации здесь.
image: это входное изображение RGB, на котором вы хотите нарисовать контур.
contours: указывает контуры, полученные с помощью функции findContours().
contourIdx: координаты точек контура в пикселях перечислены в полученных контурах. Используя этот аргумент, вы можете указать позицию индекса из этого списка, указывая, какую именно точку контура вы хотите нарисовать. Если указать отрицательное значение, будут нарисованы все точки контура.
color: указывает цвет точек контура, которые вы хотите нарисовать. Мы рисуем точки зеленым цветом.
thickness: это толщина точек контура.
# обнаруживаем контуры на двоичном изображении с помощью cv2.CHAIN_APPROX_NONE
contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# рисуем контуры на исходном изображении
image_copy = image.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# смотрим результаты
cv2.imshow('None approximation', image_copy)
cv2.waitKey(0)
cv2.imwrite('contours_none_image1.jpg', image_copy)
cv2.destroyAllWindows()
Выполнение приведенного выше кода приведет к созданию и отображению изображения, показанного ниже. Также сохраняем образ на диск.
На следующем рисунке показано исходное изображение (слева), а также исходное изображение с наложенными контурами (справа).
Как видете на приведенном выше рисунке, контуры, созданные алгоритмом, отлично справляются с определением границ каждого объекта. Однако, если вы внимательно посмотрите на телефон, то обнаружите, что он содержит более одного контура. Отдельные контуры были выделены для круглых областей, связанных с объективом камеры и источником света. Есть также «второстепенные» контуры вдоль краев телефона. Имейте в виду, что точность и качество алгоритма контура сильно зависят от качества предоставленного двоичного изображения (снова посмотрите на двоичное изображение в предыдущем разделе, где можете увидеть линии, связанные с этими вторичными контурами). Для некоторых приложений требуются контуры высокого качества. В таких случаях, поэкспериментируйте с разными порогами при создании двоичного изображения и посмотрите, улучшит ли это результирующие контуры.
Существуют и другие подходы, которые можно использовать для устранения нежелательных контуров из двоичных карт до создания контуров. Можете использовать более продвинутые функции, связанные с алгоритмом контура, которые обсудим здесь.
Использование одного канала: красный, зеленый или синий
Чтобы получить представление, ниже приведены некоторые результаты при раздельном использовании красного, зеленого и синего каналов при обнаружении контуров. Мы обсуждали это ранее на этапах определения контура. Ниже приведен код для того же изображения, что и выше.
import cv2
# прочитать изображение
image = cv2.imread('input/image_1.jpg')
# Разделение каналов B, G, R
blue, green, red = cv2.split(image)
# обнаруживаем контуры по синему каналу и без порога
contours1, hierarchy1 = cv2.findContours(image=blue, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# рисуем контуры на исходном изображении
image_contour_blue = image.copy()
cv2.drawContours(image=image_contour_blue, contours=contours1, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# смотрим результат
cv2.imshow('Contour detection using blue channels only', image_contour_blue)
cv2.waitKey(0)
cv2.imwrite('blue_channel.jpg', image_contour_blue)
cv2.destroyAllWindows()
# обнаруживаем контуры с использованием зеленого канала и без порогового значения
contours2, hierarchy2 = cv2.findContours(image=green, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# рисуем контуры на исходном изображении
image_contour_green = image.copy()
cv2.drawContours(image=image_contour_green, contours=contours2, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# смотрим результат
cv2.imshow('Contour detection using green channels only', image_contour_green)
cv2.waitKey(0)
cv2.imwrite('green_channel.jpg', image_contour_green)
cv2.destroyAllWindows()
# detect contours using red channel and without thresholding
contours3, hierarchy3 = cv2.findContours(image=red, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# рисуем контуры на исходном изображении
image_contour_red = image.copy()
cv2.drawContours(image=image_contour_red, contours=contours3, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# смотрим результат
cv2.imshow('Contour detection using red channels only', image_contour_red)
cv2.waitKey(0)
cv2.imwrite('red_channel.jpg', image_contour_red)
cv2.destroyAllWindows()
Посмотрите на результаты обнаружения контура для всех трех отдельных цветовых каналов.
На изображении выше видно, что алгоритм обнаружения контуров не может правильно найти контуры. Это связано с тем, что он не может правильно определять границы объектов, а также нечетко определена разница в интенсивности между пикселями. По этой причине предпочительнее использовать оттенки серого и двоичное пороговое изображение для обнаружения контуров.
Теперь давайте посмотрим, как работает алгоритм CHAIN_APPROX_SIMPLE и чем он отличается от алгоритма CHAIN_APPROX_NONE.
Вот для этого код:
"""
Теперь давайте попробуем с `cv2.CHAIN_APPROX_SIMPLE`
"""
# обнаруживаем контуры на двоичном изображении с помощью cv2.ChAIN_APPROX_SIMPLE
contours1, hierarchy1 = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# рисуем контуры на исходном изображении для `CHAIN_APPROX_SIMPLE`
image_copy1 = image.copy()
cv2.drawContours(image_copy1, contours1, -1, (0, 255, 0), 2, cv2.LINE_AA)
# смотрим результат
cv2.imshow('Simple approximation', image_copy1)
cv2.waitKey(0)
cv2.imwrite('contours_simple_image1.jpg', image_copy1)
cv2.destroyAllWindows()
Здесь единственная разница заключается в том, что method для findContours() определён, как CHAIN_APPROX_SIMPLE вместо CHAIN_APPROX_NONE.
Алгоритм CHAIN_APPROX_SIMPLE сжимает горизонтальные, вертикальные и диагональные сегменты вдоль контура и оставляет только их конечные точки. Это означает, что любая из точек на прямых путях будет отклонена и останутся только конечные точки. Например, рассмотрим контур по прямоугольнику. Все точки контура, кроме четырех угловых, будут отклонены. Этот метод быстрее, чем CHAIN_APPROX_NONE, потому что алгоритм не сохраняет все точки, использует меньше памяти и, следовательно, требует меньше времени для выполнения. Ниже показаны результаты.
Если вы внимательно присмотритесь, почти нет различий между CHAIN_APPROX_NONE и CHAIN_APPROX_SIMPLE.
Итак, почему это так?
Заслуга принадлежит функции drawContours(). Хотя метод CHAIN_APPROX_SIMPLE обычно приводит к меньшему количеству точек, функция drawContours() автоматически соединяет соседние точки, присоединение к ним, даже если их нет в списке контуров.
Итак, как можно подтвердить, что алгоритм CHAIN_APPROX_SIMPLE действительно работает?
Самый простой способ — вручную перебрать точки контура и нарисовать круг на обнаруженных координатах контура с помощью OpenCV.
Также, используем другое изображение, которое на самом деле поможет нам визуализировать результаты алгоритма.
В следующем коде изображение выше используется для визуализации эффекта алгоритма CHAIN_APPROX_SIMPLE. Почти все такое же, как в предыдущем примере кода, за исключением двух дополнительных циклов for и некоторых имен переменных.
Первый цикл for проходит по каждой области контура, присутствующей в списке контуров.
Второй проходит по каждой из координат в этой области.
Затем мы рисуем зеленый кружок в каждой координатной точке, используя функцию circle() из OpenCV.
Наконец, мы визуализируем результаты и сохраняем их на диск.
# чтобы визуализировать эффект `CHAIN_APPROX_SIMPLE`, нам нужно правильное изображение
image1 = cv2.imread('input/image_2.jpg')
img_gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_gray1, 150, 255, cv2.THRESH_BINARY)
contours2, hierarchy2 = cv2.findContours(thresh1, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
image_copy2 = image1.copy()
cv2.drawContours(image_copy2, contours2, -1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('SIMPLE Approximation contours', image_copy2)
cv2.waitKey(0)
image_copy3 = image1.copy()
for i, contour in enumerate(contours2): # loop over one contour area
for j, contour_point in enumerate(contour): # loop over the points
# draw a circle on the current contour coordinate
cv2.circle(image_copy3, ((contour_point[0][0], contour_point[0][1])), 2, (0, 255, 0), 2, cv2.LINE_AA)
# смотрим результат
cv2.imshow('CHAIN_APPROX_SIMPLE Point only', image_copy3)
cv2.waitKey(0)
cv2.imwrite('contour_point_simple.jpg', image_copy3)
cv2.destroyAllWindows()
Выполнение приведенного выше кода дает следующий результат:
Обратите внимание на выходное изображение, которое находится справа на рисунке выше. Обратите внимание, что вертикальная и горизонтальная стороны книги содержат только четыре точки по углам книги. Также обратите внимание, что буквы и птица обозначены дискретными точками, а не отрезками линий.
Иерархии контуров
Иерархии обозначают родительско-дочерние отношения между контурами. Вы увидите, как каждый режим поиска контура влияет на обнаружение контура на изображениях и дает иерархические результаты.
Объекты, обнаруженные алгоритмами определения контуров на изображении, могут быть:
Отдельными объектами, разбросанными по изображению (как в первом примере), или
Объектами и формами друг в друге
В большинстве случаев, когда фигура содержит больше фигур, мы можем с уверенностью заключить, что внешняя форма является родительской для внутренней формы.
Взгляните на следующий рисунок, он содержит несколько простых форм, которые помогут продемонстрировать иерархию контуров.
Теперь посмотрим на рисунок ниже, на котором обозначены контуры, связанные с каждой формой на рисунке. Каждое из чисел имеет значение.
Все отдельные числа, то есть 1, 2, 3 и 4, являются отдельными объектами в соответствии с иерархией контуров и отношениями родитель-потомок.
Можно сказать, что 3a — ребенок 3-го. Обратите внимание, что 3а представляет собой внутреннюю часть в контуре 3.
Контуры 1, 2 и 4 являются родительскими фигурами без каких-либо связанных дочерних фигур, поэтому их нумерация произвольна. Другими словами, контур 2 можно было бы обозначить как 1 и наоборот.
До сих пор мы использовали один конкретный метод поиска, RETR_TREE, чтобы найти и нарисовать контуры, но в OpenCV есть еще три метода поиска контура, а именно RETR_LIST, RETR_EXTERNAL и RETR_CCOMP.
Итак, давайте теперь воспользуемся предыдущим изображением в каждом из этих четырех методов, а также связанный с ними код для получения контуров. Следующий код считывает изображение с диска, преобразует его в оттенки серого и применяет двоичное пороговое значение.
"""
Обнаружение контуров и отрисовка с использованием различных режимов извлечения для дополнения
понимание иерархий
"""
image2 = cv2.imread('input/custom_colors.jpg')
img_gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
ret, thresh2 = cv2.threshold(img_gray2, 150, 255, cv2.THRESH_BINARY)
RETR_LIST
Метод извлечения контуров RETR_LIST не создает никаких родительских отношений между извлеченными контурами. Таким образом, для всех обнаруженных контурных областей значения позиции индекса First_Child и Parent всегда равны -1.
Все контуры будут иметь соответствующие предыдущие и следующие контуры, как описано выше.
Посмотрите, как метод RETR_LIST реализован в коде.
Ясно видно, что позиции 3-го и 4-го индексов всех обнаруженных областей контура равны -1, как и ожидалось.
RETR_EXTERNAL
Метод извлечения контура RETR_EXTERNAL действительно интересен. Он обнаруживает только родительские контуры и игнорирует любые дочерние контуры. Таким образом, на всех внутренних контурах, таких как 3a и 4, не будет нарисовано никаких точек.
На приведенном выше изображении показаны только точки, нарисованные на контурах 1, 2 и 3. Контуры 3a и 4 опущены, поскольку они являются дочерними контурами.
RETR_CCOMP
В отличие от RETR_EXTERNAL, RETR_CCOMP извлекает все контуры изображения. Наряду с этим, он также применяет двухуровневую иерархию ко всем формам или объектам на изображении.
Это значит, что
все внешние контуры будут иметь уровень иерархии 1, а
все внутренние контуры будут иметь уровень иерархии 2.
Но что, если у нас есть контур внутри другого контура с уровнем иерархии 2? Точно так же, как у нас есть контур 4 после контура 3a.
В этом случае:
контур 4, опять же, будет иметь уровень иерархии 1.
Если внутри контура 4 есть контуры, то у них будет уровень иерархии 2.
На следующем изображении контуры пронумерованы в соответствии с их уровнем иерархии, как описано выше.
На изображении выше показан уровень иерархии как HL-1 или HL-2 для уровней 1 и 2 соответственно. Теперь давайте посмотрим на код и массив выходной иерархии.
Здесь мы видим, что все отношения Next, Previous, First_Child и Parent поддерживаются в соответствии с методом поиска контуров, поскольку все контуры обнаруживаются. Как и ожидалось, Предыдущее значение первой области контура равно -1. И контуры, у которых нет Родителя, также имеют значение -1.
RETR_TREE
Как и RETR_CCOMP, RETR_TREE также извлекает все контуры. Он также создает полную иерархию с уровнями, не ограниченными 1 или 2. Каждый контур может иметь свою собственную иерархию в соответствии с уровнем, на котором он находится, и соответствующими родительско-дочерними отношениями, которые у него есть.
Из рисунка выше видно, что:
Контуры 1, 2 и 3 находятся на одном уровне, то есть на уровне 0.
Контур 3a присутствует на уровне иерархии 1, поскольку он является дочерним элементом контура 3.
Контур 4 — это новая область контура, поэтому его уровень иерархии равен 2.
Следующий код использует режим RETR_TREE для извлечения контуров.
Наконец, давайте посмотрим на полное изображение со всеми контурами, нарисованными при использовании режима RETR_TREE.
Все контуры нарисованы так, как ожидалось, и участки контура хорошо видны. Вы также делаете вывод, что контуры 3 и 3a — это два отдельных контура, поскольку они имеют разные границы контура и площади. В то же время совершенно очевидно, что контур 3a является потомком контура 3.
Теперь, когда вы знакомы со всеми алгоритмами контуров, доступными в OpenCV, вместе с соответствующими входными параметрами и конфигурациями, поэкспериментируйте и убедитесь сами, как они работают.
Сравнение времени выполнения различных методов поиска контура
Недостаточно знать методы извлечения контуров. Вы также должны знать об их относительном времени обработки. В следующей таблице сравнивается время выполнения для каждого из описанных выше методов.
Метод получения контура
Время (в секундах)
RETR_LIST
0,000382
RETR_EXTERNAL
0,000554
RETR_CCOMP
0,001845
RETR_TREE
0,005594
Сравнение скорости вывода разных методов
Из приведенной выше таблицы следует несколько интересных выводов:
RETR_LIST и RETR_EXTERNAL занимают наименьшее количество времени для выполнения, поскольку RETR_LIST не определяет никакой иерархии, а RETR_EXTERNAL только извлекает родительские контуры
RETR_CCOMP занимает второе по времени выполнение. Он извлекает все контуры и определяет двухуровневую иерархию.
RETR_TREE занимает максимальное время для выполнения, так как извлекает все контуры, а также определяет независимый уровень иерархии для каждого родительско-дочернего отношения.
Хотя приведенное выше время может показаться незначительным, важно знать о различиях для приложений, которые могут потребовать значительного объема обработки контура. Также стоит отметить, что время обработки может варьироваться в зависимости от извлекаемых контуров и определяемых ими уровней иерархии.
Ограничения
Пока что все изученные нами примеры казались интересными, а их результаты обнадеживающими. Однако бывают случаи, когда контурный алгоритм может не дать значимых и полезных результатов. Рассмотрим и такой пример.
Когда объекты на изображении сильно контрастируют с их фоном, вы можете четко определить контуры, связанные с каждым объектом. Но что, если у вас есть изображение, как на рисунке ниже. У него не только яркий объект (щенок), но и фон, загроможденный тем же значением (яркостью), что и объект интереса (щенок). Вы обнаружите, что контуры на правом изображении даже не завершены. Кроме того, в области фона есть несколько нежелательных контуров.
Обнаружение контура также может не сработать, если фон объекта на изображении заполнен линиями.
Мы начали с определения контуров и научились реализовывать это в OpenCV. Увидели, как приложения используют контуры для обнаружения и сегментации движения. Затем мы продемонстрировали использование четырех различных режимов восстановления и двух методов контурной аппроксимации. Мы также научились рисовать контуры, закончили обсуждением контурных иерархий и как различные режимы поиска контуров влияют на прорисовку контуров на изображении.
Основные выводы:
Алгоритмы определения контуров в OpenCV работают очень хорошо, когда изображение имеет темный фон и четко определенный интересующий объект.
Но когда фон входного изображения загроможден или имеет ту же интенсивность пикселей, что и объект из-Интересно, алгоритмы не так хороши.
Код у вас есть, почему бы не поэкспериментировать с разными изображениями прямо сейчас! Попробуйте изображения, содержащие разные формы, и поэкспериментируйте с разными пороговыми значениями. Кроме того, изучите различные режимы поиска, используя тестовые изображения, содержащие вложенные контуры. Здесь, вы можете полностью оценить иерархические отношения между объектами.