跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++AI算法

SGBM 半全局块匹配算法流程详解

SGBM 算法通过多路径动态规划近似全局能量最小化,实现密集视差估计。核心流程涵盖预处理、代价计算、聚合、视差提取及后处理。 Sobel 滤波、BT/Census 代价体构建、DP 聚合策略及左右一致性检查等关键步骤,提供 OpenCV 参数调优与工程优化技巧,适用于自动驾驶、机器人导航等深度感知场景。

DotNetGuy发布于 2026/3/24更新于 2026/6/2123 浏览
SGBM 半全局块匹配算法流程详解

SGBM 半全局块匹配算法流程详解

1. 算法概述

1.1 SGBM 的本质

SGBM (Semi-Global Block Matching) 是 Heiko Hirschmüller 提出的 SGM 算法的工程化实现,核心思想是通过多路径动态规划近似全局能量最小化。

  • 全局匹配: 考虑整张图像的一致性约束
  • 半全局近似: 用多个 1D 路径代替 2D 全局优化,降低复杂度
  • 块匹配: 结合局部块匹配的鲁棒性

1.2 算法复杂度

  • 时间复杂度: O(W × H × D × P)
    • W, H: 图像宽高
    • D: 视差搜索范围 (numDisparities)
    • P: 路径数量 (通常 5 或 8)
  • 空间复杂度: O(W × H × D)

2. 完整算法流程

输入:左右校正图像 预处理:Sobel 滤波 代价计算:BT/Census 代价聚合:多路径 DP 视差计算:WTA + 亚像素 后处理:一致性检查

流程概览:

步骤输入输出核心操作OpenCV 参数
1. 预处理左右图像滤波后图像Sobel 梯度preFilterCap
2. 代价计算滤波图像代价体 C(x,y,d)BT 距离/CensusblockSize
3. 代价聚合代价体聚合代价 S(x,y,d)多路径 DPP1, P2
4. 视差计算聚合代价粗视差图WTAnumDisparities
5. 后处理粗视差图精细视差图滤波uniquenessRatio

3. 步骤 1: 预处理 (Pre-processing)

3.1 目的

  • 减少光照影响: 归一化亮度差异
  • 增强纹理: 提升边缘和细节的匹配可靠性

3.2 Sobel 预滤波

OpenCV 对左右图像分别进行水平方向的 Sobel 梯度计算:

I'(x, y) = clip(∂I(x,y)/∂x, -preFilterCap, +preFilterCap)

数学展开: ∂I/∂x ≈ I(x+1, y) - I(x-1, y)

代码等效实现:

void preFilterXSobel(const cv::Mat& src, cv::Mat& dst, int preFilterCap) {
    cv::Sobel(src, dst, CV_16S, 1, 0, 3); // dx=1, dy=0, ksize=3
    // 截断到 [-preFilterCap, +preFilterCap]
    for (int y = 0; y < dst.rows; y++) {
        for (int x = 0; x < dst.cols; x++) {
            short val = dst.at<short>(y, x);
            dst.at<short>(y, x) = cv::saturate_cast<short>(std::max(-preFilterCap, std::min(preFilterCap, (int)val)));
        }
    }
}

3.3 参数影响

  • preFilterCap = 31: 适用于室内、光照均匀场景
  • preFilterCap = 63: 适用于室外、光照变化大的场景
  • 过小 → 梯度信息不足;过大 → 噪声增加

4. 步骤 2: 代价计算 (Cost Computation)

4.1 代价体构建

构建 3D 代价体 (Cost Volume): C : R^(W×H×D) where D = numDisparities

对于每个像素 (x, y) 和视差 d,计算左右图像的匹配代价。

4.2 方法 1: BT (Birchfield-Tomasi) 距离

核心思想: 考虑亚像素采样,减少离散化误差。

公式: C_BT(x, y, d) = (1/N) * Σ_(i,j)∈W ρ(I_L(x+i, y+j), I_R(x+i-d, y+j))

其中 ρ 为鲁棒代价函数: ρ(a, b) = min(|a - b|, |a - b^-|, |a - b^+|) b^- = (I_R(x-d) + I_R(x-d-1))/2, b^+ = (I_R(x-d) + I_R(x-d+1))/2

伪代码:

