表格线识别通用库文档
载入中...
搜索中...
未找到
hlines.cpp
浏览该文件的文档.
1/*
2 * @Description: 横线列表实现
3 * @Version:
4 * @Autor: LL
5 * @date: 2023-12-05
6 * @LastEditors: dreamy-xay
7 * @LastEditTime: 2024-06-06
8 */
10
11namespace cm {
12/* 横线列表成员方法实现 */
13
31inline HLines::HLines(const std::initializer_list<Line>& hlines) {
32 this->assign(hlines.begin(), hlines.end());
33}
34
52 auto& this_lines = *this;
53
54 int num_lines = this_lines.size();
55
56 if (num_lines == 0)
57 return -1;
58
59 // 定位数组中最大的上下边界,超出此边界的位置必定不在线数组之中
60 int upper = std::max(this_lines[num_lines - 1].pt1.y, this_lines[num_lines - 1].pt2.y);
61 int lower = std::min(this_lines[0].pt1.y, this_lines[0].pt2.y);
62
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.y + this_lines[i].pt2.y) / 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.y + this_lines[i - 1].pt2.y) / 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
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.y, this_lines[num_lines - 1].pt2.y);
109 int lower = std::min(this_lines[0].pt1.y, this_lines[0].pt2.y);
110
111 int postion = (line.pt1.y + line.pt2.y) / 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
138 auto& lines = *this;
139
140 int num_lines = this->size();
141
142 if (num_lines < 2)
143 return 0;
144
145 // 更新 range
146 index_range = index_range.Intersect({0, num_lines});
147
148 // 计算平均行高
149 if(is_order)
150 return (lines[index_range.end - 1].Y(AVERAGE) - lines[index_range.start].Y(AVERAGE)) / (index_range.Length() - 1);
151 else
152 return (lines.Order(index_range.end - 1, Line::CompareByCoordinate(cm::HLINE, 0))->Y(AVERAGE) - lines.Order(index_range.start, Line::CompareByCoordinate(cm::HLINE, 0))->Y(AVERAGE)) / (index_range.Length() - 1);
153
154}
155
182 Cm_Assert(extend_direction == LEFT || extend_direction == RIGHT, "The horizontal line \"extend direction\" only supports \"cm::LEFT\" or \"cm::RIGHT\" types!");
183
184 auto& hlines = *this;
185
186 int num_hlines = hlines.size();
187 int num_vlines = vlines.size();
188
189 // 如果横线数量小于2或者竖线列表数量小于2则返回
190 if (num_hlines < 2 || num_vlines < 2)
191 return hlines;
192
193 // 计算横线平均距离:
194 double hlines_avg_len = this->Reduce([](double sum, const Line& line) { return sum + line.Length(); }, 0.0) / num_hlines;
195
197 const auto& first_vline = vlines.front();
198 const auto& last_vline = vlines.back();
199
200 switch (extend_direction) {
201 case LEFT:
202 for (int i = start_hline_index; i < num_hlines; ++i) {
203 left_pt = first_vline.Intersect(hlines[i]);
204 right_pt = last_vline.Intersect(hlines[i]);
205
206 // 如果横线两边都没有对齐边界,则直接不处理该横线
207 if (strict_inspect && std::abs(hlines[i].pt1.x - left_pt.x) > 5 && std::abs(right_pt.x - hlines[i].pt2.x) > 5)
208 continue;
209
210 // 如果横线长度 < 平均横线长度 * 阈值,则直接不处理该横线
211 if (hlines[i].Length() < threshold * hlines_avg_len)
212 continue;
213
214 // 如果横线未左对齐
215 if (std::abs(hlines[i].pt1.x - left_pt.x) > 5) {
216 // 该横线下方是否存在横线左对齐了最左侧竖线
217 bool bottom_hline_is_aligned = false;
218
219 // 查看在”最大允许连续未对齐的横线的数量“的条件下,该横线下方是否存在横线左对齐了最左侧竖线
220 for (int j = 1; j <= max_num_misaligned_lines && i + j < num_hlines; ++j)
221 if (hlines[i + j].IsAligned(first_vline, LEFT)) { // 查看横线 i + j 是否左对齐了最左侧竖线
223 break;
224 }
225
226 // 如果当前横线的下方存在横线左对齐了最左侧竖线,并且上边横线左对齐于最左侧竖线,则直接延伸该横线
227 if (bottom_hline_is_aligned && (i < 1 || hlines[i - 1].IsAligned(first_vline, LEFT)))
228 hlines[i].pt1.x = left_pt.x;
229 }
230 }
231 break;
232 case RIGHT:
233 for (int i = start_hline_index; i < num_hlines; ++i) {
234 left_pt = first_vline.Intersect(hlines[i]);
235 right_pt = last_vline.Intersect(hlines[i]);
236
237 // 如果横线两边都没有对齐边界,则直接不处理该横线
238 if (strict_inspect && std::abs(hlines[i].pt1.x - left_pt.x) > 5 && std::abs(right_pt.x - hlines[i].pt2.x) > 5)
239 continue;
240
241 // 如果横线长度 < 平均横线长度 * 阈值,则直接不处理该横线
242 if (hlines[i].Length() < threshold * hlines_avg_len)
243 continue;
244
245 // 如果横线未右对齐
246 if (std::abs(right_pt.x - hlines[i].pt2.x) > 5) {
247 // 该横线下方是否存在横线右对齐了最右侧竖线
248 bool bottom_hline_is_aligned = false;
249
250 // 查看在”最大允许连续未对齐的横线的数量“的条件下,该横线下方是否存在横线右对齐了最右侧竖线
251 for (int j = 1; j <= max_num_misaligned_lines && i + j < num_hlines; ++j)
252 if (hlines[i + j].IsAligned(last_vline, RIGHT)) { // 查看横线 i + j 是否右对齐了最右侧竖线
254 break;
255 }
256
257 // 如果当前横线的下方存在横线右对齐了最右侧竖线,并且上边横线右对齐于最右侧竖线,则直接延伸该横线
258 if (bottom_hline_is_aligned && (i < 1 || hlines[i - 1].IsAligned(last_vline, RIGHT)))
259 hlines[i].pt2.x = right_pt.x;
260 }
261 }
262 break;
263
264 default:
265 throw Exception("The horizontal line \"extend direction\" only supports \"cm::LEFT\" or \"cm::RIGHT\" types!");
266 }
267
268 return hlines;
269}
270
292 // 当前横线列表
293 auto& hlines = *this;
294
295 // 计算横竖线的数量
296 int num_hlines = hlines.size();
297 int num_vlines = vlines.size();
298
299 // 如果横线或竖线数量为 0 则返回
300 if (num_hlines == 0 || num_vlines == 0)
301 return hlines;
302
303 // 临时变量
304 Point2d pt1, pt2;
306
307 // 新线列表
308 HLines new_hlines; // 新横线列表
309
310 // 优化所有横线交点
311 for (auto& hline : hlines) {
312 new_hline = hline; // 复制一份
313
314 // * 优化左端点
315 // 查找x坐标大于横线左端点竖线
316 size_t index = std::upper_bound(vlines.begin(), vlines.end(), hline, [](const Line& line1, const Line& line2) { return line1.pt1.x < line2.X(MINIMUM); }) - vlines.begin();
317
318 // 如果存在竖线
319 if (index < num_vlines) {
320 if (index > 0) {
321 pt1 = hline.Intersect(vlines[index - 1]);
322 pt2 = hline.Intersect(vlines[index]);
323
324 // 相较于端点右侧第一根竖线,交点和左端点水平距离
325 double beyond_distance = std::abs(pt2.x - hline.pt1.x);
326
327 if (threshold) // 水平距离大于等于该阈值,则强制延长横线到端点右侧第一根竖线,否则就近延长
328 new_hline.pt1 = beyond_distance >= threshold ? pt1 : (std::abs(hline.pt1.x - pt1.x) <= beyond_distance ? pt1 : pt2);
329 else // 就近延长
330 new_hline.pt1 = std::abs(hline.pt1.x - pt1.x) <= beyond_distance ? pt1 : pt2;
331 } else
332 new_hline.pt1 = hline.Intersect(vlines[index]);
333 } else // 如果不存在竖线,即所有竖线都在横线左端点左边
334 new_hline.pt1 = hline.Intersect(vlines.back());
335
336 // * 优化右端点
337 // 查找x坐标大于横线右端点竖线
338 index = std::upper_bound(vlines.begin(), vlines.end(), hline, [](const Line& line1, const Line& line2) { return line1.pt2.x < line2.X(MINIMUM); }) - vlines.begin();
339
340 // 如果存在竖线
341 if (index < num_vlines) {
342 if (index > 0) {
343 pt1 = hline.Intersect(vlines[index - 1]);
344 pt2 = hline.Intersect(vlines[index]);
345
346 // 相较于端点左侧第一根竖线,交点和右端点水平距离
347 double beyond_distance = std::abs(hline.pt2.x - pt1.x);
348
349 if (threshold) // 水平距离大于等于该阈值,则强制延长横线到端点左侧第一根竖线,否则就近延长
350 new_hline.pt2 = beyond_distance >= threshold ? pt2 : (beyond_distance <= std::abs(pt2.x - hline.pt2.x) ? pt1 : pt2);
351 else // 就近延长
352 new_hline.pt2 = beyond_distance <= std::abs(pt2.x - hline.pt2.x) ? pt1 : pt2;
353 } else
354 new_hline.pt2 = hline.Intersect(vlines[index]);
355 } else // 如果不存在竖线,即所有竖线都在横线右端点左边
356 new_hline.pt2 = hline.Intersect(vlines.back());
357
358 // 加入新横线
359 if(!new_hline.IsPoint())
360 new_hlines.emplace_back(new_hline);
361 }
362
363 // 更新横线列表并返回
364 return hlines = std::move(new_hlines);
365}
366
382 auto& hlines = *this; // 横线本身
383
384 // 如果横线或竖线数量为 0 则返回
385 if (hlines.empty() || vlines.empty())
386 return hlines;
387
388 // 新横线列表
390
391 // 遍历全部横线
392 for (auto& hline : hlines) {
393 cm::Interval x_range(hline.pt1.x - threshold, hline.pt2.x + 1 + threshold); // 交点x坐标范围区间
394 auto hline_ge = hline.GECoefficients(); // 横线一般方程
395
396 // 是否不是游离横线
397 bool is_not_free = false;
398
399 // 遍历所有竖线
400 for (auto& vline : vlines) {
401 Interval y_range(vline.pt1.y - threshold, vline.pt2.y + 1 + threshold); // 交点y坐标范围区间
402 Point2d intersection = vline.Intersect(hline_ge); // 横竖线交点
403
404 if (x_range.Include(intersection.x) && y_range.Include(intersection.y)) { // 如果交点x和y坐标在范围内,表示横竖线是相交的
405 is_not_free = true; // 横线不是游离横线
406 break;
407 }
408 }
409
410 // 如果横线不是游离横线,则加入新横线列表
411 if (is_not_free)
412 new_hlines.emplace_back(hline);
413 }
414
415 // 更新横线列表
416 hlines = std::move(new_hlines);
417
418 return hlines;
419}
420
421} // namespace cm
通用异常类
Definition type.h:39
横线列表类
Definition hlines.h:33
HLines & ExtendLines(const VLines &vlines, Position extend_direction, size_t start_hline_index=5, double threshold=0.7, size_t max_num_misaligned_lines=3, bool strict_inspect=true)
延长特殊横线
Definition hlines.cpp:181
HLines()=default
横线列表类的默认构造函数
double AvgHeight(Interval index_range=Interval::All(), bool is_order=true)
计算给定索引范围内横线的平均行高
Definition hlines.cpp:137
HLines & RmFreeHLines(const VLines &vlines, int threshold=2)
删除游离横线
Definition hlines.cpp:381
int FindNearLine(int postion, RelativePosition rp=NEAR)
定位满足条件的线
Definition hlines.cpp:51
HLines & OptimizeIntersections(const VLines &vlines, size_t threshold=0)
交点优化
Definition hlines.cpp:291
区间类
Definition interval.hpp:29
线类
Definition line.hpp:31
U Reduce(const FUNC &func, U initial_value=U{}) const
累计列表项
Definition list.hpp:322
点类
Definition point.hpp:52
T y
点的 y 坐标
Definition point.hpp:57
T x
点的 x 坐标
Definition point.hpp:55
#define Cm_Assert(expr, message)
断言宏
Definition macro.h:109
RelativePosition
相对位置枚举
Definition enum.h:100
@ AFTER
大于参考值中的最小值(在参考位置的后面)
Definition enum.h:104
@ BEFORE
小于参考值中的最大值(在参考位置的前面)
Definition enum.h:102
@ NEAR
最接近参考值的值(最靠近参考位置,不区分前后)
Definition enum.h:106
@ AVERAGE
平均值
Definition enum.h:72
Position
位置枚举
Definition enum.h:84
@ RIGHT
Definition enum.h:90
@ LEFT
Definition enum.h:86
@ HLINE
横线 (horizontal line)
Definition enum.h:24
线的比较仿函数
Definition line.hpp:190