Введение
Статья 565 показала пороговую обработку, дающую бинарную маску — в этой статье разберём, как извлечь из такой бинарной маски (или непосредственно из изображения с выделенными краями) реальную, полезную информацию о форме и расположении объектов через обнаружение контуров и распознавание геометрических примитивов.
Концепция
Контур (cv::findContours) — последовательность точек, описывающая границу связной области на бинарном изображении (типично — результат пороговой обработки статьи 565 или детектора краёв) — после обнаружения контуров OpenCV предоставляет функции для их анализа: вычисление площади (cv::contourArea), периметра, ограничивающего прямоугольника (cv::boundingRect), и аппроксимации формы более простыми геометрическими фигурами (cv::approxPolyDP), что позволяет, например, определить, является ли обнаруженный контур приблизительно прямоугольником, треугольником, или окружностью. Детектор краёв Canny (cv::Canny) — широко используемый алгоритм обнаружения резких изменений яркости (краёв объектов) на изображении, часто применяемый как предварительный шаг перед обнаружением контуров вместо (или в дополнение к) простой пороговой обработке.
Пример кода: Обнаружение контуров и
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat image = cv::imread("shapes.jpg");
cv::Mat gray, binary;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY);
std::vector<std::vector<cv::Point>> contours; // каждый контур — список точек его границы
cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
std::cout << "Найдено контуров: " << contours.size() << std::endl;
for (const auto &contour : contours) {
double area = cv::contourArea(contour);
if (area < 100) continue; // отфильтровываем слишком маленькие, вероятно шумовые контуры
cv::Rect boundingBox = cv::boundingRect(contour);
cv::rectangle(image, boundingBox, cv::Scalar(0, 255, 0), 2); // рисуем зелёный прямоугольник вокруг
std::cout << "Площадь контура: " << area << std::endl;
}
cv::imwrite("contours_result.png", image);
return 0;
}
// Аппроксимация формы контура — определение, является ли он приблизительно прямоугольником
#include <opencv2/opencv.hpp>
void classifyShape(const std::vector<cv::Point> &contour)
{
std::vector<cv::Point> approx;
double perimeter = cv::arcLength(contour, true);
cv::approxPolyDP(contour, approx, 0.02 * perimeter, true); // точность аппроксимации — 2% от периметра
if (approx.size() == 3) {
std::cout << "Треугольник" << std::endl;
} else if (approx.size() == 4) {
std::cout << "Четырёхугольник (возможно, прямоугольник)" << std::endl;
} else if (approx.size() > 6) {
std::cout << "Вероятно, окружность (много вершин аппроксимации)" << std::endl;
}
}
Пояснения к коду
cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE) показывает типичный вызов с двумя важными параметрами — RETR_EXTERNAL указывает извлекать только внешние контуры (игнорируя контуры внутренних «дырок» в объектах, если они не нужны для конкретной задачи), а CHAIN_APPROX_SIMPLE сжимает представление контура, храня только действительно значимые точки изгиба, а не каждую отдельную точку вдоль прямых участков границы. Фильтрация по area < 100 показывает практичный, часто необходимый шаг — пороговая обработка реальных, не идеально чистых изображений часто даёт множество мелких, шумовых контуров, не представляющих реального интереса, и явная фильтрация по минимальной площади помогает отсеять эти шумовые срабатывания перед дальнейшей обработкой. cv::approxPolyDP показывает практичный способ классификации формы — аппроксимация контура с заданной точностью (0.02 * perimeter, обычно выражаемой как процент от периметра самого контура) даёт упрощённый многоугольник, чьё количество вершин позволяет приблизительно судить о геометрической форме исходного объекта (3 вершины — треугольник, 4 — четырёхугольник, много — приближение к окружности).
Подводные камни
- Игнорирование фильтрации мелких, шумовых контуров перед дальнейшим анализом, особенно для изображений с реальным, неидеальным освещением и текстурой фона — без явной фильтрации (как по минимальной площади в примере) последующая обработка может обрабатывать огромное количество нерелевантных, мелких контуров, представляющих лишь шум пороговой обработки, а не реальные объекты интереса, что не только неэффективно, но и может дать ложные, ошибочные результаты при попытке содержательного анализа этих шумовых контуров.
- Выбор неподходящего значения точности аппроксимации (
epsilon) дляapproxPolyDP— слишком маленькое значение (относительно периметра контура) даёт аппроксимацию, слишком близкую к исходному, возможно зашумлённому контуру (с большим количеством вершин, не отражающих реальную, простую геометрическую форму объекта), тогда как слишком большое значение может избыточно упростить форму, потеряв реальные, значимые особенности формы объекта — подбор подходящего значения (часто выражаемого именно как процент от периметра, а не абсолютное число, для устойчивости к разным масштабам контуров) обычно требует эмпирической настройки для конкретного типа обрабатываемых изображений. - Применение
cv::findContoursк изображению, не являющемуся бинарным (одноканальным, чёрно-белым) — функция ожидает именно бинарное изображение (обычно результат пороговой обработки статьи 565 или детектора Canny), и попытка применить её непосредственно к цветному или просто не бинаризованному изображению в оттенках серого либо приведёт к ошибке, либо к результату, не отражающему реальные границы значимых объектов изображения. - Использование классификации формы через простой подсчёт вершин аппроксимации (как в
classifyShape) без дополнительной проверки геометрических свойств (соотношение сторон, выпуклость, соотношение площади контура к площади его ограничивающего прямоугольника) — простой подсчёт вершин даёт лишь приблизительную, не всегда надёжную классификацию (например, искажённый перспективой квадрат и реальный прямоугольник иной формы оба дадут 4 вершины при аппроксимации), и для более надёжной, точной классификации формы часто нужны дополнительные геометрические проверки, дополняющие простой подсчёт количества вершин аппроксимированного многоугольника.
Первый блок цикла «Компьютерное зрение (OpenCV + Qt)» (статьи 561-566, основы и базовая обработка изображений) завершён. Далее следует блок 2 — детекция признаков, видео и распознавание объектов (567-572).