float computeBT(int x, int y, int d, const Mat& left, const Mat& right) {
    float cost = 0;
    int halfBlock = blockSize / 2;
    for (int dy = -halfBlock; dy <= halfBlock; dy++) {
        for (int dx = -halfBlock; dx <= halfBlock; dx++) {
            int xl = x + dx;
            int yl = y + dy;
            int xr = xl - d;
            if (xr < 0 || xr >= right.cols) continue;
            float IL = left.at<uchar>(yl, xl);
            float IR = right.at<uchar>(yl, xr);
            float IR_minus = (right.at<uchar>(yl, xr) + right.at<uchar>(yl, xr - 1)) / 2.0;
            float IR_plus = (right.at<uchar>(yl, xr) + right.at<uchar>(yl, xr + 1)) / 2.0;
            float diff = std::min({std::abs(IL - IR), std::abs(IL - IR_minus), std::abs(IL - IR_plus)});
            cost += diff;
        }
    }
    return cost / ((blockSize * blockSize));
}

4.3 方法 2: Census 变换

更鲁棒于光照变化,OpenCV 在某些模式下使用。

步骤:

  1. 对每个像素的邻域进行二值化编码
  2. 使用 Hamming 距离比较左右图像的 Census 串

公式: ξ(x, y) = ⊗_(i,j)∈W 1_I(x+i, y+j) > I(x, y) C_Census(x, y, d) = Hamming(ξ_L(x, y), ξ_R(x-d, y))

示例 (9×9 窗口):

uint64_t computeCensus(int x, int y, const Mat& img) {
    uint64_t census = 0;
    uchar center = img.at<uchar>(y, x);
    for (int dy = -4; dy <= 4; dy++) {
        for (int dx = -4; dx <= 4; dx++) {
            if (dx == 0 && dy == 0) continue;
            uchar neighbor = img.at<uchar>(y + dy, x + dx);
            census <<= 1;
            if (neighbor > center) census |= 1;
        }
    }
    return census;
}

int hammingDistance(uint64_t a, uint64_t b) {
    return __builtin_popcountll(a ^ b); // 硬件加速
}

4.4 代价体示意图

代价体 C[x, y, d]: d (视差) ↓ 0 1 2 ... 63 x=0 [12, 45, 78, ..., 23] x=1 [34, 12, 56, ..., 89] ...

每个 C(x, y, d) 存储像素 (x, y) 在视差 d 下的匹配代价。

5. 步骤 3: 代价聚合 (Cost Aggregation)

5.1 核心思想

这是 SGBM 的灵魂步骤。通过多个方向的路径进行动态规划,将局部代价聚合成考虑全局约束的代价。

5.2 单路径动态规划公式

对于路径方向 r,递推计算累积代价: L_r(p, d): 沿路径 r 到像素 p 视差为 d 的最小累积代价 p-r: 路径 r 方向上的前一个像素 C(p, d): 当前像素的原始匹配代价 P1: 视差变化 ±1 的惩罚(平滑项) P2: 视差大幅变化的惩罚(断裂项) 最后的归一化项防止数值溢出

5.3 路径方向

OpenCV SGBM 支持多种模式:

模式路径数量方向角度计算量质量
MODE_SGBM50°, 45°, 90°, 135°, 垂直加强低中
MODE_HH8全方向高高
MODE_SGBM_3WAY83 方向×3 遍历中较高

8 方向示意图: 135° 90° 45° ↖ ↑ ↗ 180° ← [p] → 0° ↙ ↓ ↘ 225° 270° 315°

5.4 动态规划详细过程

以水平方向 (0°) 为例:

