# LineCaliperOpenCV **Repository Path**: flyingtoad/LineCaliperOpenCV ## Basic Information - **Project Name**: LineCaliperOpenCV - **Description**: No description available - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-09 - **Last Updated**: 2026-04-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # LineCaliperOpenCV 基于 **OpenCV 4.8** 的线段卡尺(Line Caliper)测量工具,使用 **Steger 1D** 亚像素边缘检测。 一个算子 + 一个交互式 demo,直接平铺在根目录。 ``` LineCaliperOpenCV/ ├── CMakeLists.txt ├── LineCaliper.h # 算子头文件 ├── LineCaliper.cpp # 算子实现 ├── demo.cpp # 交互式演示 └── README.md ``` ## 依赖 - CMake ≥ 3.16 - OpenCV 4.8(默认路径 `C:/opencv4.8_vs2022`,其他位置改 `CMakeLists.txt` 里的 `OPENCV_ROOT`) - Visual Studio 2022(MSVC, C++17) ## 构建 ```bash cmake -B build -G "Visual Studio 17 2022" -A x64 cmake --build build --config Release --parallel ./build/bin/Release/demo.exe "C:/path/to/image.png" ``` 或双击 `build/LineCaliperOpenCV.sln` 用 VS 打开 → Release x64 → `demo` 设为启动项目 → Ctrl+F5。 ## Demo 操作 命令行传入图像路径(支持中文);无参数时使用源码里的 `kDefaultImagePath`。 **鼠标**:左键点两次定义搜索线段,自动运行卡尺。 **快捷键**: | 键 | 作用 | |---|---| | `r` | 重置 | | `1` / `2` / `3` | L2 / HUBER / RANSAC | | `p` | 极性 POSITIVE / NEGATIVE / ALL | | `c` | 选择策略 STRONGEST / FIRST / LAST | | `f` | 翻转扫描方向(内↔外) | | `+` / `-` | 卡尺数量 ±5 | | `[` / `]` | profileLength ±5 | | `,` / `.` | handleWidth ±1(投影积分降噪) | | `s` | 保存 result.png | | `q` / ESC | 退出 | **可视化**:黄线=搜索线,蓝框=卡尺采样区域,橙色箭头=扫描方向,绿点=亚像素边缘,红线=拟合结果。 --- ## 在自己的代码里使用 只需 `LineCaliper.h` + `LineCaliper.cpp` 两个文件,复制进你的项目即可。全部 API 在 `lc` 命名空间下。 ### 最简调用 ```cpp #include "LineCaliper.h" #include cv::Mat gray = cv::imread("workpiece.png", cv::IMREAD_GRAYSCALE); lc::LineCaliperConfig cfg; cfg.caliperCount = 25; cfg.profileLength = 40.0; cfg.handleWidth = 5.0; cfg.sigma = 1.5; cfg.threshold = 10.0; cfg.transition = lc::LineCaliperConfig::POSITIVE; // 暗→亮 cfg.select = lc::LineCaliperConfig::STRONGEST; std::vector edgePoints; auto result = lc::MeasureAndFitLine( gray, cv::Point2d(100, 280), cv::Point2d(700, 320), // 搜索线段 cfg, lc::LineFitMethod::RANSAC, &edgePoints); if (result) { std::cout << "angle = " << result->angleDeg << " deg\n" << "rms = " << result->rmsError << " px\n" << "inliers = " << result->numInliers << "/" << edgePoints.size() << "\n"; } ``` ### 分步调用(自定义过滤) ```cpp // 只测量 std::vector edges = lc::MeasureAlongLine(gray, p1, p2, cfg); // 自行过滤 std::vector pts; for (const auto& e : edges) { if (e.valid && std::abs(e.amplitude) > 20) pts.push_back(e.pos); } // 单独拟合 auto fit = lc::FitLine(pts, lc::LineFitMethod::HUBER); ``` ### 底层:单条 profile 的边缘检测 ```cpp auto profile = lc::ExtractProfile( gray, cv::Point2d(400, 300), // 中心 cv::Point2d(0.0, 1.0), // 扫描方向 40.0, // 长度 cv::Point2d(1.0, 0.0), // tangent(用于投影平均) 5.0); // 投影宽度 auto edge = lc::FindEdge1D_Steger( profile, 1.5, 10.0, lc::LineCaliperConfig::ALL, lc::LineCaliperConfig::STRONGEST); if (edge) { double subPixelIdx = edge->first; double amplitude = edge->second; } ``` --- ## 参数表 ```cpp struct LineCaliperConfig { int caliperCount = 20; // 卡尺数量 double profileLength = 40.0; // 扫描长度(像素) double handleWidth = 5.0; // 投影宽度(1=单线,N>1=平均 N 条降噪) double sigma = 1.5; // Gaussian 导数 sigma double threshold = 10.0; // 最小梯度幅值 Transition transition = ALL; // POSITIVE / NEGATIVE / ALL Select select = STRONGEST; // FIRST / LAST / STRONGEST bool flipDirection = false; // 反向扫描 }; struct LineFitResult { bool ok; cv::Point2d p0; // 线上一点 cv::Point2d dir; // 归一化方向 double rmsError; int numInliers; double angleDeg; cv::Point2d PointAt(double t) const; // p0 + t * dir }; enum class LineFitMethod { L2, HUBER, WELSCH, RANSAC }; ``` **经验法则**: - 噪声大 → 加 `handleWidth`(SNR 提升 ~√N) - 找错边 → 缩 `profileLength`、明确 `transition`、用 `FIRST`/`LAST` + `flipDirection` - 外点多 → 用 `RANSAC` 或 `HUBER` --- ## Steger 1D 算法 对 1D 灰度 profile `f(x)`: 1. 构造 1/2 阶 Gaussian 导数核:`g'(x) = -(x/σ²)g(x)`,`g''(x) = ((x²-σ²)/σ⁴)g(x)` 2. 卷积得到 `f'` 与 `f''` 3. 找 `|f'|` 局部极大(边缘候选) 4. 在极大点附近用 `f''` 的**零交叉点**做亚像素定位(线性插值) 相比抛物线拟合 `|f'|`,Steger 方法: - 利用 `f'` 极值处 `f''=0` 的数学关系,系统误差更小 - `f''` 已隐式平滑,对噪声更鲁棒 - 典型亚像素精度 < 0.03 px(1σ) **投影积分**:每个卡尺真实采样的是 `profileLength × handleWidth` 矩形,沿 tangent 方向取 `handleWidth` 条子线求平均,噪声方差按 `1/handleWidth` 衰减。