"
OpenCV предоставляет множество инструментов для распознавания образов, в том числе и посредством контурного анализа. В третьей версии фреймворка практически всё, что касается сравнения контуров было выделено в отдельный модуль с говорящим названием .
Автор вопроса не уточнил, с какими фигурами (наборами фигур) будет сравниваться знак бесконечности, и отметил лишь, что интерес представляет только факт похожести кандидата искомому объекту.
Очевидно, что само по себе понятие ""бесконечность"" машине ничего не скажет, а потому придётся предложить ей некий образ в виде эталона. Пусть этот образ будет выглядеть так (файл model1.jpg):
![]()
Между тем, чтобы убедиться, что раскрытый далее метод позволяет различать и другие фигуры, добавим к эталонной модели ещё несколько различных изображений (файлы model2.jpg, model3.jpg и model4.jpg соответственно):
![]()
На самом деле все изображения одинакового размера и приведены в уменьшенном виде лишь с целью экономии места на странице.
Начнём с выделения объекта, изображение которого предоставил автор вопроса:
cv::Mat tst_src_mat = cv::imread(""test.jpg"", cv::IMREAD_GRAYSCALE);if(tst_src_mat.empty()) retu;cv::Mat tst_bin_mat;cv::threshold(tst_src_mat, tst_bin_mat, 0, 255 , cv::THRESH_OTSU | cv::THRESH_BINARY_INV);
После проведения бинаризации окажется, что помимо объекта интереса на изображении в левом нижнем углу присутствует и артефакт:
![]()
Можно от него избавиться посредством простого и в чём-то философского подхода - не обращать внимания, оставив в поле зрения лишь тот контур, что наоборот - представляет интерес. Это можно сделать, например, ориентируясь на максимальную площадь и соответствующий индекс в векторе:
std::vector<std::vector<cv::Point> > tst_cs;cv::findContours(tst_bin_mat.clone(), tst_cs , cv::RETR_EXTEAL, cv::CHAIN_APPROX_SIMPLE);int ci = -1; double max_area = 0.;for(int i = 0, n = tst_cs.size(); i < n; ++i) { const double area = cv::contourArea(tst_cs.at(i)); if(area > max_area) {max_area = area; ci = i;}}if(ci == -1) retu;
Далее следует нюанс, который подразумевает, что любые сравниваемые контуры должны иметь одинаковое количество точек в своём составе. Разумеется, что различные фигуры таковому правилу обычно следовать не будут, а значит придётся ""на ручнике"" им в этом помочь, просто добавив необходимое количество точек, но только с теми координатами, что и так уже имеются в фигуре:
const int num_pts = 300;for(int i = tst_cs.at(ci).size()-1, d = 0; i < num_pts; ++i) tst_cs[ci].push_back(tst_cs[ci][d++]);
Константа num_pts - это произвольное число. Оно в принципе зависит от того, с какой сложностью фигур предстоит работа. В идеале оно должно быть равно максимальному количеству точек у самого э-э-э... многоточечного контура из сравниваемых.
Пожалуй, настало время перейти к ранее рассмотренным эталону и изображениям сильно отличающихся от тестового фигур:
// Герой настоящей статьи, который собственно// и будет производить сравнение контуров.cv::Ptr<cv::ShapeContextDistanceExtractor> sc = cv::createShapeContextDistanceExtractor();for(int i = 0; i < 4; ++i) { std::string fname = std::string(""model"") + std::to_string(i+1) + std::string("".jpg""); cv::Mat src_mat = cv::imread(fname, cv::IMREAD_GRAYSCALE); if(src_mat.empty()) retu -1; cv::Mat bin_mat; cv::threshold(src_mat, bin_mat, 0, 255 , cv::THRESH_OTSU | cv::THRESH_BINARY_INV); std::vector<std::vector<cv::Point> > cs; cv::findContours(bin_mat.clone(), cs , cv::RETR_EXTEAL, cv::CHAIN_APPROX_SIMPLE); for(int i = cs.at(0).size()-1, d = 0; i < num_pts; ++i) cs[0].push_back(cs[0][d++]); std::cout << fname << "" - "" << sc->computeDistance(tst_cs[ci], cs[0]) << std::endl;}
Результатом работы кода окажутся такие значения:
model1.jpg - 2.84489
model2.jpg - 112.033
model3.jpg - 32.8308
model4.jpg - 524.659
Изображение model1.jpg содержит знак бесконечности, а соответственно имеет и наименьшую дистанцию различия с тестовой фигурой. Изображения со знаком ""собаки"" (model2.jpg) и ""серпа с молотом"" (model4.jpg) в виду очевидных различий завершают список непохожести. Но кто бы мог подумать, что ""бэтмену"" (model3.jpg) здесь достанется второе место. А впрочем, если приглядеться, вполне возможно уловить общие с бесконечностью черты.
"