表格线识别通用库文档
载入中...
搜索中...
未找到
lines.hpp
浏览该文件的文档.
1/*
2 * @Description: 线列表类 头文件及其实现
3 * @Version:
4 * @Autor: dreamy-xay
5 * @date: 2023-12-04
6 * @LastEditors: dreamy-xay
7 * @LastEditTime: 2024-06-06
8 */
9#ifndef COMMON_LINES_HPP
10#define COMMON_LINES_HPP
11
12#include <unordered_set>
13
16#include "common/base/line.hpp"
17#include "common/base/list.hpp"
18#include "common/base/rect.hpp"
19#include "common/enum.h"
20#include "common/macro.h"
21#include "common/type.h"
23#include "opencv2/core.hpp"
24
25namespace cm {
26
34class Lines : public List<Line> {
35 public:
36 using List<Line>::List;
37
39 Lines() = default;
41 Lines(const Lines& lines) = default;
42 Lines(const std::vector<cv::Vec4i>& lines);
43 Lines(const std::vector<std::vector<int>>& lines);
45 ~Lines() = default;
46
47 Lines& operator=(const std::vector<cv::Vec4i>& lines);
48 Lines& operator=(const std::vector<std::vector<int>>& lines);
49
50 Lines& RemoveLines(const std::unordered_set<size_t>& deleted_indexes);
51 Lines& RemoveLines(const std::unordered_set<Line, Line::Hash>& deleted_lines);
53
54 const Lines& ClassifyLines(Lines& hlines, Lines& vlines) const;
55 double LineLength(Statistic type, const Interval& x_range = Interval::All(), const Interval& y_range = Interval::All(), bool is_rough = false);
56
57 Line MergeLines(LineType line_type) const;
58
59 Rect Boundary(int margin = 0) const;
61 std::unordered_set<int> GetLinesIndexInLine(const Line& line, int threshold = 2, bool strict_inspect = true);
64 template <typename FUNC = Line::CompareByLength>
65 std::unordered_set<size_t> GetDuplicateLines(LineType line_type = ULINE, double threshold = 5, double min_overlap_ratio = 0.5, Statistic near_method = cm::MAXIMUM, const FUNC& func = Line::CompareByLength(true)) const;
66 template <typename FUNC = Line::CompareByLength>
67 std::unordered_set<size_t> GetAdjacentDuplicateLines(double threshold, double min_overlap_ratio = 0.5, Statistic near_method = cm::MAXIMUM, const FUNC& func = Line::CompareByLength(true)) const;
68 template <typename FUNC = Line::CompareByLength>
70 template <typename FUNC = Line::CompareByLength>
72};
73
87inline Lines::Lines(const std::vector<cv::Vec4i>& lines) {
88 for (auto& line : lines)
89 this->emplace_back(line[0], line[1], line[2], line[3]);
90}
91
105inline Lines::Lines(const std::vector<std::vector<int>>& lines) {
106 for (auto& line : lines)
107 this->emplace_back(line[0], line[1], line[2], line[3]);
108}
109
123inline Lines& Lines::operator=(const std::vector<cv::Vec4i>& lines) {
124 this->clear();
125 for (auto& line : lines)
126 this->emplace_back(line[0], line[1], line[2], line[3]);
127
128 return *this;
129}
130
144inline Lines& Lines::operator=(const std::vector<std::vector<int>>& lines) {
145 this->clear();
146 for (auto& line : lines)
147 this->emplace_back(line[0], line[1], line[2], line[3]);
148
149 return *this;
150}
151
169inline Lines& Lines::RemoveLines(const std::unordered_set<size_t>& deleted_indexes) {
170 // 记录 原始数组:
171 auto& this_lines = *this;
172 size_t num_lines = this_lines.size();
173
174 Lines new_lines; // 暂时存储不删的数据
175
176 for (size_t i = 0; i < num_lines; ++i)
177 if (!deleted_indexes.count(i))
178 new_lines.emplace_back(this_lines[i]);
179
180 this_lines = std::move(new_lines);
181
182 return this_lines;
183}
184
202inline Lines& Lines::RemoveLines(const std::unordered_set<Line, Line::Hash>& deleted_lines) {
203 // 记录原始列表:
204 auto& this_lines = *this;
205
207
208 for (auto& line : this_lines)
209 if (!deleted_lines.count(line))
210 new_lines.emplace_back(line);
211
212 this_lines = std::move(new_lines);
213
214 return this_lines;
215}
216
235 // 考虑先将 Lines 转变成 std::unordered_set<Line, Line::Hash>
236 std::unordered_set<Line, Line::Hash> lines_hash;
237 for (auto& line : deleted_lines)
238 lines_hash.insert(line);
239
240 // 调用重载的函数:
241 return this->RemoveLines(lines_hash);
242}
243
259 hlines.clear();
260 vlines.clear();
261
262 for (auto& line : *this) {
263 if (line.Type() == HLINE)
264 hlines.emplace_back(line);
265 else
266 vlines.emplace_back(line);
267 }
268 return *this;
269}
270
287inline double Lines::LineLength(Statistic type, const Interval& x_range, const Interval& y_range, bool is_rough) {
288 Lines effective_lines = std::move(this->Filter([&x_range, &y_range](const Line& line) {
289 return line.InInterval(x_range, y_range);
290 }));
291
292 switch (type) {
293 case AVERAGE:
294 return effective_lines.Reduce<double>([=](double sum, const Line& line) {
295 return sum + line.Length(is_rough);
296 }) / effective_lines.size();
297 case MAXIMUM:
298 return effective_lines.Max(Line::CompareByLength(is_rough))->Length(is_rough);
299 case MINIMUM:
300 return effective_lines.Min(Line::CompareByLength(is_rough))->Length(is_rough);
301 default:
302 throw Exception("The type(Statistic) of param \"type\" is incorrect!");
303 }
304}
305
320inline Line Lines::MergeLines(LineType line_type) const {
321 Line new_line; // 合并的新线
322
323 switch (line_type) {
324 case HLINE: // 如果是横线
325 new_line.pt1 = this->Min([](const Line& line1, const Line& line2) { return line1.pt1.x < line2.pt1.x; })->pt1;
326
327 new_line.pt2 = this->Max([](const Line& line1, const Line& line2) { return line1.pt2.x < line2.pt2.x; })->pt2;
328 break;
329 case VLINE: // 否则是竖线
330 new_line.pt1 = this->Min([](const Line& line1, const Line& line2) { return line1.pt1.y < line2.pt1.y; })->pt1;
331
332 new_line.pt2 = this->Max([](const Line& line1, const Line& line2) { return line1.pt2.y < line2.pt2.y; })->pt2;
333 break;
334 default:
335 throw Exception("The \"line type\" only supports \"cm::HLINE\" or \"cm::VLINE\" types!");
336 }
337
338 return std::move(new_line);
339}
340
352inline Rect Lines::Boundary(int margin) const {
355
356 for (const Line& line : *this) {
357 left_boundary = std::min(left_boundary, line.pt1.x);
358 right_boundary = std::max(right_boundary, line.pt2.x);
359 upper_boundary = std::min(upper_boundary, line.pt1.y);
360 lower_boundary = std::max(lower_boundary, line.pt2.y);
361 }
362
363 upper_boundary -= margin; // 根据 margin 向上扩充像素
364 lower_boundary += margin; // 根据 margin 向下扩充像素
365 left_boundary -= margin; // 根据 margin 向左扩充像素
366 right_boundary += margin; // 根据 margin 向右扩充像素
367
369}
370
384 return this->Count([&x_range, &y_range](const Line& line) {
385 return line.InInterval(x_range, y_range);
386 });
387}
388
406inline std::unordered_set<int> Lines::GetLinesIndexInLine(const Line& line, int threshold, bool strict_inspect) {
407 auto& lines = *this; // 自身作为线列表
408
409 // 获取线列表的长度
410 int lines_len = lines.size();
411
412 // 如果线列表为空则返回
413 if (lines_len == 0)
414 return std::unordered_set<int>();
415
416 // 断言查看所有线是否都是同一类型
417 Cm_Assert(lines.Filter([lines](const Line& line) { return line.Type() == lines[0].Type(); }).size() == lines.size(), "All lines must be of the same type!!!");
418
419 std::unordered_set<int> indexes; // 结果
420
421 // 计算线的斜率截距式方程
422 auto ge = line.GECoefficients();
423
424 if (line.Type() == VLINE) {
425 // 子线段最小的x坐标和最大的x坐标
426 int vline_min_x = line.X(MINIMUM) - threshold / 2;
427 int vline_max_x = line.X(MAXIMUM) + threshold / 2;
428
429 // 遍历所有直线
430 for (int i = 0; i < lines_len; ++i) {
431 if (lines[i].pt1.y < line.pt1.y || lines[i].pt2.y > line.pt2.y) // 如果线 lines[i] 在y坐标上不是被线 v_line 所包含的,则不做处理
432 continue;
433
434 // 开启严格检查模式后,检查x坐标范围:如果 lines[i] 在x坐标上不是被线 v_line 所包含的,则不做处理
435 if (strict_inspect && (lines[i].X(MINIMUM) < vline_min_x || lines[i].X(MAXIMUM) > vline_max_x))
436 continue;
437
438 int x1 = ge.GetX(lines[i].pt1.y); // 计算上端点y坐标在线 v_line 上的x坐标
439 int x2 = ge.GetX(lines[i].pt2.y); // 计算下端点y坐标在线 v_line 上的x坐标
440
441 // 如果线lines[i]的两个端点无限接近于线v_line,则表示线 lines[i] 属于线 v_line 的一部分
442 if (std::abs(x1 - lines[i].pt1.x) <= threshold && std::abs(x2 - lines[i].pt2.x) <= threshold)
443 indexes.emplace(i);
444 }
445 } else {
446 // 子线段最小的y坐标和最大的y坐标
447 int hline_min_y = line.Y(MINIMUM) - threshold / 2;
448 int hline_max_y = line.Y(MAXIMUM) + threshold / 2;
449
450 // 遍历所有直线
451 for (int i = 0; i < lines_len; ++i) {
452 if (lines[i].pt1.x < line.pt1.x || lines[i].pt2.x > line.pt2.x) // 如果线 lines[i] 在x坐标上不是被线 h_line 所包含的,则不做处理
453 continue;
454
455 // 开启严格检查模式后,检查x坐标范围:如果 lines[i] 在y坐标上不是被线 h_line 所包含的,则不做处理
456 if (strict_inspect && (lines[i].Y(MINIMUM) < hline_min_y || lines[i].Y(MAXIMUM) > hline_max_y))
457 continue;
458
459 int y1 = ge.GetY(lines[i].pt1.x); // 计算左端点x坐标在线 h_line 上的y坐标
460 int y2 = ge.GetY(lines[i].pt2.x); // 计算右端点x坐标在线 h_line 上的y坐标
461
462 // 如果线lines[i]的两个端点无限接近于线h_line,则表示线 lines[i] 属于线 h_line 的一部分
463 if (abs(y1 - lines[i].pt1.y) <= threshold && abs(y2 - lines[i].pt2.y) <= threshold)
464 indexes.emplace(i);
465 }
466 }
467
468 return std::move(indexes);
469}
470
491 auto& lines = *this; // 自身作为线列表
492
493 // 获取线列表的长度
494 int lines_len = lines.size();
495
496 // 如果线的数量小于2,则直接返回
497 if (lines_len < 2)
498 return lines;
499
500 if (line_type == HLINE || line_type == VLINE) {
501 // 断言查看所有线是否都是指定线类型
502 Cm_Assert(lines.Filter([line_type](const cm::Line& line) { return line.Type() == line_type; }).size() == lines.size(), Sprintf("The line type in the line list must all be \"%s\"!!!", line_type == HLINE ? "cm::HLINE" : "cm::VLINE"));
503
504 // 当前线计算距离的距离类型
506
507 // 使用并查集去计算能连接的线的集合
509
510 // 判断两根原始线长度是否满足连接需求的函数
511 bool (*old_len_ok)(const Line&, const Line&, const Interval&);
513 old_len_ok = [](const Line& line1, const Line& line2, const Interval& old_line_len_range) {
514 return old_line_len_range.Include(line1.Length()) && old_line_len_range.Include(line2.Length());
515 };
516 else
517 old_len_ok = [](const Line& line1, const Line& line2, const Interval& old_line_len_range) {
518 return old_line_len_range.Include(line1.Length()) || old_line_len_range.Include(line2.Length());
519 };
520
521 // 能相互连接的线合并成一个个集合
522 disjoint_set.UnionAll([&](const Line& line1, const Line& line2) {
523 // 如果两条短线小于最小线长度或者大于最大线长度,则不处理这两条短线
525 return false;
526
527 // 两条线合并成新的线
528 Line new_line = line1.SimpleConnect(line2);
529
530 // 如果新线的长度小于最小线长度或者大于最大线长度,则不处理该线
531 if (new_line.Type() != line1.Type() || !new_line_len_range.Include(new_line.Length()))
532 return false;
533
534 // 获取新线上所有子线段在线列表(线列表:线1和线2)上的索引
535 std::unordered_set<int> indexes = std::move(Lines({line1, line2}).GetLinesIndexInLine(new_line, threshold, strict_inspect));
536
537 // 如果线1和线2是新线的子线段(两条线是由新线分裂开来的),并且 两条线的距离 <= 线最大距离,则连接线1和线2并组成新的线
538 if (indexes.size() == 2 && line1.DistanceTo(line2, line_dis_type) <= max_distance)
539 return true;
540
541 return false;
542 });
543
544 // 获取线列表集合列表
545 auto lines_collections = std::move(disjoint_set.GetAllCollections());
546
547 // 清空原来横线
548 lines.clear();
549
550 // 遍历所有可合并线的集合
552 lines.emplace_back(static_cast<Lines&>(lines_collection).MergeLines(line_type));
553 } else if (line_type == ULINE) {
554 // 横线列表和竖线列表
556
557 // 分类横竖线
558 lines.ClassifyLines(hlines, vlines);
559
560 // 横线列表和竖线列表分别做线连接
563
564 // 合并线列表
565 lines.clear();
566 lines += hlines;
567 lines += vlines;
568 } else
569 throw Exception("The type(LineType) of param \"line_type\" is incorrect!");
570
571 return lines;
572}
573
597 auto& lines = *this; // 自身作为线列表
598
599 // 获取线列表的长度
600 int lines_len = lines.size();
601
602 // 如果线的数量小于2,则直接返回
603 if (lines_len < 2)
604 return lines;
605
606 // 断言查看所有线是否都是同一类型
607 Cm_Assert(lines.Filter([lines](const Line& line) { return line.Type() == lines[0].Type(); }).size() == lines.size(), "All lines must be of the same type!!!");
608
609 // 当前线计算距离的距离类型
611
612 // 临时变量,两条线合并的新线
614
615 // 构造新的线列表,首先添加前一根线进去
617 new_lines.emplace_back(lines[0]);
618
619 // 判断两根原始线长度是否满足连接需求的函数
620 bool (*old_len_ok)(const Line&, const Line&, const Interval&);
622 old_len_ok = [](const Line& line1, const Line& line2, const Interval& old_line_len_range) {
623 return old_line_len_range.Include(line1.Length()) && old_line_len_range.Include(line2.Length());
624 };
625 else
626 old_len_ok = [](const Line& line1, const Line& line2, const Interval& old_line_len_range) {
627 return old_line_len_range.Include(line1.Length()) || old_line_len_range.Include(line2.Length());
628 };
629
630 // 遍历所有相邻线
631 for (int i = 1; i < lines_len; ++i) {
632 // 判断是否合并线1和线2
633 const Line &line1 = new_lines.back(), line2 = lines[i];
634
635 // 如果两条短线小于最小线长度或者大于最大线长度,则不处理这两条短线
637 new_lines.emplace_back(line2); // 线2未与线1合并,单独加入线2
638 continue;
639 }
640
641 // 两条线合并成新的线
642 new_line = line1.SimpleConnect(line2);
643
644 // 如果新线的长度小于最小线长度或者大于最大线长度,则不处理该线
645 if (new_line.Type() != line1.Type() || !new_line_len_range.Include(new_line.Length())) {
646 new_lines.emplace_back(line2); // 线2未与线1合并,单独加入线2
647 continue;
648 }
649
650 // 获取新线上所有子线段在线列表(线列表:线1和线2)上的索引
651 std::unordered_set<int> indexes = std::move(Lines({line1, line2}).GetLinesIndexInLine(new_line, threshold, strict_inspect));
652
653 // 如果线1和线2是新线的子线端(两条线是由新线分裂开来的),并且 两条线的距离 <= 线最大距离,则连接线1和线2并组成新的线
654 if (indexes.size() == 2 && line1.DistanceTo(line2, line_dis_type) <= max_distance) {
655 new_lines.pop_back(); // 删除线1
656 new_lines.emplace_back(new_line); // 加入线1和线2合并的新线
657 } else
658 new_lines.emplace_back(line2); // 线2未与线1合并,单独加入线2
659 }
660
661 // 更新线列表
662 lines = std::move(new_lines);
663
664 return lines;
665}
666
686template <typename FUNC>
687inline std::unordered_set<size_t> Lines::GetDuplicateLines(LineType line_type, double threshold, double min_overlap_ratio, Statistic near_method, const FUNC& func) const {
688 auto& lines = *this; // 自身作为线列表
689
690 // 获取线列表的长度
691 const int lines_len = lines.size();
692
693 // 移除的线的索引
694 std::unordered_set<size_t> erase_lines_indexes;
695
696 // 如果线的数量小于2,则直接返回
697 if (lines_len < 2)
698 return erase_lines_indexes;
699
700 if (line_type == HLINE || line_type == VLINE) {
701 // 断言查看所有线是否都是指定线类型
702 Cm_Assert(lines.Filter([line_type](const cm::Line& line) { return line.Type() == line_type; }).size() == lines.size(), Sprintf("The line type in the line list must all be \"%s\"!!!", line_type == HLINE ? "cm::HLINE" : "cm::VLINE"));
703
704 // 新的线列表
706
707 // 当前线计算距离的距离类型
709
710 // 遍历所有线
711 for (size_t i = 0; i < lines_len; ++i) {
712 double line1_len = lines[i].Length(true);
713
714 // 遍历第 i 根线之后的线
715 for (size_t j = i + 1; j < lines_len; ++j)
716 // 如果两线类型相同,两线距离很近,两线重叠部分长度占最短线长(粗糙线长)的比率 >= 最小重叠比率,则删除重复线
717 if (lines[i].IsNear(lines[j], threshold, near_method) && lines[i].DistanceTo(lines[j], line_dis_type) <= -min_overlap_ratio * std::min(line1_len, lines[j].Length(true)))
718 erase_lines_indexes.emplace(func(lines[i], lines[j]) ? i : j); // func 函数返回 true,删除第一根横线,反之删除第二根横线
719 }
720 } else if (line_type == ULINE) {
721 // 横线列表和竖线列表
723
724 // 横竖线原始索引
725 std::vector<size_t> hlines_index, vlines_index;
726
727 // 分类横竖线
728 for (size_t i = 0; i < lines_len; ++i) {
729 auto& line = lines[i];
730 if (line.Type() == HLINE) {
731 hlines.emplace_back(line);
732 hlines_index.emplace_back(i);
733 } else {
734 vlines.emplace_back(line);
735 vlines_index.emplace_back(i);
736 }
737 }
738
739 // 横线列表和竖线列表分别获取重复线
740 std::unordered_set<size_t> erase_hlines_indexes = std::move(hlines.GetDuplicateLines(HLINE, threshold, min_overlap_ratio, near_method, func));
741 std::unordered_set<size_t> erase_vlines_indexes = std::move(vlines.GetDuplicateLines(VLINE, threshold, min_overlap_ratio, near_method, func));
742
743 // 获取重复横线索引
746
747 // 获取重复竖线索引
750 } else
751 throw Exception("The type(LineType) of param \"line_type\" is incorrect!");
752
753 return erase_lines_indexes;
754}
755
779template <typename FUNC>
780inline std::unordered_set<size_t> Lines::GetAdjacentDuplicateLines(double threshold, double min_overlap_ratio, Statistic near_method, const FUNC& func) const {
781 auto& lines = *this; // 自身作为线列表
782
783 // 获取线列表的长度
784 const int lines_len = lines.size();
785
786 // 移除的线的索引
787 std::unordered_set<size_t> erase_lines_indexes;
788
789 // 如果线的数量小于2,则直接返回
790 if (lines_len < 2)
791 return erase_lines_indexes;
792
793 // 断言查看所有线是否都是同一类型
794 Cm_Assert(lines.Filter([lines](const Line& line) { return line.Type() == lines[0].Type(); }).size() == lines.size(), "All lines must be of the same type!!!");
795
796 // 当前线计算距离的距离类型
798
799 // 遍历所有相邻线
800 for (size_t i = 1; i < lines_len; ++i)
801 // 如果两线类型相同,两线距离很近,两线重叠部分长度占最短线长(粗糙线长)的比率 >= 最小重叠比率,则判断为重复线
802 if (lines[i - 1].IsNear(lines[i], threshold, near_method) && lines[i - 1].DistanceTo(lines[i], line_dis_type) <= -min_overlap_ratio * std::min(lines[i - 1].Length(true), lines[i].Length(true)))
803 erase_lines_indexes.emplace(func(lines[i - 1], lines[i]) ? i - 1 : i); // func 函数返回 true,选择第一根横线,反之选择第二根横线
804
805 return erase_lines_indexes;
806}
807
827template <typename FUNC>
829 // 重复的线的索引
830 std::unordered_set<size_t> erase_lines_indexes = std::move(this->GetDuplicateLines(line_type, threshold, min_overlap_ratio, near_method, func));
831
832 // 删除重复线
833 return this->RemoveLines(erase_lines_indexes);
834}
835
859template <typename FUNC>
861 // 重复的线的索引
862 std::unordered_set<size_t> erase_lines_indexes = std::move(this->GetAdjacentDuplicateLines(threshold, min_overlap_ratio, near_method, func));
863
864 // 删除重复线
865 return this->RemoveLines(erase_lines_indexes);
866}
867
868} // namespace cm
869
870#endif
通用异常类
Definition type.h:39
区间类
Definition interval.hpp:29
static Interval All()
获取表示全范围的区间
Definition interval.hpp:493
线类
Definition line.hpp:31
线列表类
Definition lines.hpp:34
Lines & RemoveLines(const std::unordered_set< size_t > &deleted_indexes)
移除指定索引的线段
Definition lines.hpp:169
double LineLength(Statistic type, const Interval &x_range=Interval::All(), const Interval &y_range=Interval::All(), bool is_rough=false)
计算线数组中有效线的长度
Definition lines.hpp:287
const Lines & ClassifyLines(Lines &hlines, Lines &vlines) const
对线段进行分类
Definition lines.hpp:258
std::unordered_set< size_t > GetDuplicateLines(LineType line_type=ULINE, double threshold=5, double min_overlap_ratio=0.5, Statistic near_method=cm::MAXIMUM, const FUNC &func=Line::CompareByLength(true)) const
获取重复线段对应的索引
Definition lines.hpp:687
std::unordered_set< int > GetLinesIndexInLine(const Line &line, int threshold=2, bool strict_inspect=true)
获取线在线列表中的索引
Definition lines.hpp:406
Lines(const Lines &lines)=default
线列表类的拷贝构造函数
Lines & ConnectAdjacentLines(int threshold=4, int max_distance=100, bool strict_inspect=true, const Interval &old_line_len_range=Interval::All(), bool oline_len_condition=true, const Interval &new_line_len_range=Interval::All())
连接线列表的相邻线段
Definition lines.hpp:596
Line MergeLines(LineType line_type) const
合并线列表
Definition lines.hpp:320
Lines()=default
线列表类的默认构造函数
Lines & RmDuplicateLines(LineType line_type=ULINE, double threshold=5, double min_overlap_ratio=0.5, Statistic near_method=cm::MAXIMUM, const FUNC &func=Line::CompareByLength(true))
删除重复线
Definition lines.hpp:828
Lines & operator=(const std::vector< cv::Vec4i > &lines)
线列表类的赋值拷贝构造函数
Definition lines.hpp:123
std::unordered_set< size_t > GetAdjacentDuplicateLines(double threshold, double min_overlap_ratio=0.5, Statistic near_method=cm::MAXIMUM, const FUNC &func=Line::CompareByLength(true)) const
获取相邻重复线段对应的索引
Definition lines.hpp:780
Lines & RmAdjacentDuplicateLines(double threshold, double min_overlap_ratio=0.5, Statistic near_method=cm::MAXIMUM, const FUNC &func=Line::CompareByLength(true))
删除相邻的重复线
Definition lines.hpp:860
Rect Boundary(int margin=0) const
获取线列表的边界
Definition lines.hpp:352
cm::size_t NumberInInterval(const Interval &x_range, const Interval &y_range)
计算区间内的线段数量
Definition lines.hpp:383
~Lines()=default
线列表类的析构函数
Lines & ConnectLines(LineType line_type=ULINE, int threshold=4, int max_distance=100, bool strict_inspect=true, const Interval &old_line_len_range=Interval::All(), bool oline_len_condition=true, const Interval &new_line_len_range=Interval::All())
连接线列表的线段
Definition lines.hpp:490
列表类
Definition list.hpp:36
const_iterator Min() const
求列表中的最小值
Definition list.hpp:673
List< Line > Filter(const FUNC &func) const
过滤列表项
Definition list.hpp:270
size_t Count(const Line &value) const
统计列表中指定值的出现次数
Definition list.hpp:1221
List()=default
列表类的默认构造函数
const_iterator Max() const
求列表中的最大值
Definition list.hpp:572
点类
Definition point.hpp:52
T y
点的 y 坐标
Definition point.hpp:57
T x
点的 x 坐标
Definition point.hpp:55
double DistanceTo(const Point< U > &pt) const
计算点到点之间的距离
Definition point.hpp:410
矩形类
Definition rect.hpp:31
#define Cm_Assert(expr, message)
断言宏
Definition macro.h:109
std::string Sprintf(const char *format,...)
格式化输出字符串
Definition string.hpp:47
Statistic
统计方式枚举
Definition enum.h:70
@ AVERAGE
平均值
Definition enum.h:72
@ MINIMUM
最小值
Definition enum.h:76
@ MAXIMUM
最大值
Definition enum.h:74
LineDistanceType
线与线的间距类型枚举
Definition enum.h:32
@ LDIS_VLINE_V
竖线与竖线之间的垂直距离(可能为负数,为负数时表示两竖线在y坐标上相交)
Definition enum.h:40
@ LDIS_HLINE_H
横线与横线之间的水平距离(可能为负数,为负数时表示两横线在x坐标上相交)
Definition enum.h:38
LineType
线类型枚举
Definition enum.h:20
@ HLINE
横线 (horizontal line)
Definition enum.h:24
@ ULINE
未知线类型 (unknown line)
Definition enum.h:22
@ VLINE
竖线 (vertical line)
Definition enum.h:26
线的比较仿函数
Definition line.hpp:159