Skip to content

Incorrect image borders taken in cv::stereoRectify (calibration.cpp) #6336

@vicproon

Description

@vicproon

Please state the information for your system

  • OpenCV version: 3.1
  • Host OS: Linux (Ubuntu 14.04) / Windows 7

In which part of the OpenCV library you got the issue?

  • calib3d

Introduction

The math that stands behind pinhole camera model, radial distortion and stereo rectification can be easily applied to warping images of different scales. When one performes all the calibration routines on the sourse image in some applications it can be useful to undistort/rectify image of, say 0.25*actual size.

This can be achieved by generating separate remap maps via InitUndistorRectifyMap. The only thing to change is the camera matrices passed to cv::stereoRectify functiuon. Their first 2 lines should be scaled respectively; Distortion coefficient operate to scale-independent coordinates, Rotation and translation describe real world and thus are independent from the output image size too.

bool resizeCameraMatrix(const cv::Mat &in_cm, cv::Mat &dst_cm, double scale)
{
  if (in_cm.empty() || in_cm.cols != 3 || in_cm.rows != 3 || in_cm.type() != CV_64FC1)
    return false;
  dst_cm = in_cm * scale;
  dst_cm.at<double>(2, 2) = 1.0;
  return true;
}

source image

image

Expected behaviour

stereoRectify of resized image with resized camera matrix would equal to the resize of rectified source image
src_result
remap(rectify)->resize result

Actual behaviour

It is not under certain conditions.
rsz_result
resize->remap(rectify) result

Additional description

This trick was working just fine until we have obtained unlucky rectification values (or, from the other side, we were indeed very lucky). The principal point of the resized rectified image moved to the bottom right part of the image and all the stereo reconstructions failed.

The problem appers because of incorrect image border copmutation in cv::stereoRectify on the undistorted image. When dealing with double-valued image coordinates the width of pixel becomes an issue. The top-left corner of a pixel which is used to compute new principal point on current master version has different double coordinates on different image scales. In case of 0.25 scale it moves 3 pixels left and top, respectively. If the actual corners of the image are taken the problem disappears since scaling does not change actual double relative points coordinates, which are passed to cv::undistortPoints and the rectification process is similar for all the scales.

The possible solution is to pass (width+1, height+1) image size to cv::stereoRectify function, however it can be bad for focal length computation that is performed in the beginning of the function.
The more elegant way to solve the problem is to replace image corners to actual corner coordinates: (nx-1) and (ny-1) by nx and ny, respectively

pull request to solve the issue #6337.

Code example to reproduce the issue / Steps to reproduce the issue

The test data and results are attached.

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

bool resizeCameraMatrix(const Mat &in_cm, Mat &dst_cm, double scale)
{
  if (in_cm.empty() || in_cm.cols != 3 || in_cm.rows != 3 || in_cm.type() != CV_64FC1)
    return false;
  dst_cm = in_cm * scale;
  dst_cm.at<double>(2, 2) = 1.0;
  return true;
}


static const Matx33d M1(906.7857732303256, 0.0, 1026.456125870669,
                        0.0, 906.7857732303256, 540.0531577669913,
                        0.0, 0.0, 1.0);
static const Matx33d M2(906.782205162265, 0.0, 1014.619997352785,
                        0.0, 906.782205162265, 561.9990018887295,
                        0.0, 0.0, 1.0);
static const Matx<double, 5, 1> D1(0.0064836857220181504, 0.033880363848984636, 0.0, 0.0, -0.042996356352306114);
static const Matx<double, 5, 1> D2(0.023754068600491646, -0.02364619610835259, 0.0, 0.0, 0.0015014971456262652);

static const Size imageSize(2048, 1088);
static const double scale = 0.25;

static const Matx33d Rot(0.999788461750194, -0.015696495349844446, -0.013291041528534329, 
                         0.015233019205877604, 0.999296086451901, -0.034282455101525826, 
                         0.01381980018141639, 0.03407274036010432, 0.9993238021218641);
static const Matx31d T(-1.552005597952028, 0.0019508251875105093, -0.023335501616116062);

int main()
{ 
  // generate camera matrices for resized image rectification.
  Mat srcM1(M1), srcM2(M2);
  Mat rszM1, rszM2;
  resizeCameraMatrix(srcM1, rszM1, scale);
  resizeCameraMatrix(srcM2, rszM2, scale);
  Size rszImageSize(cvRound(scale * imageSize.width), cvRound(scale * imageSize.height));
  Size srcImageSize = imageSize;
  // apply stereoRectify
  Mat srcR[2], srcP[2], srcQ;
  Mat rszR[2], rszP[2], rszQ;
  stereoRectify(srcM1, D1, srcM2, D2, srcImageSize, Rot, T,
   srcR[0], srcR[1], srcP[0], srcP[1], srcQ,
   CALIB_ZERO_DISPARITY, 0);
  stereoRectify(rszM1, D1, rszM2, D2, rszImageSize, Rot, T,
   rszR[0], rszR[1], rszP[0], rszP[1], rszQ,
   CALIB_ZERO_DISPARITY, 0);
  //generate remap maps
  Mat srcRmap[2], rszRmap[2];
  initUndistortRectifyMap(srcM1, D1, srcR[0], srcP[0], srcImageSize, CV_32FC2, srcRmap[0], srcRmap[1]);
  initUndistortRectifyMap(rszM1, D1, rszR[0], rszP[0], rszImageSize, CV_32FC2, rszRmap[0], rszRmap[1]);

  // read source image
  Mat image = imread("./image.png");
  if (image.empty())
  {
    cerr << "Can not read input image;" << endl;
    return 1;
  }
  // perform remap-resize
  Mat src_result;
  remap(image, src_result, srcRmap[0], srcRmap[1], CV_INTER_LINEAR);
  resize(src_result, src_result, Size(), scale, scale, CV_INTER_LINEAR);
  // perform resize-remap
  Mat rsz_result;
  resize(image, rsz_result, Size(), scale, scale, CV_INTER_LINEAR);
  remap(rsz_result, rsz_result, rszRmap[0], rszRmap[1], CV_INTER_LINEAR);

  //output mats;
  imwrite("./src_result.png", src_result);
  imwrite("./rsz_result.png", rsz_result);
  return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions