基于块匹配的双目视差函数的实现

理想状态下,双目相机拍出的两张照片left和right种,left图像中的某一个像素应当可以在right中同一行(opencv中的row)中找到该像素对应的像素,根据这两个像素的x坐标差,便可以得到图像的深度图。但是并非每一个像素都可以找到其对应像素,原因有:

(1)由于遮挡关系,left中的像素无法在right中找到

(2)由于某些处于非朗伯面(比如水面、镜面、玻璃)的点由于光照原因无法匹配

(3)密集重复纹理(树林、草地)下或纹理特别稀疏(墙面、天空)时,很难准确匹配

(4)纹理都是水平线条时(线条与极线平行),很难准确匹配

此处,鄙人给出最简单的双目匹配的实现方式,即采用块匹配的方式计算深度图。

#pragma once

#include
#include 
#include"myexception.h"

using std::vector;

class stereo {
public:
    stereo(const cv::Mat& _left, const cv::Mat& _right);
    ~stereo() {}
    cv::Mat getDepthImage(const int half_window_size = 4, const int threshold = 20);

    void getGradient(void);
    inline int SAD(const int left_row, const int left_col, const int right_row, const int right_col, int half_window_size);
protected:
    cv::Mat getMergeImage(); 
    cv::Mat left;
    cv::Mat right;
    cv::Mat gleft;
    cv::Mat gright;

};
#include "stereo.h"

stereo::stereo(const cv::Mat& _left, const cv::Mat& _right): left(_left), right(_right){
    gleft = cv::Mat(left.rows, left.cols, CV_16SC1, cv::Scalar(0));
    gright = cv::Mat(right.rows, right.cols, CV_16SC1, cv::Scalar(0));
}
void stereo::getGradient(void) {
    int row = left.rows;
    int col = left.cols;

    for (int i = 0; i < row; ++i) {
        for (int j = 1; j < col - 1; ++j) {
            gleft.at(i, j) = abs(short(left.at(i, j - 1)) - left.at(i, j + 1));
            gright.at(i, j) = abs(short(right.at(i, j - 1)) - right.at(i, j + 1));
        }
    }
}

cv::Mat stereo::getMergeImage() {

    int row = gleft.rows;
    int col = gleft.cols;

    cv::Mat merge(left.rows, left.cols, CV_8UC1);

    for (int i = 0; i < row; ++i) {
        for (int j = 0; j < col; ++j) {
            int temp = 0;
            temp = abs(gleft.at(i, j)) + abs(gright.at(i, j));
            temp >>= 1;
            if (temp > 255)temp = 255;
            merge.at(i, j) = temp;
        }
    }

    return merge;
}

cv::Mat stereo::getDepthImage(const int half_window_size, const int threshold) {

    if (half_window_size < 1)throw param_exception("the param half_window_size should upper than  1");
    if(threshold < 0)throw param_exception("the param threshold should upper than 0");

    int row = left.rows;
    int col = left.cols;

    cv::Mat depth(row, col, CV_8UC3, cv::Scalar(0, 0, 0));

    int window_size = (half_window_size << 1) + 1;

    int search_range = col * 0.2;

    for (int i = half_window_size; i < row - half_window_size; ++i) {
        for (int j = half_window_size; j < col - half_window_size; ++j) {
            uchar* pleft = &(left.data[i * col + j]);

            if (gleft.at(i, j) < threshold)continue;
            int search_left = j - search_range;
            if (search_left < half_window_size) search_left = half_window_size;
            int search_right = j + search_range;
            if (search_right > col - half_window_size) search_right = col - half_window_size;

            int min_ssd = 0x0fffffff;
            int min_k = search_left;
            for (int k = search_left; k < search_right; ++k) {
                int ssd = SAD(i, j, i, k, half_window_size);
                if (ssd < min_ssd) {
                    min_ssd = ssd;
                    min_k = k;
                }
            }
            if(min_ssd < window_size * window_size * 9){
                int tmp = abs(min_k - j);
                if (tmp == 0)
                    tmp = 0xffff;
                else
                    tmp = (481) / tmp;
                depth.at(i, j) = cv::Vec3b(tmp >> 8, tmp & 0xff, tmp & 0xff);
            }
            else {
                depth.at(i, j) = cv::Vec3b(0, 0, 0);
            }
        }
    }

    return depth;
}

inline int stereo::SAD(const int left_row, const int left_col, const int right_row, const int right_col, int half_window_size) {

    int window_size = 1 + (half_window_size << 1);

    int sum = 0;

    for (int i = -half_window_size; i <= half_window_size; ++i) {
        for (int j = -half_window_size; j <= half_window_size; ++j) {
            int temp = int(left.at(left_row + i, left_col + j)) - right.at(right_row + i, right_col + j);
            sum += abs(temp);
        }
    }

    return sum;
}

注:half_window_size是窗口的半径,越大,匹配越准确,但是速度越慢,threshold为阈值,即将梯度小的点去除掉,可以加快计算并且忽略掉纹理变换小的区域,可以设置为0计算所有像素

函数的使用方式为:

cv::Mat img1 = cv::imread("G:\\dataset\\sequences\\00\\image_0\\000000.png", CV_LOAD_IMAGE_GRAYSCALE);
    cv::Mat img2 = cv::imread("G:\\dataset\\sequences\\00\\image_1\\000000.png", CV_LOAD_IMAGE_GRAYSCALE);

    stereo st(img1, img2);
    st.getGradient();
    auto depth = st.getDepthImage();
    for (float i = 1; i < 10; i += 0.2) {
        cv::imshow("depth", depth * i);
        cv::waitKey();
    }//调节亮度

 

你可能感兴趣的:(SLAM)