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

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

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

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

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

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

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

#pragma once

#include<opencv.hpp>
#include <vector>
#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<short>(i, j) = abs(short(left.at<uchar>(i, j - 1)) - left.at<uchar>(i, j + 1));
            gright.at<short>(i, j) = abs(short(right.at<uchar>(i, j - 1)) - right.at<uchar>(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<short>(i, j)) + abs(gright.at<short>(i, j));
            temp >>= 1;
            if (temp > 255)temp = 255;
            merge.at<uchar>(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<short>(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<cv::Vec3b>(i, j) = cv::Vec3b(tmp >> 8, tmp & 0xff, tmp & 0xff);
            }
            else {
                depth.at<cv::Vec3b>(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<uchar>(left_row + i, left_col + j)) - right.at<uchar>(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();
    }//调节亮度

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值