void aggregateCostPath(const Mat& cost, Mat& L, int direction, int P1, int P2) {
    int rows = cost.rows;
    int cols = cost.cols;
    int dispRange = cost.size[2]; // 视差范围
    // 方向向量
    int dx = direction_dx[direction]; // 例如:0° → dx=1, dy=0
    int dy = direction_dy[direction];
    // 从路径起点开始扫描
    for (int y = 0; y < rows; y++) {
        for (int x = 0; x < cols; x++) {
            int prev_x = x - dx;
            int prev_y = y - dy;
            if (prev_x < 0 || prev_x >= cols || prev_y < 0 || prev_y >= rows) {
                // 路径起点,直接复制代价
                for (int d = 0; d < dispRange; d++) {
                    L.at<float>(y, x, d) = cost.at<float>(y, x, d);
                }
                continue;
            }
            // 找到前一个像素的最小累积代价
            float prev_min = FLT_MAX;
            for (int d = 0; d < dispRange; d++) {
                prev_min = std::min(prev_min, L.at<float>(prev_y, prev_x, d));
            }
            // 对当前像素的每个视差进行 DP
            for (int d = 0; d < dispRange; d++) {
                float C_curr = cost.at<float>(y, x, d);
                // 4 种情况的最小值
                float cost0 = L.at<float>(prev_y, prev_x, d); // 无变化
                float cost1 = (d > 0) ? L.at<float>(prev_y, prev_x, d - 1) + P1 : FLT_MAX; // +1
                float cost2 = (d < dispRange - 1) ? L.at<float>(prev_y, prev_x, d + 1) + P1 : FLT_MAX; // -1
                float cost3 = prev_min + P2; // 大变化
                float min_cost = std::min({cost0, cost1, cost2, cost3});
                // 归一化
                L.at<float>(y, x, d) = C_curr + min_cost - prev_min;
            }
        }
    }
}

5.5 多路径聚合

将所有路径的累积代价求和: S(p, d) = Σ_r=1^P L_r(p, d)

void aggregateAllPaths(const Mat& cost, Mat& S, int P1, int P2) {
    int numPaths = 8; // 或 5
    // 初始化聚合代价为 0
    S = Mat::zeros(cost.size(), CV_32F);
    // 对每个方向进行聚合
    for (int r = 0; r < numPaths; r++) {
        Mat L_r;
        aggregateCostPath(cost, L_r, r, P1, P2);
        // 累加到总代价
        S += L_r;
    }
}

5.6 自适应惩罚系数

在图像边缘处降低 P2,允许视差断裂: P2_adaptive(p, p-r) = P2 / max(|∇I_p|, |∇I_{p-r}|, ε)

float getAdaptiveP2(int x, int y, int prev_x, int prev_y, const Mat& gradX, const Mat& gradY, float P2_base) {
    float grad1 = std::abs(gradX.at<short>(y, x)) + std::abs(gradY.at<short>(y, x));
    float grad2 = std::abs(gradX.at<short>(prev_y, prev_x)) + std::abs(gradY.at<short>(prev_y, prev_x));
    float maxGrad = std::max({grad1, grad2, 1.0f});
    return P2_base / maxGrad;
}

6. 步骤 4: 视差计算 (Disparity Computation)

6.1 WTA (Winner-Takes-All) 策略

选择累积代价最小的视差作为最终视差: D(x, y) = argmin_d∈[0, D_max] S(x, y, d)

cv::Mat computeDisparityWTA(const Mat& S) {
    int rows = S.rows;
    int cols = S.cols;
    int dispRange = S.size[2];
    Mat disparity(rows, cols, CV_16S);
    for (int y = 0; y < rows; y++) {
        for (int x = 0; x < cols; x++) {
            float minCost = FLT_MAX;
            int bestDisp = 0;
            for (int d = 0; d < dispRange; d++) {
                float cost = S.at<float>(y, x, d);
                if (cost < minCost) {
                    minCost = cost;
                    bestDisp = d;
                }
            }
            disparity.at<short>(y, x) = bestDisp * 16; // OpenCV 用 16 倍存储
        }
    }
    return disparity;
}

6.2 亚像素增强 (Subpixel Refinement)

通过二次曲线拟合提升精度到 1/16 像素。

数学原理: 假设代价函数在最优视差附近呈二次曲线: S(d) ≈ a(d - d_0)^2 + b

通过三点 (d-1, S_{d-1}), (d, S_d), (d+1, S_{d+1}) 拟合,得到: d_sub = d - (S(d-1) - S(d+1)) / (2(S(d-1) - 2S(d) + S(d+1)))

代码实现:

float subpixelInterpolation(float cost_prev, float cost_curr, float cost_next) {
    float numerator = cost_prev - cost_next;
    float denominator = 2.0 * (cost_prev - 2.0 * cost_curr + cost_next);
    if (std::abs(denominator) < 1e-6) {
        return 0.0f; // 避免除零
    }
    float delta = numerator / denominator;
    // 限制在 [-1, 1] 范围内
    return std::max(-1.0f, std::min(1.0f, delta));
}

