Ответ 1
Если вы не знаете количество строк на изображении, вы можете использовать функцию cv::partition
для разделения строк на группу эквивалентности.
Я предлагаю вам следующую процедуру:
-
Разделите свои строки, используя
cv::partition
. Вам нужно указать хорошую функцию предиката. Это действительно зависит от линий, которые вы извлекаете из изображения, но я думаю, что он должен проверять следующие условия:- Угол между линиями должен быть довольно небольшим (например, менее 3 градусов). Используйте dot product для вычисления углового косинуса.
- Расстояние между центрами сегментов должно быть меньше половины максимальной длины двух сегментов.
Например, он может быть реализован следующим образом:
bool isEqual(const Vec4i& _l1, const Vec4i& _l2)
{
Vec4i l1(_l1), l2(_l2);
float length1 = sqrtf((l1[2] - l1[0])*(l1[2] - l1[0]) + (l1[3] - l1[1])*(l1[3] - l1[1]));
float length2 = sqrtf((l2[2] - l2[0])*(l2[2] - l2[0]) + (l2[3] - l2[1])*(l2[3] - l2[1]));
float product = (l1[2] - l1[0])*(l2[2] - l2[0]) + (l1[3] - l1[1])*(l2[3] - l2[1]);
if (fabs(product / (length1 * length2)) < cos(CV_PI / 30))
return false;
float mx1 = (l1[0] + l1[2]) * 0.5f;
float mx2 = (l2[0] + l2[2]) * 0.5f;
float my1 = (l1[1] + l1[3]) * 0.5f;
float my2 = (l2[1] + l2[3]) * 0.5f;
float dist = sqrtf((mx1 - mx2)*(mx1 - mx2) + (my1 - my2)*(my1 - my2));
if (dist > std::max(length1, length2) * 0.5f)
return false;
return true;
}
Угадайте, что у вас есть свои строки в vector<Vec4i> lines;
. Затем вы должны вызвать cv::partition
следующим образом:
vector<Vec4i> lines;
std::vector<int> labels;
int numberOfLines = cv::partition(lines, labels, isEqual);
Вам нужно позвонить cv::partition
один раз, и он будет группировать все строки. Вектор labels
будет хранить для каждой метки строки кластера, к которой он принадлежит. См. документация для cv::partition
- После того, как вы получите все группы строк, вы должны объединить их. Я предлагаю вычислять средний угол всех линий в группе и оценивать "пограничные" точки. Например, если угол равен нулю (т.е. Все линии почти горизонтальны), это будут самые левые и самые правые точки. Остается только нарисовать линию между этими точками.
Я заметил, что все строки в ваших примерах являются горизонтальными или вертикальными. В таком случае вы можете вычислить точку, которая является средней для всех сегментных центров и "пограничных" точек, а затем просто нарисовать горизонтальную или вертикальную линию, ограниченную "пограничными" точками через центральную точку.
Обратите внимание, что cv::partition
занимает время O (N ^ 2), поэтому, если вы обрабатываете огромное количество строк, это может занять много времени.
Надеюсь, это поможет. Я использовал такой подход для аналогичной задачи.