-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgetLine.h
121 lines (114 loc) · 3.54 KB
/
getLine.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#pragma once
#include <unordered_map>
#include "DisjSet.h"
#define PI 3.14159265358979323846
/// <summary>
/// 从矩阵集中搜索基准线
/// </summary>
/// <param name="vRect">待搜索矩阵集</param>
/// <param name="vLine">基准线集,以二维点集形式表示</param>
/// <param name="width">合并宽度,在这个距离内的两个矩形会被加入到一条线内</param>
void getLine(std::vector<cv::Rect>& vRect, std::vector<std::vector<cv::Point>>& vLine, const int width = 100) {
for (auto it = vRect.begin(); it < vRect.end();) {
if (it->area() < 200)it = vRect.erase(it); // 计算线段时忽略小矩形
else it++;
}
std::vector<cv::Rect>vY(vRect);
for (auto& it : vY) {
it.x += it.width / 2;
it.y += it.height / 2;
}
auto cmpY = [](const cv::Rect& a, const cv::Rect& b) {return a.y < b.y; };
sort(vRect.begin(), vRect.end(), cmpY);
sort(vY.begin(), vY.end(), cmpY);
int n = vRect.size();
DisjSet f(n);
int i = 0;
for (auto it : vY) {
//printf("\ni:%d ", i);
auto xl = std::lower_bound(vY.begin(), vY.end(), cv::Rect(0, it.y - it.height * 2 / 3, 0, 0), cmpY);
auto xr = std::upper_bound(vY.begin(), vY.end(), cv::Rect(0, it.y + it.height * 2 / 3, 0, 0), cmpY);
cv::Rect next;
int nexti, dMin = 8192;
for (auto jt = xl; jt < xr; jt++) {
if (jt->x > it.x) {
int d = jt->x - it.x + abs(jt->y - it.y); // 距离:暂取D4
if (d < dMin) {
dMin = d;
next = *jt;
nexti = jt - vY.begin();
//printf("+");
}
}
}
if (next.x > it.x && next.x - next.width / 2 < it.x + it.width / 2 + width) {
f.find(i); // 将连接的矩形用并查集记录
f.to_union(i, nexti);
//printf("next:%d", nexti);
}
i++;
}
std::unordered_map<int, int>map; // 将并查集中的点连成直线
int nLine = 0;
for (int i = 0; i < n; i++) {
if (f.find(i) == i) {
map[i] = nLine;
nLine++;
}
}
vLine.clear();
vLine.insert(vLine.begin(), nLine, std::vector<cv::Point>());
for (int i = 0; i < n; i++) {
vLine[map[f.find(i)]].emplace_back(vY[i].tl());
}
}
// 计算两个向量间角度(此处以点表示向量)
double vectorAngle(const cv::Point a, const cv::Point b) {
double dot = a.x * b.x + a.y * b.y;
double mold = sqrt(((double)a.x * a.x + a.y * a.y) * (b.x * b.x + b.y * b.y));
return acos(dot / mold) * 180 / PI;
}
// 进一步整理基准线集,剔除过短的连线,剔除拐角过大的连线
void sortLine(std::vector<std::vector<cv::Point>>& vLine) {
auto cmpX = [](const cv::Point& a, const cv::Point& b) {return a.x < b.x; };
auto cmpY = [](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b)
{return a.begin()->y < b.begin()->y; };
for (auto it = vLine.begin(); it < vLine.end();) {
if (it->size() < 5) {// 少于5个点的连线删去
it = vLine.erase(it);
continue;
}
sort(it->begin(), it->end(), cmpX);
if ((it->end() - 1)->x - it->begin()->x < 200)
it = vLine.erase(it); // 过短的连线删去
else it++;
}
for (auto it = vLine.begin(); it < vLine.end();) {
bool eraseFlag = 0;
for (auto jt = it->begin(); jt < it->end() - 2;) {
double angle = vectorAngle(*jt - *(jt + 1), *(jt + 1) - *(jt + 2));
if (angle > 45) { // 拐角超过45度的连线删去整条线
eraseFlag = 1;
break;
}
else if (angle > 30) { // 拐角超过30度的连线删去线上的一个点
it->erase((jt + 1));
jt = it->begin();
}
else jt++;
}
if (eraseFlag)it = vLine.erase(it);
else it++;
}
sort(vLine.begin(), vLine.end(), cmpY);
}
// 将连线集vLine画到图像src上
void drawLine(cv::Mat& src, const std::vector<std::vector<cv::Point>> vLine) {
int i = 0;
for (auto it : vLine) {
for (auto jt = it.begin(); jt < it.end() - 1; jt++) {
line(src, *jt, *(jt + 1), CV_RGB(255 - 5 * i, 5 * i, 5 * i), 2);
}
i++;
}
}