void refineDisparity(Mat& disparity, const Mat& S) {
    int dispRange = S.size[2];
    for (int y = 0; y < disparity.rows; y++) {
        for (int x = 0; x < disparity.cols; x++) {
            short d16 = disparity.at<short>(y, x);
            int d = d16 / 16;
            if (d <= 0 || d >= dispRange - 1) continue;
            float cost_prev = S.at<float>(y, x, d - 1);
            float cost_curr = S.at<float>(y, x, d);
            float cost_next = S.at<float>(y, x, d + 1);
            float delta = subpixelInterpolation(cost_prev, cost_curr, cost_next);
            // 更新为亚像素视差 (16 倍精度)
            disparity.at<short>(y, x) = (d + delta) * 16;
        }
    }
}

效果对比: 像素级:d = 10 → 深度误差 ±5cm (假设基线 100mm, 焦距 500px) 亚像素:d = 10.3125 → 深度误差 ±0.3cm (提升 16 倍)

7. 步骤 5: 后处理 (Post-processing)

7.1 左右一致性检查 (Left-Right Consistency Check)

目的: 检测遮挡区域和误匹配。

原理:

  • 计算右视差图 D_R (将左右图像交换再计算)
  • 对于左图像素 (x, y),其对应右图像素为 (x - D_L(x, y), y)
  • 若两者视差不一致,则标记为无效

公式: Valid = { 1 if |D_L(x, y) - D_R(x - D_L(x, y), y)| ≤ disp12MaxDiff 0 otherwise }

代码实现:

void leftRightCheck(Mat& dispLeft, const Mat& dispRight, int disp12MaxDiff) {
    for (int y = 0; y < dispLeft.rows; y++) {
        for (int x = 0; x < dispLeft.cols; x++) {
            short dL = dispLeft.at<short>(y, x);
            if (dL == -16) continue; // 已标记为无效
            int d = dL / 16;
            int x_right = x - d;
            if (x_right < 0 || x_right >= dispRight.cols) {
                dispLeft.at<short>(y, x) = -16; // 无效视差
                continue;
            }
            short dR = dispRight.at<short>(y, x_right);
            // 检查一致性
            if (std::abs(dL - dR) > disp12MaxDiff * 16) {
                dispLeft.at<short>(y, x) = -16;
            }
        }
    }
}

参数建议:

  • disp12MaxDiff = 1: 严格模式,适用于高质量标定
  • disp12MaxDiff = 2: 宽松模式,适用于有畸变的场景
  • disp12MaxDiff = -1: 禁用检查(不推荐)

7.2 唯一性检查 (Uniqueness Ratio Check)

目的: 过滤匹配不唯一的像素(多个视差代价接近)。

公式: Uniqueness = (S_2ndmin - S_min) / S_min × 100

若 Uniqueness < uniquenessRatio,则标记为无效。

void uniquenessCheck(Mat& disparity, const Mat& S, int uniquenessRatio) {
    int dispRange = S.size[2];
    for (int y = 0; y < disparity.rows; y++) {
        for (int x = 0; x < disparity.cols; x++) {
            short d16 = disparity.at<short>(y, x);
            if (d16 == -16) continue;
            int d = d16 / 16;
            float minCost = S.at<float>(y, x, d);
            // 找第二小代价
            float secondMinCost = FLT_MAX;
            for (int d2 = 0; d2 < dispRange; d2++) {
                if (d2 == d) continue;
                float cost = S.at<float>(y, x, d2);
                if (cost < secondMinCost) {
                    secondMinCost = cost;
                }
            }
            // 计算唯一性比率
            float ratio = (secondMinCost - minCost) / minCost * 100.0;
            if (ratio < uniquenessRatio) {
                disparity.at<short>(y, x) = -16; // 匹配不唯一
            }
        }
    }
}

参数影响:

  • uniquenessRatio = 5: 非常严格,会产生较多空洞
  • uniquenessRatio = 10: 平衡模式(推荐)
  • uniquenessRatio = 15: 宽松模式,保留更多匹配

7.3 斑点滤波 (Speckle Filtering)

目的: 去除孤立的噪声点和小连通区域。

算法:

  1. 使用连通域标记找到所有连通区域
  2. 计算每个区域的大小和内部视差方差
  3. 若区域大小 < speckleWindowSize 且方差 > speckleRange,则标记为噪声
