Обработка изображений Python OpenCV (15): контуры изображения

OpenCV

Предыдущий портал:

«Обработка изображений Python OpenCV (1): начало работы»

«Обработка изображений Python OpenCV (2): обработка пикселей и операции Numpy и отображение изображений в Matplotlib»

«Обработка изображений Python OpenCV (3): атрибуты изображения, интересующие области изображения и обработка каналов»

«Обработка изображений Python OpenCV (4): арифметические операции с изображениями и изменение цветового пространства»

«Обработка изображений Python OpenCV (5): геометрическое преобразование изображений»

«Обработка изображений Python OpenCV (6): пороговое значение изображения»

«Обработка изображений Python OpenCV (7): обработка сглаживания (фильтрации) изображений»

«Обработка изображений Python OpenCV (8): коррозия изображения и расширение изображения»

«Обработка изображений Python OpenCV (9): операция морфологического открытия обработки изображений, операция закрытия и операция градиента»

«Обработка изображений Python OpenCV (10): операция Top Hat и операция Black Hat морфологии обработки изображений»

«Обработка изображений Python OpenCV (11): технология обнаружения границ оператора Canny»

«Обработка изображений Python OpenCV (12): оператор Робертса, оператор Превитта, оператор Собеля и методы обнаружения границ оператора Лапласа»

«Обработка изображений Python OpenCV (13): оператор Scharr и технология обнаружения границ оператора LOG»

«Обработка изображений Python OpenCV (14): пирамида изображений»

введение

На самом деле мне очень неловко, я только что пролистал свой блог, последний раз я писал статью на OpenCV почти полмесяца назад, я использовал 3 секунды, чтобы вспомнить, где я провел последние две недели.

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

Нехорошо так себе врать!

Это на самом деле очень ароматная американская драма.Я недавно смотрела "Контратака" с 1 по 6 сезон.Неплохо.Студенты, которые любят смотреть экшен, могут попробовать.

Эта статья посвящена наброскам обработки изображений, давайте начнем с текста, надеюсь, он вам поможет.

В: Что такое набросок?

A: Контур представляет собой кривую, состоящую из ряда соединенных точек, которая представляет собой основную форму объекта.Относительно края контур непрерывен, а край не весь непрерывен.

найти контуры

Нахождение контуров OpenCV предоставляет нам готовую функциюfindContours().

В OpenCV функция извлечения контураfindContours()Это было достигнуто в 1985 г.Satoshi SuzukiАлгоритм в статье, опубликованной человеком, следующий:

Сатоши Судзуки и др. Топологический структурный анализ оцифрованных бинарных изображений по границам, Компьютерное зрение, графика и обработка изображений, 30(1):32–46, 1985.

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

Давайте сначала посмотрим на пример кода:

import cv2 as cv

img = cv.imread("black.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

print(len(contours[0]))

Сначала используйте этот кодthreshold()Изображение дешумировано, и его функция-прототип выглядит следующим образом:

retval, dst = cv.threshold(src, thresh, maxval, type[, dst] ) 
  • dst: Результирующее изображение.
  • источник: Исходное изображение.
  • thresh: текущий порог.
  • maxVal: максимальный порог, обычно 255.
  • type: Тип порога, необязательные значения:
enum ThresholdTypes {
    THRESH_BINARY     = 0,  # 大于阈值的部分被置为 255 ,小于部分被置为 0
    THRESH_BINARY_INV = 1,  # 大于阈值部分被置为 0 ,小于部分被置为 255
    THRESH_TRUNC      = 2,  # 大于阈值部分被置为 threshold ,小于部分保持原样
    THRESH_TOZERO     = 3,  # 小于阈值部分被置为 0 ,大于部分保持不变
    THRESH_TOZERO_INV = 4,  # 大于阈值部分被置为 0 ,小于部分保持不变
    THRESH_OTSU       = 8,  # 自动处理,图像自适应二值化,常用区间 [0,255]
};          

Функция, используемая для нахождения контура,findContours(), его функция-прототип выглядит следующим образом:

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  
  • изображение: исходное изображение.
  • режим: Указывает режим поиска контура.
cv2.RETR_EXTERNAL 表示只检测外轮廓。
cv2.RETR_LIST 检测的轮廓不建立等级关系。
cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
cv2.RETR_TREE 建立一个等级树结构的轮廓。
  • метод: указывает метод аппроксимации контура.
cv2.CHAIN_APPROX_NONE 存储所有的轮廓点。
cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。

можно использовать здесьprint(len(contours[0]))Функция выводит количество включенных точек, как в примере выше, используя параметрcv2.CHAIN_APPROX_NONEИмеется 1382 точки контура, при использовании параметраcv2.CHAIN_APPROX_SIMPLEТогда есть только 4 точки контура.

рисовать контур

OpenCV, используемый для рисования контура, предоставляет намdrawContours()Эта функция, вот три простых примера:

# To draw all the contours in an image:
cv2.drawContours(img, contours, -1, (0,255,0), 3)
# To draw an individual contour, say 4th contour:
cv2.drawContours(img, contours, 3, (0,255,0), 3)
# But most of the time, below method will be useful:
cnt = contours[4]
cv2.drawContours(img, [cnt], 0, (0,255,0), 3)

drawContours()В функции пять параметров:

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

Мы продолжаем предыдущий пример, чтобы использоватьfindContours()Найдите контур и нарисуйте его:

import cv2 as cv

img = cv.imread("black.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("img", img)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

print(len(contours[0]))

# 绘制绿色轮廓
cv.drawContours(img, contours, -1, (0,255,0), 3)

cv.imshow("draw", img)

cv.waitKey(0)
cv.destroyAllWindows()

характерный момент

Момент признака может помочь нам рассчитать некоторые характеристики изображения, такие как центр масс объекта, площадь объекта и т. д. Используемая функцияmoments().

moments()Функция возвращает рассчитанные моменты в виде словаря.

import cv2 as cv

img = cv.imread("number.png")

gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

cnt = contours[0]
# 获取图像矩
M = cv.moments(cnt)
print(M)

# 质心
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])

