Страницы

Поиск по вопросам

пятница, 24 января 2020 г.

Как findContours() сохраняет найденные точки контура

#cpp #opencv


Есть изображение круглого предмета, для которого надо найти его геометрический центр. 
Снятые, с помощью findContours(), с него контуры выглядят так(один из вариантов):


Так как контуры всегда "рваные", то найти контур полного круга и снять с него минимумы/максимумы
для поиска центра не представляется возможным.
Для поиска геом.центра я беру самый длинный контур, на нём выбираю три точки на расстоянии
друг от друга и по формуле декартовых координат центра уже, собственно, ищу его(https://ru.wikipedia.org/wiki/Описанная_окружность).
Но когда я рисую на изображении контур, который я рассматриваю, и точки на нём, то
оказывается что точки расположены не там где я ожидаю их увидеть. Для точек я выбираю
первую, после четверти контура и после половины, но располагаются они на других расстояниях
друг от друга.

vector > contours;
findContours(centerROI, contours, CV_RETR_EXTERNAL, 
CV_CHAIN_APPROX_NONE);

int maxPerimIndex = 0;
for(int i = 0; i < contours.size(); i++)
{
    if(contours[i].size() > contours[maxPerimIndex].size())
        maxPerimIndex = i;
}
// точки для определения центра
Point p1 = contours[maxPerimIndex][0];
Point p2 = contours[maxPerimIndex][contours[maxPerimIndex].size() / 4];
Point p3 = contours[maxPerimIndex][contours[maxPerimIndex].size() / 2];


///////////////////////////////
// тут поиск центра
///////////////////////////////

Mat tmp = Mat::zeros(centerROI.size(), centerROI.type());
    drawContours( tmp, contours, maxPerimIndex, Scalar( 255, 255, 
    255), 1, 8);
circle( tmp, Point(CX, CY), 1, Scalar( 255, 255, 255), -1, 8, 0 );
circle( tmp, p1, 3, Scalar( 255, 255, 255), -1, 8, 0 );
circle( tmp, p2, 3, Scalar( 255, 255, 255), -1, 8, 0 );
circle( tmp, p3, 3, Scalar( 255, 255, 255), -1, 8, 0 );


Итоговое фото выглядит так:


Причём, если выбрать в контуре первую точку, последнюю и середину, вообще какой-то
бред получается:

p1 = contours1[maxPerimIndex][0];
p2 = contours1[maxPerimIndex][contours1[maxPerimIndex].size() / 2];
p3 = contours1[maxPerimIndex][contours1[maxPerimIndex].size() - 1];




Верхняя точка,которую вы видите - это p3 практически наложившаяся на p1(между ними
разница в пару пикселей).
Получается, что в контуре точки хранятся в каком-то непонятном для меня порядке и
я не могу нормально их выбрать для нахождения центра.

Помогите понять как хранятся точки и можно ли их сортировать так, чтобы в векторе
контура они шли "по порядку" или способ как можно выбрать точки на равноудалённом друг
от друга расстоянии.

В findContours() я использовал такие методы нахождения контуров: 
CV_CHAIN_APPROX_NONE, CV_CHAIN_APPROX_SIMPLE, CV_CHAIN_APPROX_TC89_L1, CV_CHAIN_APPROX_TC89_KCOS,
CV_LINK_RUNS.
CV_CHAIN_CODE выдавал какую-то дичь в ввиде прямых, остальные методы располагают
точки примерно в одном интервале друг от друга.

Благодарю за любую помощь.
    


Ответы

Ответ 1



Посмотрел, какие точки входят в контур при использовании последней картинки как исходника. Получается, что алгоритм находит самую верхнюю точку контура (не конец линии), затем идет по внешней стороне против часовой стрелки до правого нижнего конца, заворачивается внутрь, проходит по часовой стрелке по внутренней стороне всю кривую, заворачивается наружу и против часовой стрелки доходит до начальной верхней точки. Получается, что можно поступить так - взять первую точку - это верхняя. Затем идти по списку, пока не найдем первый (устойчивый) максимум по Y. И третьей точкой взять средний между ними индекс. Этот подход, похоже, будет работать и для небольшой дуги концами вверх или вниз. Если не вдаваться в систему хранения, то можно попробовать следующее - выбрать три случайных несовпадающих точки контура так, чтобы площадь треугольника на этих точках была побольше - сгенерировать несколько случайных троек и выбрать ту, для которой векторное произведение ABxAC побольше

Комментариев нет:

Отправить комментарий