void speckleFilter(Mat& disparity, int speckleWindowSize, int speckleRange) {
    if (speckleWindowSize == 0) return;
    Mat labels, stats, centroids;
    int numComponents = cv::connectedComponentsWithStats(disparity > 0, // 有效视差掩码
                                                          labels, stats, centroids, 8);
    for (int i = 1; i < numComponents; i++) {
        // 跳过背景 (label=0)
        int area = stats.at<int>(i, cv::CC_STAT_AREA);
        if (area < speckleWindowSize) {
            // 计算该连通区域的视差方差
            std::vector<short> disparities;
            for (int y = 0; y < disparity.rows; y++) {
                for (int x = 0; x < disparity.cols; x++) {
                    if (labels.at<int>(y, x) == i) {
                        disparities.push_back(disparity.at<short>(y, x) / 16);
                    }
                }
            }
            // 计算标准差
            float mean = std::accumulate(disparities.begin(), disparities.end(), 0.0) / disparities.size();
            float variance = 0;
            for (auto d : disparities) {
                variance += (d - mean) * (d - mean);
            }
            float stddev = std::sqrt(variance / disparities.size());
            // 若区域小且视差变化大,视为噪声
            if (stddev > speckleRange) {
                for (int y = 0; y < disparity.rows; y++) {
                    for (int x = 0; x < disparity.cols; x++) {
                        if (labels.at<int>(y, x) == i) {
                            disparity.at<short>(y, x) = -16;
                        }
                    }
                }
            }
        }
    }
}

参数建议:

  • speckleWindowSize = 100: 过滤小于 100 像素的连通区域
  • speckleRange = 2: 区域内视差变化超过 2 则视为噪声
  • 设为 0 可禁用斑点滤波(提升速度)

7.4 中值滤波 (可选)

对最终视差图进行 3×3 或 5×5 中值滤波平滑:

cv::medianBlur(disparity, disparity, 5);

8. 完整代码示例

8.1 基础调用

#include <opencv2/opencv.hpp>
#include <opencv2/calib3d.hpp>

int main() {
    // 1. 读取校正后的立体图像对
    cv::Mat leftRect = cv::imread("left_rect.png", cv::IMREAD_GRAYSCALE);
    cv::Mat rightRect = cv::imread("right_rect.png", cv::IMREAD_GRAYSCALE);

    // 2. 创建 SGBM 对象
    int minDisparity = 0;
    int numDisparities = 128; // 必须是 16 的倍数
    int blockSize = 5;
    int P1 = 8 * blockSize * blockSize;
    int P2 = 32 * blockSize * blockSize;
    
    cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(
        minDisparity, numDisparities, blockSize, P1, P2, 1, // disp12MaxDiff
        63, // preFilterCap
        10, // uniquenessRatio
        100, // speckleWindowSize
        32, // speckleRange
        cv::StereoSGBM::MODE_SGBM_3WAY
    );

    // 3. 计算视差图
    cv::Mat disparity16S;
    sgbm->compute(leftRect, rightRect, disparity16S);

    // 4. 转换为浮点视差 (除以 16)
    cv::Mat disparity32F;
    disparity16S.convertTo(disparity32F, CV_32F, 1.0 / 16.0);

    // 5. 处理无效视差
    cv::Mat validMask = disparity16S > 0;
    disparity32F.setTo(0, ~validMask);

    // 6. 可视化
    cv::Mat disparityVis;
    cv::normalize(disparity32F, disparityVis, 0, 255, cv::NORM_MINMAX, CV_8U);
    cv::applyColorMap(disparityVis, disparityVis, cv::COLORMAP_JET);
    cv::imshow("Disparity Map", disparityVis);
    cv::waitKey(0);

    // 7. 转换为深度图
    float baseline = 0.120; // 120mm
    float focal = 718.856; // 像素
    cv::Mat depth = (baseline * focal) / disparity32F;

    return 0;
}

8.2 高级参数调优

