表格线识别通用库文档
载入中...
搜索中...
未找到
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 */
10
11namespace cm {
12/* 横线列表成员方法实现 */
13
31inline VLines::VLines(const std::initializer_list<Line>& vlines) {
32 this->assign(vlines.begin(), vlines.end());
33}
34
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
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
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
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 是否上对齐了最上方横线
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 是否下对齐了最下方横线
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
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;
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
347 auto& vlines = *this; // 竖线本身
348
349 // 如果横线或竖线数量为 0 则返回
350 if (hlines.empty() || vlines.empty())
351 return vlines;
352
353 // 新竖线列表
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
通用异常类
Definition type.h:39
横线列表类
Definition hlines.h:33
区间类
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
VLines & OptimizeIntersections(const HLines &hlines, size_t threshold=0)
交点优化
Definition vlines.cpp:255
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
int FindNearLine(int postion, RelativePosition rp=NEAR)
定位满足条件的线
Definition vlines.cpp:52
VLines & RmFreeVLines(const HLines &hlines, int threshold=2)
删除游离竖线
Definition vlines.cpp:346
VLines()=default
竖线列表类的默认构造函数
#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
Position
位置枚举
Definition enum.h:84
@ TOP
Definition enum.h:88
@ BOTTOM
Definition enum.h:92