opencv - 如何获得干净的视差图和干净的深度图
问题描述
目前我正在使用来自titathink(TT522PW)的IP摄像头,它提供1280 * 720的流和30 FPS的普通传感器(不是具有高灵敏度低光的模型)
在捕获视频流时,我们会在帧上看到鱼眼类型的失真。
未校正的图像
我首先单独校准每个相机以消除失真(在校准中,我得到左相机rms_left = 0.166和右相机rms_right = 0.162的 rms 误差)。然后使用单个校准相机过程产生的 xml 文件,我校准了立体相机,在立体校准中,我得到RMS 误差 = 0.207
通过显示校准后的图像,我们看到立体校准做得很好
校准后的图像
用水平线校正图像
- 我接手了dji的计算视差图和计算点云的功能
计算和过滤视差图的代码
bool Disparity_filter::initDispParam(){
#ifdef USE_CUDA
block_matcher_ = cv::cuda::createStereoBM(num_disp_, block_size_);
#else
block_matcher_ = cv::StereoBM::create(num_disp_, block_size_);
#endif
#ifdef USE_OPEN_CV_CONTRIB
wls_filter_ = cv::ximgproc::createDisparityWLSFilter(block_matcher_); // left_matcher
wls_filter_->setLambda(8000.0);
wls_filter_->setSigmaColor(1.5);
right_matcher_ = cv::ximgproc::createRightMatcher(block_matcher_);
#endif
return true;
}
void Disparity_filter::computeDisparityMap(std::shared_ptr<Frame> framel, std::shared_ptr<Frame> framer){
framel->raw_disparity_map_=cv::Mat(HEIGHT, WIDTH, CV_16SC1);
#ifdef USE_CUDA
cv::cuda::GpuMat cuda_disp_left;
framel->cuda_crop_left.upload(framel->cpu_crop_left);
framer->cuda_crop_right.upload(framer->cpu_crop_right);
// GPU implementation of stereoBM outputs uint8_t, i.e. CV_8U
block_matcher_->compute(framel->cuda_crop_left.clone(),
framer->cuda_crop_right.clone(),
cuda_disp_left);
cuda_disp_left.download(framel->raw_disparity_map_);
framel->raw_disparity_map_.convertTo(framel->disparity_map_8u_, CV_8UC1, 1);
// convert it from CV_8U to CV_16U for unified
// calculation in filterDisparityMap() & unprojectPtCloud()
framel->raw_disparity_map_.convertTo(framel->raw_disparity_map_, CV_16S, 16);
#else
// CPU implementation of stereoBM outputs short int, i.e. CV_16S
cv::Mat left_for_matcher ,right_for_matcher;
left_for_matcher = framel->cpu_crop_left.clone();
right_for_matcher = framer->cpu_crop_right.clone();
cv::cvtColor(left_for_matcher, left_for_matcher, cv::COLOR_BGR2GRAY);
cv::cvtColor(right_for_matcher, right_for_matcher, cv::COLOR_BGR2GRAY);
block_matcher_->compute(left_for_matcher, right_for_matcher, framel->raw_disparity_map_);
framel->raw_disparity_map_.convertTo(framel->disparity_map_8u_, CV_8UC1, 0.0625);
#endif
}
void Disparity_filter::filterDisparityMap(std::shared_ptr<Frame> framel, std::shared_ptr<Frame> framer){
right_matcher_->compute(framer->cpu_crop_right.clone(),
framel->cpu_crop_left.clone(),
raw_right_disparity_map_);
// Only takes CV_16S type cv::Mat
wls_filter_->filter(framel->raw_disparity_map_,
framel->cpu_crop_left,
filtered_disparity_map_,
raw_right_disparity_map_);
filtered_disparity_map_.convertTo(framel->filtered_disparity_map_8u_, CV_8UC1, 0.0625);
}
计算点云的代码
bool PointCloud::initPointCloud(){
std::string stereo_c2="../calibration/sterolast.xml"; //calib_stereo.xml"; //
cv::FileStorage ts(stereo_c2,cv::FileStorage::READ);
if (!ts.isOpened()) {
std::cerr << "Failed to open calibration parameter file." << std::endl;
return false;
}
cv::Mat P1,P2;
ts["P1"] >> param_proj_left_;
ts["P2"] >> param_proj_right_;
principal_x_ = param_proj_left_.at<double>(0, 2);
principal_y_ = param_proj_left_.at<double>(1, 2);
fx_ = param_proj_left_.at<double>(0, 0);
fy_ = param_proj_left_.at<double>(1, 1);
baseline_x_fx_ = -param_proj_right_.at<double>(0, 3);
std::cout<<"** principal_x= " << principal_x_ <<" ** principal_y= " << principal_y_ <<" ** fx= " << fx_ <<" ** fy= " << fy_<<" ** baseline_x_fx= " << baseline_x_fx_<<std::endl<< std::flush;
return true;
}
void PointCloud::unprojectPtCloud(std::shared_ptr<Frame> framel)
{
// due to rectification, the image boarder are blank
// we cut them out
int border_size = num_disp_;
const int trunc_img_width_end = HEIGHT - border_size;
const int trunc_img_height_end = WIDTH - border_size;
mat_vec3_pt_ = cv::Mat_<cv::Vec3f>(HEIGHT, WIDTH, cv::Vec3f(0, 0, 0));
cv::Mat color_mat_(HEIGHT, WIDTH, CV_8UC1, &color_buffer_[0]) ;
for(int v = border_size; v < trunc_img_height_end; ++v)
{
for(int u = border_size; u < trunc_img_width_end; ++u)
{
cv::Vec3f &point = mat_vec3_pt_.at<cv::Vec3f>(v, u);
#ifdef USE_OPEN_CV_CONTRIB
float disparity = (float)(framel->raw_disparity_map_.at<short int>(v, u)*0.0625);
#else
float disparity = (float)(framel->raw_disparity_map_.at<short int>(v, u)*0.0625);
#endif
//std::cout<<"** disparity " << disparity << std::endl<< std::flush;
// do not consider pts that are farther than 8.6m, i.e. disparity < 6
if(disparity >= 60)
{
point[2] = baseline_x_fx_/disparity;
point[0] = (u-principal_x_)*point[2]/fx_;
point[1] = (v-principal_y_)*point[2]/fy_;
}
color_buffer_[v*WIDTH+u] = framel->cpu_crop_left.at<uint8_t>(v, u);
}
}
color_mat_ = cv::Mat(HEIGHT, WIDTH, CV_8UC1, &color_buffer_[0]).clone();
framel->mat_vec3=mat_vec3_pt_;
framel->color_m=color_mat_;
pt_cloud_ = cv::viz::WCloud(mat_vec3_pt_, color_mat_);
}
当我计算视差图并对其进行过滤时,我得到的地图在 100% 处不清晰(尽管相机的位置和障碍物固定在流中,但我们看到区域会改变光强度,不是很清晰但可以接受)你可以看一个小视频,我在其中使用 RMS = 0.2 的校准文件对其进行了测试。
立体视觉-视差图测试
点云
问题
我以 RMS=0.20 的误差执行的立体校准是否足以获得清晰的视差图和两个相机视野的完整云点?
如何获得稳定干净的视差图和干净的DEPTH MAP?
谢谢你的帮助 :)
解决方案
如何获得稳定干净的视差图和干净的DEPTH MAP?
为了回答这个问题,我看了你分享的视频。过滤后的视差图看起来不错。您使用的 WLS 过滤器提供了一个像这样的视差图。没有什么问题。但通常,对于点云,不建议将过滤后的视差图作为输入。这是因为过滤器倾向于填充算法找不到的孔。换句话说,它们是不可靠的数据。因此,尝试将未过滤的视差图作为点云的输入。
此外,您用来查看点云的查看器,即meshlab,往往会吃掉一些点。因此,您可以使用其他查看器,例如 CloudCompare。
我以 RMS=0.20 的误差执行的立体校准是否足以获得清晰的视差图和两个相机视野的完整云点?
是的,在大多数情况下,0.20 RMS 误差就足够了。但同样,越小越好。
推荐阅读
- python - 类上带有 **kwargs 的函数返回“意外参数”
- cluster-computing - ambari 2.7.4 安装向导问题
- css - 如何为使用 ngx-markdown 渲染的内容设置样式?
- android - 调用 startActivity() 后应用程序崩溃
- react-native - React Native cli 项目 - 在 vscode 中调试
- php - 仅允许在我的帐户上的每个客户编辑自定义字段一次 > 在 WooCommerce 中编辑帐户
- c - 程序运行的具体情况如何?
- flutter - 如何在 TimeOfDay 中转换字符串“TimeOfDay(00:00)”
- xamarin.forms - 当属性通过 XAML 中的数据绑定从 xamarin 表单中的视图模型更改时,如何更改图像源?
- javascript - 更改地址栏中的整个 url (Javascript)