-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathoutliers.cpp
171 lines (150 loc) · 4.71 KB
/
outliers.cpp
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <vector>
#include <cstdio>
#include <cmath>
#include <opencv2/core.hpp>
class node
{
public:
int line;
int group;
node* next = nullptr;
node(int line) : line(line) {}
};
bool closeEnough(cv::Vec4i l1, cv::Vec4i l2);
bool intersects(cv::Vec4i l1, cv::Vec4i l2);
int orientation(int x1, int y1, int x2, int y2, int x3, int y3);
void addEdge(std::vector<node*> &graph, int i, int j);
void floodfill(std::vector<node*> &graph, int line, bool visited[], int group);
void dealocGraph(std::vector<node*> &graph);
/*
* given a vector of lines, remove all lines that are not part of the biggest "clump"
*/
void eliminateOutliers(std::vector<cv::Vec4i> &lines)
{
// creates a graph with a vertice for every line
std::vector<node*> graph = std::vector<node*>(lines.size());
for (int i = 0; i < graph.size(); i++)
graph[i] = new node(i);
// adds an edge between every pair of lines that intersect
// or has points that are close enough
for (int i = 0; i < lines.size(); i++)
for (int j = i+1; j < lines.size(); j++)
if (intersects(lines[i], lines[j]) || closeEnough(lines[i], lines[j]))
addEdge(graph, i, j);
// uses a floodfill to divide the graph into groups of connected vertices
bool visited[graph.size()];
for (int i = 0; i < graph.size(); i++)
visited[i] = false;
int group = 0;
for (int i = 0; i < graph.size(); i++)
if (!visited[i]) {
floodfill(graph, i, visited, group);
group++;
}
// counts the number of items in each group and finds the group with the most
int groups[group];
for (int i = 0; i < group; i++)
groups[i] = 0;
for (int i = 0; i < graph.size(); i++)
groups[graph[i]->group]++;
int max = 0;
for (int i = 1; i < group; i++)
if (groups[i] > groups[max])
max = i;
// erases all lines that are not part of biggest group
for (int i = graph.size()-1; i >= 0; i--)
if (graph[i]->group != max)
lines.erase(lines.begin() + i);
// deallocate graph memory
dealocGraph(graph);
}
/*
* adds an edge to the graph
*/
void addEdge(std::vector<node*> &graph, int i, int j)
{
node* inode = graph[i];
while (inode->next != nullptr)
inode = inode->next;
inode->next = new node(j);
node* jnode = graph[j];
while (jnode->next != nullptr)
jnode = jnode->next;
jnode->next = new node(i);
}
/*
* checks if 2 line's points are close enough to have an edge
*/
bool closeEnough(cv::Vec4i l1, cv::Vec4i l2)
{
double d1 = sqrt(pow(l1[0]-l2[0], 2) + pow(l1[1]-l2[1], 2));
double d2 = sqrt(pow(l1[0]-l2[2], 2) + pow(l1[1]-l2[4], 2));
double d3 = sqrt(pow(l1[2]-l2[0], 2) + pow(l1[3]-l2[1], 2));
double d4 = sqrt(pow(l1[2]-l2[2], 2) + pow(l1[3]-l2[3], 2));
// ensures we're not dividing by 0
l1[0] += l1[0] == l1[2] ? 1 : 0;
l2[0] += l2[0] == l2[2] ? 1 : 0;
// if the lines are approximetly parallel, allow for further distance
double s1 = (l1[1]-l1[3])/(l1[0]-l1[2]);
double s2 = (l2[1]-l2[3])/(l2[0]-l2[2]);
if (s1 * 0.9 > s2 && s1 * 1.1 < s2)
return d1 < 12 || d2 < 12 || d3 < 12 || d4 < 12;
return d1 < 7 || d2 < 7 || d3 < 7 || d4 < 7;
}
/*
* checks if 2 lines intersect
*/
bool intersects(cv::Vec4i l1, cv::Vec4i l2) {
int o1 = orientation(l1[0], l1[1], l2[0], l2[1], l1[2], l1[3]); // p1 q1 p2
int o2 = orientation(l1[0], l1[1], l2[0], l2[1], l2[2], l2[3]); // p1 q1 q2
int o3 = orientation(l1[2], l1[3], l2[2], l2[3], l1[0], l1[1]); // p1 q1 p1
int o4 = orientation(l1[2], l1[3], l2[2], l2[3], l2[0], l2[1]); // p2 q2 q1
return o2 != o1 && o3 != o4;
}
/*
* returns orientation of 3 points given x and y coordinates
* 1 - clockwise
* 2 - counterclockwise
*/
int orientation(int x1, int y1, int x2, int y2, int x3, int y3)
{
// ensures we're not dividing by 0
x1 += x1 == x2 ? 1 : 0;
x3 += x3 == x2 ? 1 : 0;
double slope1 = (y1 - y2)/(x1 - x2);
double slope2 = (y2 - y3)/(x2 - x3);
return slope1 > slope2 ? 1 : 2;
}
/*
* finds all connected vertices
*/
void floodfill(std::vector<node*> &graph, int line, bool visited[], int group)
{
if (visited[line])
return;
visited[line] = true;
node* curr = graph[line];
curr->group = group;
while (curr->next)
{
curr = curr->next;
floodfill(graph, curr->line, visited, group);
}
}
/*
* dealocates all memory in graph
*/
void dealocGraph(std::vector<node*> &graph)
{
for (int i = 0; i < graph.size(); i++)
{
node* head = graph[i];
while (head->next != nullptr)
{
node* next = head->next;
delete head;
head = next;
}
delete head;
}
}