class SGBMTuner {
public:
    static cv::Ptr<cv::StereoSGBM> createForIndoor() {
        return cv::StereoSGBM::create(
            0, 128, 3, 72, // P1 = 8*1*3²
            288, // P2 = 4*P1
            1, // 严格一致性
            31, // 小预滤波
            10, // 中等唯一性
            50, // 小斑点窗口
            1, // 严格斑点过滤
            cv::StereoSGBM::MODE_HH // 高质量模式
        );
    }
    static cv::Ptr<cv::StereoSGBM> createForOutdoor() {
        return cv::StereoSGBM::create(
            0, 192, 7, 392, // P1 = 8*1*7²
            1568, // P2 = 4*P1
            2, // 宽松一致性
            63, // 大预滤波(应对光照)
            15, // 宽松唯一性
            200, // 大斑点窗口
            2, // 宽松斑点过滤
            cv::StereoSGBM::MODE_SGBM_3WAY
        );
    }
    static cv::Ptr<cv::StereoSGBM> createForRealtime() {
        return cv::StereoSGBM::create(
            0, 64, 5, 200, 800, -1, // 禁用左右检查
            63, 5, 0, // 禁用斑点过滤
            0,
            cv::StereoSGBM::MODE_SGBM // 5 路径模式
        );
    }
};

8.3 视差转深度

cv::Mat disparityToDepth(const cv::Mat& disparity, float baseline, float focal, float minDepth = 0.1, float maxDepth = 10.0) {
    cv::Mat depth = cv::Mat::zeros(disparity.size(), CV_32F);
    for (int y = 0; y < disparity.rows; y++) {
        for (int x = 0; x < disparity.cols; x++) {
            float disp = disparity.at<float>(y, x);
            if (disp <= 0) {
                depth.at<float>(y, x) = 0; // 无效深度
                continue;
            }
            float d = (baseline * focal) / disp;
            // 深度范围限制
            if (d < minDepth || d > maxDepth) {
                depth.at<float>(y, x) = 0;
            } else {
                depth.at<float>(y, x) = d;
            }
        }
    }
    return depth;
}

9. 工程优化技巧

9.1 性能优化

1. 多线程加速

sgbm->setNumThreads(8); // OpenCV 4.x+ 支持

2. ROI 限制

cv::Rect roi(100, 100, 800, 600);
cv::Mat dispROI;
sgbm->compute(leftRect(roi), rightRect(roi), dispROI);

3. 降采样

cv::Mat leftSmall, rightSmall;
cv::resize(leftRect, leftSmall, cv::Size(), 0.5, 0.5);
cv::resize(rightRect, rightSmall, cv::Size(), 0.5, 0.5);
// 计算后上采样
cv::resize(disparity, disparity, leftRect.size());
disparity *= 2; // 视差也需缩放

9.2 参数自适应

根据图像纹理密度调整 blockSize:

int computeOptimalBlockSize(const cv::Mat& image) {
    cv::Mat grad;
    cv::Sobel(image, grad, CV_16S, 1, 1);
    cv::Scalar meanGrad = cv::mean(grad);
    if (meanGrad[0] > 50) {
        return 3; // 纹理丰富 → 小窗口
    } else if (meanGrad[0] > 20) {
        return 5;
    } else {
        return 7; // 纹理稀疏 → 大窗口
    }
}

9.3 错误诊断

常见问题及解决:

问题可能原因解决方案
视差图全黑未进行极线校正使用 stereoRectify + remap
大量空洞uniquenessRatio 过小增大到 10-15
过度平滑P2 过大降低到 4 × P1
噪点多P1 过小增大到 8 × blockSize²
边缘断裂未启用自适应 P2使用 MODE_HH 模式
计算慢路径过多改用 MODE_SGBM (5 路径)

9.4 质量评估

使用 Middlebury 标准评估:

float evaluateDisparity(const cv::Mat& computed, const cv::Mat& groundTruth, float threshold = 1.0) {
    int badPixels = 0;
    int totalPixels = 0;
    for (int y = 0; y < computed.rows; y++) {
        for (int x = 0; x < computed.cols; x++) {
            float gt = groundTruth.at<float>(y, x);
            if (gt == 0) continue; // 跳过无效区域
            float comp = computed.at<float>(y, x);
            if (std::abs(comp - gt) > threshold) {
                badPixels++;
            }
            totalPixels++;
        }
    }
    return (float)badPixels / totalPixels * 100.0; // 错误率 %
}

10. 总结

10.1 算法特点

优点缺点
密集匹配 (90%+ 覆盖率)计算量大 (实时需优化)
边缘保持好弱纹理区域易失败
抗噪声能力强参数敏感
亚像素精度需要良好的极线校正

10.2 适用场景

  • ✅ 自动驾驶: 道路障碍物检测
  • ✅ 机器人导航: SLAM 中的深度估计
  • ✅ 工业检测: 3D 尺寸测量
  • ✅ AR/VR: 实时深度感知
  • ❌ 高速运动: 帧率要求 > 60fps (考虑 GPU 加速)
  • ❌ 极弱纹理: 如白墙 (需结合结构光)

