为什么 cv::Mat 对象在调试和发布之间表现不同?
Why is a cv:: Mat object behaving differently between debug and release?
我真的很头疼,试图弄清楚为什么我的 OpenCV 矩阵的填充在调试模式下完全按照我的预期工作,但是当我尝试发布项目时,它截断了给它的所有值,并错误地填充矩阵!
我的设置的简短描述:
- 我正在使用 OpenCV 和 ViSP 库来处理 USB Flycapture 相机。
- 我 运行 Qt-Creator Ubuntu 16.04 上的所有内容,因为我计划在此项目中使用 GUI。
- 填充矩阵的参数对于相机的校准很重要,因此截断是绝对不行的!
进入代码!
首先,我使用 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_CTYPE
、LC_NUMERIC
和 LC_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 对此事的见解。我怀疑如果没有他们的建议我会解决这个问题!非常感谢,太棒了!
我真的很头疼,试图弄清楚为什么我的 OpenCV 矩阵的填充在调试模式下完全按照我的预期工作,但是当我尝试发布项目时,它截断了给它的所有值,并错误地填充矩阵!
我的设置的简短描述:
- 我正在使用 OpenCV 和 ViSP 库来处理 USB Flycapture 相机。
- 我 运行 Qt-Creator Ubuntu 16.04 上的所有内容,因为我计划在此项目中使用 GUI。
- 填充矩阵的参数对于相机的校准很重要,因此截断是绝对不行的!
进入代码!
首先,我使用 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_CTYPE
、LC_NUMERIC
和 LC_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 对此事的见解。我怀疑如果没有他们的建议我会解决这个问题!非常感谢,太棒了!