print(f'质心为:[{cx}, {cy}]')

В это время мы получили момент этого изображения. Момент M содержит много информации об особенностях контура. В дополнение к расчету центроида, показанного в примере, есть также область контура, такая как M[' м00'].

площадь контура

area = cv.contourArea(cnt)
print(f'轮廓面积为:{area}')

Площадь контура, взятая здесь, такая же, как M['m00'] выше.

контур периметр

perimeter = cv.arcLength(cnt, True)
print(f'轮廓周长为:{perimeter}')

параметрTrueУказывает, замкнут ли контур, наш контур здесь замкнут, так что пишите сюдаTrue.

Контур описанного прямоугольника

Описанный прямоугольник контура делится на обычный прямоугольник и минимальный прямоугольник. использоватьcv2.boundingRect(cnt)для получения описанного прямоугольника контура не учитывает поворот объекта, поэтому площадь прямоугольника вообще не самая маленькая; используйтеcv.minAreaRect(cnt)Можно получить наименьший прямоугольник, ограничивающий контур.

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

import cv2 as cv
import numpy as np

img = cv.imread("number.png")

gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

cnt = contours[0]

# 外接正矩形
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 外接最小矩形
min_rect = cv.minAreaRect(cnt)
print(min_rect)

box = cv.boxPoints(min_rect)
box = np.int0(box)
cv.drawContours(img, [box], 0, (0, 0, 255), 2)

cv.imshow("draw", img)

cv.waitKey(0)
cv.destroyAllWindows()

boundingRect()Возвращаемое значение функции содержит четыре значения: координаты (x, y) левого верхнего угла прямоугольника, ширину w и высоту h.

minAreaRect()Возвращаемое значение функции также содержит информацию о вращении, которая включает координаты центральной точки (x, y), ширину и высоту (w, h) и угол поворота.

аппроксимация контура

В зависимости от заданной нами точности он может аппроксимировать формы контура другими фигурами с меньшим количеством вершин. Он реализуется алгоритмом Дугласа-Пекера.

Функции, предоставляемые OpenCV,approxPolyDP(cnt, epsilon, True), второй параметр эпсилон используется для точности аппроксимации контура, указывая максимальное расстояние между исходным контуром и его аппроксимируемым контуром.Чем меньше значение, тем лучше аппроксимированный контур соответствует исходному контуру. Третий параметр указывает, является ли приближенный контур замкнутым. Конкретное использование заключается в следующем:

import cv2 as cv

img = cv.imread("number.png")

gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

cnt = contours[0]

# 计算 epsilon ,按照周长百分比进行计算,分别取周长 1% 和 10%
epsilon_1 = 0.1 * cv.arcLength(cnt, True)
epsilon_2 = 0.01 * cv.arcLength(cnt, True)

# 进行多边形逼近
approx_1 = cv.approxPolyDP(cnt, epsilon_1, True)
approx_2 = cv.approxPolyDP(cnt, epsilon_2, True)

# 画出多边形
image_1 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
image_2 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)

cv.polylines(image_1, [approx_1], True, (0, 0, 255), 2)
cv.polylines(image_2, [approx_2], True, (0, 0, 255), 2)

cv.imshow("image_1", image_1)
cv.imshow("image_2", image_2)
cv.waitKey(0)
cv.destroyAllWindows()

Первое изображение представляет собой приблизительный контур, когда эпсилон составляет 10% периметра исходного контура, а зеленая линия на втором изображении представляет собой приблизительный контур, когда эпсилон составляет 1% периметра исходного контура.

Контур выпуклой оболочки

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

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

import cv2 as cv

img = cv.imread("number.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt = contours[0]
# 绘制轮廓
image = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
cv.drawContours(image, contours, -1, (0, 0 , 255), 2)

# 寻找凸包,得到凸包的角点
hull = cv.convexHull(cnt)

# 绘制凸包
cv.polylines(image, [hull], True, (0, 255, 0), 2)

cv.imshow("image", image)
cv.waitKey(0)
cv.destroyAllWindows()

Существует также функция, с помощью которой можно определить, является ли граф выпуклым:

print(cv.isContourConvex(hull)) # True

Его возвращаемое значение — True или False.

Минимальный замкнутый круг

Далее используйте функциюcv.minEnclosingCircle()Найдите длину окружности предмета. Это круг, полностью покрывающий объект с наименьшей площадью.

import cv2 as cv

img = cv.imread("number.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt = contours[0]

# 绘制最小外接圆
(x, y), radius = cv.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
cv.circle(img, center, radius, (0, 255, 0), 2)

cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()

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

ellipse = cv.fitEllipse(cnt)
cv.ellipse(img, ellipse, (0, 255, 0), 2)

Ссылаться на

zhuanlan.zhihu.com/p/61328775

zhuanlan.zhihu.com/p/77783347

Ваше внимание к скан-коду — самый большой стимул для редактора придерживаться оригинальности :)