10.3 进阶方向

  1. 深度学习融合: 使用 CNN 计算初始代价体 (如 PSMNet)
  2. 时序一致性: 利用视频序列前后帧约束
  3. GPU 加速: CUDA 实现路径聚合并行化
  4. 自适应参数: 根据场景动态调整 P1, P2

参考文献

  1. Hirschmüller, H. (2007). 'Stereo Processing by Semiglobal Matching and Mutual Information'. IEEE TPAMI, 30(2), 328-341.
  2. OpenCV Documentation: StereoSGBM Class Reference
  3. Birchfield, S., & Tomasi, C. (1998). 'A Pixel Dissimilarity Measure That Is Insensitive to Image Sampling'. IEEE TPAMI, 20(4), 401-406.

目录

  1. SGBM 半全局块匹配算法流程详解
  2. 1. 算法概述
  3. 1.1 SGBM 的本质
  4. 1.2 算法复杂度
  5. 2. 完整算法流程
  6. 3. 步骤 1: 预处理 (Pre-processing)
  7. 3.1 目的
  8. 3.2 Sobel 预滤波
  9. 3.3 参数影响
  10. 4. 步骤 2: 代价计算 (Cost Computation)
  11. 4.1 代价体构建
  12. 4.2 方法 1: BT (Birchfield-Tomasi) 距离
  13. 4.3 方法 2: Census 变换
  14. 4.4 代价体示意图
  15. 5. 步骤 3: 代价聚合 (Cost Aggregation)
  16. 5.1 核心思想
  17. 5.2 单路径动态规划公式
  18. 5.3 路径方向
  19. 5.4 动态规划详细过程
  20. 5.5 多路径聚合
  21. 5.6 自适应惩罚系数
  22. 6. 步骤 4: 视差计算 (Disparity Computation)
  23. 6.1 WTA (Winner-Takes-All) 策略
  24. 6.2 亚像素增强 (Subpixel Refinement)
  25. 7. 步骤 5: 后处理 (Post-processing)
  26. 7.1 左右一致性检查 (Left-Right Consistency Check)
  27. 7.2 唯一性检查 (Uniqueness Ratio Check)
  28. 7.3 斑点滤波 (Speckle Filtering)
  29. 7.4 中值滤波 (可选)
  30. 8. 完整代码示例
  31. 8.1 基础调用
  32. 8.2 高级参数调优
  33. 8.3 视差转深度
  34. 9. 工程优化技巧
  35. 9.1 性能优化
  36. 9.2 参数自适应
  37. 9.3 错误诊断
  38. 9.4 质量评估
  39. 10. 总结
  40. 10.1 算法特点
  41. 10.2 适用场景
  42. 10.3 进阶方向
  43. 参考文献
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 2026年全球AI大模型深度研究报告
  • TortoiseSVN/TortoiseGit 本地修改文件拉取与更新最佳实践
  • Cursor Agent Skills 实战指南:打造专属前端 AI 助手
  • Qwen-Image-2512 免费本地部署实战指南
  • Isaac Lab 机器人强化学习实战:配置架构、添加流程与调参技巧
  • 多智能体强化学习(MARL)核心概念与算法概览
  • Playwright 与 Puppeteer 模拟人工操作攻克纯前端渲染页面
  • 使用 llama.cpp 部署本地 AI 模型遇到的问题与解决方案
  • ASR 自动语音识别技术与 Whisper 模型详解
  • 网络安全专业就业前景与职业发展深度解析
  • Coze 工作流一键生成“葬经人”风格动画(含提示词)
  • Web 团队开发 App,是否应该选择 Capacitor?
  • 修复 Java 编译错误:源发行版 17 需要目标发行版 17
  • 使用 frontend-design Skill 提升大模型前端设计审美
  • Stable Diffusion 整合包安装与使用指南
  • OpenClaw 插件开发指南:为 AI 数字员工添加新技能
  • LLaMA 衍生模型:官方演进与社区微调
  • GLM-4-9B开源大模型:超越Llama-3-8B的性能评测
  • OpenAI Codex 终端 AI 编程助手安装与配置指南
  • 大模型与生成式 AI 的发展脉络及应用安全思考

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online