表格线识别通用库文档
载入中...
搜索中...
未找到
vlines.cpp
浏览该文件的文档.
1
/*
2
* @Description: 竖线列表实现
3
* @Version:
4
* @Autor: LL
5
* @date: 2023-12-05
6
* @LastEditors: dreamy-xay
7
* @LastEditTime: 2024-06-06
8
*/
9
#include "
common/feature/vlines.h
"
10
11
namespace
cm
{
12
/* 横线列表成员方法实现 */
13
31
inline
VLines::VLines
(
const
std::initializer_list<Line>&
vlines
) {
32
this->
assign
(
vlines
.begin(),
vlines
.end());
33
}
34
52
inline
int
VLines::FindNearLine
(
int
postion
,
RelativePosition
rp
) {
53
auto
&
this_lines
= *
this
;
54
55
int
num_lines
=
this_lines
.size();
56
57
if
(
num_lines
== 0)
58
return
-1;
59
60
// 定位数组中最大的上下边界,超出此边界的位置必定不在线数组之中
61
int
upper
= std::max(
this_lines
[
num_lines
- 1].pt1.
x
,
this_lines
[
num_lines
- 1].pt2.
x
);
62
int
lower
= std::min(
this_lines
[0].pt1.
x
,
this_lines
[0].pt2.
x
);
63
if
(
postion
<
lower
||
num_lines
== 1)
// 越界
64
return
0;
65
else
if
(
postion
>
upper
)
// 最后一根线就是所要定位的最近的线,不需要考虑函数指定的是之前还是之后的那根线
66
return
num_lines
- 1;
67
68
// 遍历全部线,寻找首根线的位置大于所给位置 postion 的线
69
for
(
int
i
= 0;
i
<
num_lines
; ++
i
) {
70
int
this_distance
= (
this_lines
[
i
].pt1.
x
+
this_lines
[
i
].pt2.
x
) / 2 -
postion
;
71
if
(
this_distance
> 0) {
72
// 根据所所给的位置类型判定是要指定在这根线之前还是之后的线
73
if
(
i
== 0)
// 如果索引为零,就是索引为0的线,不需要考虑函数指定的是之前还是之后的那根线
74
return
0;
75
else
if
(
abs
(
this_distance
) >
abs
((
this_lines
[
i
- 1].pt1.
x
+
this_lines
[
i
- 1].pt2.
x
) / 2 -
postion
)) {
76
if
(
rp
==
NEAR
||
rp
==
BEFORE
)
77
return
i
- 1;
// 返回最近的线或者函数指定的后面面的线
78
else
79
return
i
;
// 返回函数指定的之前的线
80
}
else
{
81
if
(
rp
==
NEAR
||
rp
==
AFTER
)
82
return
i
;
// 返回最近的线或者函数指定的前面的线
83
else
84
return
i
- 1;
// 返回函数指定的之后的线
85
}
86
}
87
}
88
}
89
102
inline
int
VLines::FindNearLine
(
const
Line
&
line
,
RelativePosition
rp
) {
103
auto
&
this_lines
= *
this
;
104
int
num_lines
=
this_lines
.size();
105
if
(
num_lines
== 0)
106
return
-1;
107
108
int
upper
= std::max(
this_lines
[
num_lines
- 1].pt1.
x
,
this_lines
[
num_lines
- 1].pt2.
x
);
109
int
lower
= std::min(
this_lines
[0].pt1.
x
,
this_lines
[0].pt2.
x
);
110
111
int
postion
= (
line
.pt1.
x
+
line
.pt2.
x
) / 2;
// 寻找到所给线的位置,再用重载函数
112
113
if
(
postion
<
lower
||
num_lines
== 1)
// 越界
114
return
0;
115
else
if
(
postion
>
upper
)
116
return
num_lines
- 1;
117
118
return
this->
FindNearLine
(postion,
rp
);
119
}
120
146
VLines
&
VLines::ExtendLines
(
const
HLines
&
hlines
,
Position
extend_direction
,
size_t
start_vline_index
,
double
threshold
,
size_t
max_num_misaligned_lines
,
bool
strict_inspect
) {
147
Cm_Assert
(
extend_direction
==
TOP
||
extend_direction
==
BOTTOM
,
"The vertical line \"extend direction\" only supports \"cm::TOP\" or \"cm::BOTTOM\" types!"
);
148
149
auto
&
vlines
= *
this
;
150
151
int
num_hlines
=
hlines
.size();
152
int
num_vlines
=
vlines
.size();
153
154
// 如果横线数量小于2或者竖线列表数量小于2则返回
155
if
(
num_hlines
< 2 ||
num_vlines
< 2)
156
return
vlines
;
157
158
// 计算横线平均距离:
159
double
vlines_avg_len
= this->
Reduce
([](
double
sum
,
const
Line
&
line
) {
return
sum
+
line
.Length(); }, 0.0) /
num_vlines
;
160
161
Point2i
top_pt
,
bottom_pt
;
162
const
auto
&
first_hline
=
hlines
.front();
163
const
auto
&
last_hline
=
hlines
.back();
164
165
switch
(
extend_direction
) {
166
case
TOP
:
167
for
(
int
i
=
start_vline_index
;
i
<
num_vlines
; ++
i
) {
168
top_pt
=
first_hline
.Intersect(
vlines
[
i
]);
169
bottom_pt
=
last_hline
.Intersect(
vlines
[
i
]);
170
171
// 如果竖线两边都没有对齐边界,则直接不处理该竖线
172
if
(
strict_inspect
&& std::abs(
vlines
[
i
].pt1.
y
-
top_pt
.
y
) > 5 && std::abs(
bottom_pt
.
y
-
vlines
[
i
].pt2.
y
) > 5)
173
continue
;
174
175
// 如果竖线长度 < 平均竖线长度 * 阈值,则直接不处理该竖线
176
if
(
vlines
[
i
].Length() <
threshold
*
vlines_avg_len
)
177
continue
;
178
179
// 如果竖线未上对齐
180
if
(std::abs(
vlines
[
i
].pt1.
y
-
top_pt
.
y
) > 5) {
181
// 该竖线右侧是否存在竖线上对齐了最上方横线
182
bool
right_vline_is_aligned
=
false
;
183
184
// 查看在”最大允许连续未对齐的竖线的数量“的条件下,该竖线右侧是否存在竖线上对齐了最上方横线
185
for
(
int
j
= 1;
j
<=
max_num_misaligned_lines
&&
i
+
j
<
num_vlines
; ++
j
)
186
if
(
vlines
[
i
+
j
].IsAligned(
first_hline
,
TOP
)) {
// 查看竖线 i + j 是否上对齐了最上方横线
187
right_vline_is_aligned
=
true
;
188
break
;
189
}
190
191
// 如果当前竖线的右侧存在竖线上对齐了最上方横线,并且左侧竖线上对齐了最上方横线,则直接延伸该竖线
192
if
(
right_vline_is_aligned
&& (
i
< 1 ||
vlines
[
i
- 1].IsAligned(
first_hline
,
TOP
)))
193
vlines
[
i
].pt1.
y
=
top_pt
.
y
;
194
}
195
}
196
break
;
197
case
BOTTOM
:
198
for
(
int
i
=
start_vline_index
;
i
<
num_vlines
; ++
i
) {
199
top_pt
=
first_hline
.Intersect(
vlines
[
i
]);
200
bottom_pt
=
last_hline
.Intersect(
vlines
[
i
]);
201
202
// 如果竖线两边都没有对齐边界,则直接不处理该竖线
203
if
(
strict_inspect
&& std::abs(
vlines
[
i
].pt1.
y
-
top_pt
.
y
) > 5 && std::abs(
bottom_pt
.
y
-
vlines
[
i
].pt2.
y
) > 5)
204
continue
;
205
206
// 如果竖线长度 < 平均竖线长度 * 阈值,则直接不处理该竖线
207
if
(
vlines
[
i
].Length() <
threshold
*
vlines_avg_len
) {
208
continue
;
209
}
210
211
// 如果竖线未下对齐
212
if
(std::abs(
bottom_pt
.
y
-
vlines
[
i
].pt2.
y
) > 5) {
213
// 该竖线右侧是否存在竖线下对齐了最下方横线
214
bool
right_vline_is_aligned
=
false
;
215
216
// 查看在”最大允许连续未对齐的竖线的数量“的条件下,该竖线右侧是否存在竖线下对齐了最下方横线
217
for
(
int
j
= 1;
j
<=
max_num_misaligned_lines
&&
i
+
j
<
num_vlines
; ++
j
)
218
if
(
vlines
[
i
+
j
].IsAligned(
last_hline
,
BOTTOM
)) {
// 查看竖线 i + j 是否下对齐了最下方横线
219
right_vline_is_aligned
=
true
;
220
break
;
221
}
222
223
// 如果当前竖线的右侧存在竖线下对齐了最下方横线,并且左侧竖线下对齐了最下方横线,则直接延伸该竖线
224
if
(
right_vline_is_aligned
&& (
i
< 1 ||
vlines
[
i
- 1].IsAligned(
last_hline
,
BOTTOM
)))
225
vlines
[
i
].pt2.
y
=
bottom_pt
.
y
;
226
}
227
}
228
break
;
229
230
default
:
231
throw
Exception
(
"The horizontal line \"extend direction\" only supports \"cm::LEFT\" or \"cm::RIGHT\" types!"
);
232
}
233
}
234
255
VLines
&
VLines::OptimizeIntersections
(
const
HLines
&
hlines
,
size_t
threshold
) {
256
// 当前竖线列表
257
auto
&
vlines
= *
this
;
258
259
// 计算横竖线的数量
260
int
num_hlines
=
hlines
.size();
261
int
num_vlines
=
vlines
.size();
262
263
// 如果横线或竖线数量为 0 则返回
264
if
(
num_hlines
== 0 ||
num_vlines
== 0)
265
return
vlines
;
266
267
// 临时变量
268
Point2d
pt1, pt2;
269
Line
new_vline
;
270
271
// 新线列表
272
VLines
new_vlines
;
// 新竖线列表
273
274
// 优化所有竖线交点
275
for
(
auto
&
vline
:
vlines
) {
276
new_vline
=
vline
;
// 复制一份
277
278
// * 优化上端点
279
// 查找y坐标大于竖线上端点横线
280
size_t
index = std::upper_bound(
hlines
.begin(),
hlines
.end(),
vline
, [](
const
Line
&
line1
,
const
Line
&
line2
) { return line1.pt1.y < line2.Y(MINIMUM); }) -
hlines
.begin();
281
282
// 如果存在横线
283
if
(index <
num_hlines
) {
284
if
(index > 0) {
285
pt1 =
vline
.Intersect(
hlines
[index - 1]);
286
pt2 =
vline
.Intersect(
hlines
[index]);
287
288
// 相较于端点下方第一根横线,上端点和交点垂直距离
289
double
beyond_distance
= std::abs(pt2.
y
-
vline
.pt1.
y
);
290
291
if
(
threshold
)
// 垂直距离大于等于该阈值,则强制延长竖线到端点上方第一根横线,否则就近延长
292
new_vline
.pt1 =
beyond_distance
>=
threshold
? pt1 : (std::abs(
vline
.pt1.
y
- pt1.
y
) <=
beyond_distance
? pt1 : pt2);
293
else
// 就近延长
294
new_vline
.pt1 = std::abs(
vline
.pt1.
y
- pt1.
y
) <=
beyond_distance
? pt1 : pt2;
295
}
else
296
new_vline
.pt1 =
vline
.Intersect(
hlines
[index]);
297
}
else
// 如果不存在横线,即所有横线都在竖线上端点上边
298
new_vline
.pt1 =
vline
.Intersect(
hlines
.back());
299
300
// * 优化下端点
301
// 查找y坐标大于竖线下端点横线
302
index = std::upper_bound(
hlines
.begin(),
hlines
.end(),
vline
, [](
const
Line
&
line1
,
const
Line
&
line2
) { return line1.pt2.y < line2.Y(MINIMUM); }) -
hlines
.begin();
303
304
// 如果存在横线
305
if
(index <
num_hlines
) {
306
if
(index > 0) {
307
pt1 =
vline
.Intersect(
hlines
[index - 1]);
308
pt2 =
vline
.Intersect(
hlines
[index]);
309
310
// 相较于端点上方第一根横线,交点和下端点垂直距离
311
double
beyond_distance
= std::abs(
vline
.pt2.
y
- pt1.
y
);
312
313
if
(
threshold
)
// 垂直距离大于等于该阈值,则强制延长竖线到端点下方第一根横线,否则就近延长
314
new_vline
.pt2 =
beyond_distance
>=
threshold
? pt2 : (
beyond_distance
<= std::abs(pt2.
y
-
vline
.pt2.
y
) ? pt1 : pt2);
315
else
// 就近延长
316
new_vline
.pt2 =
beyond_distance
<= std::abs(pt2.
y
-
vline
.pt2.
y
) ? pt1 : pt2;
317
}
else
318
new_vline
.pt2 =
vline
.Intersect(
hlines
[index]);
319
320
}
else
// 如果不存在横线,即所有横线都在竖线下端点下边
321
new_vline
.pt2 =
vline
.Intersect(
hlines
.back());
322
323
// 加入新竖线
324
if
(!
new_vline
.IsPoint())
325
new_vlines
.emplace_back(
new_vline
);
326
}
327
328
// 更新竖线列表并返回
329
return
vlines
= std::move(
new_vlines
);
330
}
331
346
VLines
&
VLines::RmFreeVLines
(
const
HLines
&
hlines
,
int
threshold
) {
347
auto
&
vlines
= *
this
;
// 竖线本身
348
349
// 如果横线或竖线数量为 0 则返回
350
if
(
hlines
.empty() ||
vlines
.empty())
351
return
vlines
;
352
353
// 新竖线列表
354
VLines
new_vlines
;
355
356
// 遍历全部竖线
357
for
(
auto
&
vline
:
vlines
) {
358
Interval
y_range
(
vline
.pt1.
y
-
threshold
,
vline
.pt2.
y
+ 1 +
threshold
);
// 交点y坐标范围区间
359
auto
vline_ge
=
vline
.GECoefficients();
// 竖线一般方程
360
361
// 是否不是游离竖线
362
bool
is_not_free
=
false
;
363
364
// 遍历所有横线
365
for
(
auto
&
hline
:
hlines
) {
366
Interval
x_range
(
hline
.pt1.
x
-
threshold
,
hline
.pt2.
x
+ 1 +
threshold
);
// 交点x坐标范围区间
367
Point2d
intersection
=
hline
.Intersect(
vline_ge
);
// 横竖线交点
368
369
if
(
x_range
.Include(
intersection
.
x
) &&
y_range
.Include(
intersection
.
y
)) {
// 如果交点x和y坐标在范围内,表示横竖线是相交的
370
is_not_free
=
true
;
// 竖线不是游离竖线
371
break
;
372
}
373
}
374
375
// 如果竖线不是游离竖线,则加入新竖线列表
376
if
(
is_not_free
)
377
new_vlines
.emplace_back(
vline
);
378
}
379
380
// 更新竖线列表
381
vlines
= std::move(
new_vlines
);
382
383
return
vlines
;
384
}
385
386
}
// namespace cm
cm::Exception
通用异常类
Definition
type.h:39
cm::HLines
横线列表类
Definition
hlines.h:33
cm::Interval
区间类
Definition
interval.hpp:29
cm::Line
线类
Definition
line.hpp:31
cm::List< Line >::Reduce
U Reduce(const FUNC &func, U initial_value=U{}) const
累计列表项
Definition
list.hpp:322
cm::Point
点类
Definition
point.hpp:52
cm::Point::y
T y
点的 y 坐标
Definition
point.hpp:57
cm::Point::x
T x
点的 x 坐标
Definition
point.hpp:55
cm::VLines
Definition
vlines.h:25
cm::VLines::OptimizeIntersections
VLines & OptimizeIntersections(const HLines &hlines, size_t threshold=0)
交点优化
Definition
vlines.cpp:255
cm::VLines::ExtendLines
VLines & ExtendLines(const HLines &hlines, Position extend_direction, size_t start_vline_index=5, double threshold=0.7, size_t max_num_misaligned_lines=3, bool strict_inspect=true)
延长特殊竖线
Definition
vlines.cpp:146
cm::VLines::FindNearLine
int FindNearLine(int postion, RelativePosition rp=NEAR)
定位满足条件的线
Definition
vlines.cpp:52
cm::VLines::RmFreeVLines
VLines & RmFreeVLines(const HLines &hlines, int threshold=2)
删除游离竖线
Definition
vlines.cpp:346
cm::VLines::VLines
VLines()=default
竖线列表类的默认构造函数
Cm_Assert
#define Cm_Assert(expr, message)
断言宏
Definition
macro.h:109
cm
Definition
disjoint_set.hpp:22
cm::RelativePosition
RelativePosition
相对位置枚举
Definition
enum.h:100
cm::AFTER
@ AFTER
大于参考值中的最小值(在参考位置的后面)
Definition
enum.h:104
cm::BEFORE
@ BEFORE
小于参考值中的最大值(在参考位置的前面)
Definition
enum.h:102
cm::NEAR
@ NEAR
最接近参考值的值(最靠近参考位置,不区分前后)
Definition
enum.h:106
cm::Position
Position
位置枚举
Definition
enum.h:84
cm::TOP
@ TOP
上
Definition
enum.h:88
cm::BOTTOM
@ BOTTOM
下
Definition
enum.h:92
vlines.h
common
src
common
feature
vlines.cpp
制作者
1.10.0