为什么 cv::Mat 对象在调试和发布之间表现不同?

Why is a cv:: Mat object behaving differently between debug and release?

我真的很头疼,试图弄清楚为什么我的 OpenCV 矩阵的填充在调试模式下完全按照我的预期工作,但是当我尝试发布项目时,它截断了给它的所有值,并错误地填充矩阵!

我的设置的简短描述:

进入代码!

首先,我使用 ViSP 的内置 类 从文件中加载已保存的参数。出于调试目的(并确保我没有失去理智,)我还将值保存到全局变量以供快速参考:

bool load_intrinsics()
{
  vpCameraParameters cam;
  vpXmlParserCaemra parser;
  std::string intrinsicFilePath = "...path to file...";
  std::string cameraName = "Camera";

  if (parser.parse(cam, intrinsicFilePath, camera_name, vpCameraParameters::perspectiveProjWithDistortion) != vpXmlParserCamera::SEQUENCE_OK)
  {
    std::cout << "ERROR WITH LOADING INTRINSICS" << std::endl;
    return false;
  }
  else
  { // variables here are <double> variables, defined globally
    global_px = cam.get_px();
    global_py = cam.get_py();
    global_u0 = cam.get_u0();
    global_v0 = cam.get_v0();
    global_kud = cam.get_kud();
    return true;
  }
}

`

这一切都按预期工作。该函数正确读取所有值并将这些值(未截断!)存储到全局变量。

接下来我尝试将这些变量传递给一个 cv::Mat 对象,这就是事情开始走下坡路的地方...

void createCvMat()
{
  //  create camera matrix
  cv::Mat tempCamMat = cv::Mat::zeros(3, 3, CV_64F);
  tempCamMat.at<double>(0, 0) = global_px;
  tempCamMat.at<double>(0, 2) = global_u0;
  tempCamMat.at<double>(1, 1) = global_py;
  tempCamMat.at<double>(1, 2) = global_v0;
  tempCamMat.at<double>(2, 2) = 1.0;
  // create distortion matrix
  cv::Mat tempDisMat = cv::Mat::zeros(5, 1, CV_64F);
  tempDisMat.at<double>(0, 0) = global_kud;
  // pass temporary matrices to empty global matrices
  tempCamMat.copyTo(cameraMatrix);
  tempDisMat.copyTo(distortionMatrix);
}

现在,如第一个代码片段中所述,我已经确保全局变量中的值正确。然而,当我使用这些矩阵时,例如,使用 cv::undistort(...),很明显矩阵没有正确加载。

作为完整性检查,我尝试将矩阵写到控制台,并确保值被正确加载:

void sanityCheck(cv::Mat checkMatrix)
{
  std::cout << std::endl << "Matrix holds:" <<std::endl;
  for (int r = 0; r < checkMatrix.rows; r++)
  {
    for (int c = 0; c < checkMatrix.cols; c++)
    {
      std::cout << checkMatrix.at<double>(r, c) << " | ";
    }
    std::cout << std::endl;
  }
}

在调试模式下调用两个矩阵的健全性检查,我得到以下输出:

`

Camera
Matrix Includes:
606.204 | 0 | 607.664 |
0 | 612.354 | 521.775 |
0 | 0 | 1 |

Distortion
Matrix Includes:
-0.240543 |
0 |
0 |
0 |
0 |

`

请注意,std::cout 控制台输出确实将写入的值截断为六个有效数字,因为从文件中读取的数字有将近二十个有效数字。将 iomanip 库与 std::setprecision(...) 结合使用,我已确认完整数字已正确结转并插入到矩阵中。

在发布中调用相同的函数,我得到以下输出:

Camera
Matrix Includes:
606 | 0 | 607 |
0 | 612 | 521 |
0 | 0 | 1 |

Distortion
Matrix Includes:
-0 |
0 |
0 |
0 |
0 |

` 请注意,即使将双精度变量传递给能够容纳双精度变量的矩阵,矩阵中也只有整数!对于我的生活,我无法弄清楚为什么它在项目的发布版本中这样做,而不是调试版本!

作为当前的解决方法,我尝试将数字直接从“*.xml”文件复制粘贴到变量作为常量,然后再将它们传递给 cv::Mat 对象:

// define each global variable (don't read from file)
global_px = 606.20444013030078;
...
global_kud = -0.24054321189834804;
// populate matrices
createCvMat();

。这工作得很好......我不知道为什么,但确实如此。

正如任何程序员很可能会告诉您的那样,应该避免这种常量的“硬编码”。如果我出于某种原因更改了相机的设置并且必须重新校准,或者如果最终用户希望校准他们自己的相机,最好从外部文件中读取这些固有值。

详情:

double 变量加载到 cv::Mat<double> 对象的问题与安装的 Ubuntu-16.04 OS 的本地化问题有关。由于 PC 在德国配置,尽管所有参数都设置为模仿安装时的 EN 标准,PC 仍然将其数字、货币、时间等解释设置为 DE 标准。

这意味着,从外部文件解析 double 变量将导致 OS 将 EN 标准小数位(句点 (.))误认为是 DE 标准千分之一分隔符。这导致 double 变量的小数部分被截断!

解决方法:

使用Ubuntu-16.04 OS,这可以通过在终端内更改系统区域设置来解决。打开终端,输入locale命令,会给用户一个用于系统本地化解释的语言标准列表,如:

LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=en_US.UTF-8
LC_TIME=de_DE.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=en_US.UTF-8
...

LC_CTYPELC_NUMERICLC_MEASUREMENT 标签更改为 en_US.UTF-8 解决了我这边的问题。这可以通过终端使用以下命令完成:

sudo update-locale LC_CTYPE=en_US.UTF-8
sudo update-locale LC_NUMERIC=en_US.UTF-8
sudo update-locale LC_MEASUREMENT=en_US.UTF-8

或者在您选择的文本编辑器中,使用 root 权限编辑 /etc/default/locale 文件。文件更新后,请确保重新启动以完成更改!

非常感谢@Scheff 对此事的见解。我怀疑如果没有他们的建议我会解决这个问题!非常感谢,太棒了!