相机内参

Zivid相机模型比一些广为人知的针孔相机模型(例如OpenCV相机模型)更复杂,使用了更多的内参。此外,我们的相机使用了一种滚动校准技术,这意味着点云是作为光圈、温度和颜色通道的函数生成的。

Zivid相机模型是专有的,这就是我们的内部相机内参无法通过SDK获取的原因。但是,我们提供了OpenCV和Halcon模型的近似值,因为许多机器视觉算法都依赖它们。

小心

一般来说,我们倾向于不鼓励使用相机内参。其中一个原因是在近似值中丢失了一些有价值的信息。另一个原因是通常有更好的方法可以达到相同的结果。由于主题的复杂性,我们建议您联络我们来讨论您的用例。内参是复杂的,建议尽可能使用点云!

OpenCV相机内参

Zivid SDK 提供了一些获取 OpenCV 相机内参的方法。

intrinsics(camera)

返回与相机的 Zivid::Settings::Sampling::Pixel 默认值相对应的内参。

intrinsics(camera, settings)

返回与 settings 中使用的 Zivid::Settings::Sampling::Pixel 参数相对应的内参数据。

intrinsics(camera, settings_2d)

返回与 settings_2d 捕获相对应的内参。

estimateIntrinsics(frame)

返回与用于捕获的 frame 中的 Zivid::Settings::Sampling::Pixel 的设置相对应的内参。

针对固定光圈和固定温度给出的硬编码的相机内参。这个温度和光圈对应于我们认为的典型条件。

相机型号

镜头温度 (°C)

光圈

Zivid 2+

35

2.68

Zivid 2

35

2.30

要获得硬编码的相机内参,您首先必须连接到相机:

跳转到源码

源码

std::cout << "Connecting to camera" << std::endl;
auto camera = zivid.connectCamera();
跳转到源码

源码

Console.WriteLine("Connecting to camera");
var camera = zivid.ConnectCamera();
跳转到源码

源码

print("Connecting to camera")
camera = app.connect_camera()

然后,您可以获得默认的 OpenCV 相机内参(该函数立即返回):

跳转到源码

源码

std::cout << "Getting camera intrinsics" << std::endl;
const auto intrinsics = Zivid::Experimental::Calibration::intrinsics(camera);
跳转到源码

源码

Console.WriteLine("Getting camera intrinsics");
var intrinsics = Zivid.NET.Experimental.Calibration.Calibrator.Intrinsics(camera);
跳转到源码

源码

print("Getting camera intrinsics")
intrinsics = zivid.experimental.calibration.intrinsics(camera)

或者,您可以获取 settings 中使用的 Zivid::Settings::Sampling::Pixel 的配置对应的OpenCV 相机内参:

跳转到源码

源码

const auto settingsSubsampled =
    Zivid::Settings{ Zivid::Settings::Engine::phase,
                     Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
                     Zivid::Settings::Sampling::Pixel::blueSubsample2x2 };
const auto fixedIntrinsicsForSubsampledSettings =
    Zivid::Experimental::Calibration::intrinsics(camera, settingsSubsampled);
跳转到源码

源码

var settingsSubsampled = new Zivid.NET.Settings();
settingsSubsampled.Acquisitions.Add(new Zivid.NET.Settings.Acquisition { });
settingsSubsampled.Sampling.Pixel = Zivid.NET.Settings.SamplingGroup.PixelOption.BlueSubsample2x2;
var fixedIntrinsicsForSubsampledSettings = Zivid.NET.Experimental.Calibration.Calibrator.Intrinsics(camera, settingsSubsampled);
跳转到源码

源码

settings_subsampled = zivid.Settings(
    acquisitions=[zivid.Settings.Acquisition()],
    sampling=zivid.Settings.Sampling(pixel=zivid.Settings.Sampling.Pixel.blueSubsample2x2),
)
fixed_intrinsics_for_subsampled_settings = zivid.experimental.calibration.intrinsics(camera, settings_subsampled)

或者,类似地对于 2D 设置:

const auto settings2D = Zivid::Settings2D{ Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} }};
const auto fixedIntrinsicsForSettings2D = Zivid::Experimental::Calibration::intrinsics(camera, settings2D);
var settings2D = new Zivid.NET.Settings2D
{
   Acquisitions = { new Zivid.NET.Settings2D.Acquisition { } }
};
var fixedIntrinsicsForSettings2D = Zivid.NET.Experimental.Calibration.Intrinsics(camera, settings2D);
settings_2d = zivid.Settings2D()
fixed_intrinsics_for_settings_2d = calibration.intrinsics(camera, settings_2d)

小心

与我们的点云校准不同,硬编码的 OpenCV 内参是固定数值的,无法适应环境变化带来的影响。因此,它们不能完美地对应于在不同温度下使用不同光圈拍摄的图像。

生产中的环境温度可能会发生变化,相机温度也会随之变化。热稳定有助于调节相机内部温度。然而,如果相机没有时间进行预热,那么在部署开始时镜头温度可能会比稳定期后低得多。由于温差,硬编码的内参与点云数据的一致性会较差。

当谈到光圈时,如果使用每次采集都设置了不同光圈的HDR模式捕获点云,情况会变得更加复杂。这是因为确定这种情况的内参并非易事。

由于这些复杂性,我们提供了一种获取OpenCV类型相机内参的替代方法:从点云估计的相机内参。

这些相机内参是从点云中估计出来的。Zivid标定下的点云是由包括温度和光圈数据的函数生成的。因此,估计的内参间接利用了温度和光圈数据。

要获得估计的OpenCV内参,您首先必须连接到相机:

跳转到源码

源码

std::cout << "Connecting to camera" << std::endl;
auto camera = zivid.connectCamera();
跳转到源码

源码

Console.WriteLine("Connecting to camera");
var camera = zivid.ConnectCamera();
跳转到源码

源码

print("Connecting to camera")
camera = app.connect_camera()

然后您必须捕获一幅图像:

跳转到源码

源码

const auto frame = camera.capture(settings);
跳转到源码

源码

var frame = camera.Capture(settings);
跳转到源码

源码

with camera.capture(settings=settings) as frame:

然后,您可以从frame估计内参(因为涉及计算,此函数不会立即返回):

跳转到源码

源码

const auto estimated_intrinsics = Zivid::Experimental::Calibration::estimateIntrinsics(frame);
跳转到源码

源码

var estimatedIntrinsics = Zivid.NET.Experimental.Calibration.Calibrator.EstimateIntrinsics(frame);
跳转到源码

源码

estimated_intrinsics = zivid.experimental.calibration.estimate_intrinsics(frame)

请注意,如果您将 Zivid::Settings::Sampling::Pixel 设置为例如 Zivid::Settings::Sampling::Pixel::blueSubsample2x2 ,那么您将通过同一函数获得正确的(子采样的)内参:

跳转到源码

源码

const auto settingsSubsampled =
    Zivid::Settings{ Zivid::Settings::Engine::phase,
                     Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
                     Zivid::Settings::Sampling::Pixel::blueSubsample2x2 };
const auto frame = camera.capture(settingsSubsampled);
const auto estimatedIntrinsicsForSubsampledSettings =
    Zivid::Experimental::Calibration::estimateIntrinsics(frame);
跳转到源码

源码

var settingsSubsampled = new Zivid.NET.Settings();
settingsSubsampled.Acquisitions.Add(new Zivid.NET.Settings.Acquisition { });
settingsSubsampled.Sampling.Pixel = Zivid.NET.Settings.SamplingGroup.PixelOption.BlueSubsample2x2;
var frameSubsampled = camera.Capture(settingsSubsampled);
var estimatedIntrinsicsForSubsampledSettings = Zivid.NET.Experimental.Calibration.Calibrator.EstimateIntrinsics(frameSubsampled);
跳转到源码

源码

settings_subsampled = zivid.Settings(
    acquisitions=[zivid.Settings.Acquisition()],
    sampling=zivid.Settings.Sampling(pixel=zivid.Settings.Sampling.Pixel.blueSubsample2x2),
)
frame = camera.capture(settings_subsampled)
estimated_intrinsics_for_subsampled_settings = zivid.experimental.calibration.estimate_intrinsics(frame)

保存相机内参

可以将OpenCV相机内参保存到YML文件中:

跳转到源码

源码

const std::string outputFile = "Intrinsics.yml";
std::cout << "Saving camera intrinsics to file: " << outputFile << std::endl;
intrinsics.save(outputFile);
跳转到源码

源码

var intrinsicsFile = "Intrinsics.yml";
Console.WriteLine("Saving camera intrinsics to file: " + intrinsicsFile);
intrinsics.Save(intrinsicsFile);
跳转到源码

源码

output_file = "Intrinsics.yml"
print(f"Saving camera intrinsics to file: {output_file}")
intrinsics.save(output_file)

Halcon相机内参

Halcon使用的是不同于Zivid和OpenCV的相机模型。有两种方法可以获得Halcon内参,两者都基于OpenCV模型的近似值。

小心

如果您需要使用Halcon内参(相机内部参数),请使用下面描述的方法之一来获取它们。不要在Halcon中标定我们的2D相机来获得Halcon内参,该方法不适用于我们的相机。

要求:

  • 安装了Python和具备运行Python脚本的技能

  • 安装了 Zivid-Python

为您的相机获取Halcon内参的最简单方法是运行 convert_intrinsics_opencv_to_halcon.py 代码示例。阅读示例描述,重点关注 example when reading from camera

备注

此方法仅限于获取硬编码的相机内参。

要求:

  • 安装了Python和具备运行Python脚本的技能

  • 构建C++或C#代码示例的技能

为您的相机获取Halcon内参的另一种方法是从YML文件加载OpenCV相机内参并将它们转换为Halcon格式。

要获取OpenCV相机内参并将它们保存到文件中,请运行以下示例之一:

要获取OpenCV相机内参并将它们保存到文件中,请运行 GetCameraIntrinsics.cpp 。查看 使用 CMake 配置 C++ 示例并在 Windows 的 Visual Studio 中构建它们

要获取OpenCV相机内参并将它们保存到文件中,请运行 GetCameraIntrinsics.cs 。查看 使用Visual Studio构建C#示例 构建C#示例。

要获取OpenCV相机内参并将它们保存到文件中,请运行 get_camera_intrinsics.py

然后,运行 convert_intrinsics_opencv_to_halcon.py 代码示例。阅读示例描述,重点关注*Example when reading from file*。

备注

这种方法允许获得硬编码的相机内参和从点云估计的相机内参。但是对于后面一种方式,您需要保存和加载您需要的内参的每一次捕获。

我应该使用哪种相机内参?

硬编码相机内参

我们建议仅在以下情况下使用硬编码相机内参:

  • 您可以从以下任一方式获得彩色图像:

    • 2D捕获

    • 单次采集3D捕获

    • Color Mode 设置为 UseFirstAcquisition 的多次采集HDR 3D捕获。

  • 对于您的相关采集(上述三个选项之一),您可以使用我们提供硬编码内参的光圈值(参见 )。

  • 接近*室内*温度的环境温度。

  • 仅使用2D数据的应用,例如,使图像不失真以检测直线。

通过点云估计相机内参

我们建议在以下情况使用估计的内参:

  • 您从多次采集HDR 3D捕获中获取彩色图像,且 Color Mode 被设置为 AutomaticToneMapping 。这是因为点云很可能是使用不同光圈和在不同于对应硬编码内参的温度下进行采集的结果。

  • 存在必须使用OpenCV内参的任何其他用例时,例如:

    • 使用projectPoints()将3D数据投影到2D图像。

    • 使用solvePnP()从2D图像估计3D位姿。

备注

估计的相机内参也适用于我们推荐硬编码相机内参的所有情况。因此,为了简单起见,您始终可以使用 estimateIntrinsics 。然而,从点云估计内参需要消耗更多时间,而从相机获取硬编码内参是瞬时的。

此外,估计的内参使用了 3D 捕获设置。这意味着内参对应着 3D 分辨率。如果您进行单独的 2D 捕获,而且分辨率与 3D 不同,那么您将得到不正确的内参。在这种情况下,请使用硬编码方法。

不同分辨率的 2D 和 3D 捕获

根据 2D 图像的分辨率,您将需要不同的内参。当您执行 Monochrome Capture(单色捕获) 时,您可能会获得不同的 2D 和 3D 分辨率。如果您从 3D 捕获中获取 2D 数据,则 estimateIntrinsics 将返回您需要的内参。如果您从单独的 2D 捕获中获取 2D 数据,则可以调用 intrinsics(camera, settings_2d) ,其中 settings_2d 是用于 2D 捕获的设置。

备注

estimateIntrinsics 提供了最好的内参。因此,即使您捕获单独的 2D,我们也建议使用这些内参。但是,如果 2D 分辨率不同,您将必须相应地修改内参。在这种情况下,请通过 customersuccess@zivid.com 联系我们 。

您可以对 2D 图像进行降采样或子采样来匹配 3D 分辨率,而不是创建内参来匹配 2D 分辨率。

手眼标定

一般来说,我们推荐的手眼标定是Zivid 手眼标定 ,因为它最适合Zivid相机。我们的手眼标定方法不需要相机内参,但是许多其它方法需要内参。如果您使用这些方法中的某一种,我们建议使用从点云估计的相机内参。阅读有关 选择正确的手眼标定方法 了解更多信息。

将3D点投影到2D图像平面

在将Zivid点云投影到2D图像平面时,使用估计的内参将获得比使用硬编码内参更小的重新投影误差。但是请记住,如果使用我们的校正过滤器,比如 Gaussian Smoothing(高斯平滑)Contrast Distortion Filter(对比度失真过滤器) ,那么将导致更大的重新投影误差。当在没有使用校正过滤器的情况下捕获的点云上使用估计的内参时,预计的重新投影误差小于1个像素。当使用了校正过滤器时,对于显着校正的点,重新投影误差会更大。然而,对于大多数点,应该仅有小于1个像素的重新投影误差。

备注

降采样或子采样 2D

当您对 2D 数据进行子采样时,您可以从 Monochrome Capture(单色捕获) 中获取2D 和 3D 之间的直接一对一映射。

现在考虑对全分辨率 2D 图像进行降采样。了解哪些像素用于生成降采样像素非常重要。如果您在所需像素周围对称地使用像素,则平均像素应落在与 3D 数据相对应的像素位置上。这与 SDK 提供的内参最相符。

更多相关信息,请参阅 全分辨率 2D 图像和子采样点云之间的映射

从2D图像估计3D位姿

假设您正在根据2D图像估计对象的3D位姿,例如,使用OpenCV中的solvePnP()。与硬编码相机内参相比,从点云估计的内参将提供更好的结果。但是,我们不建议从2D图像中估计位姿,因为可以直接从点云中更准确地估计位姿。

2D图像处理算法

如果您的应用只使用2D数据,那么硬编码的内参和从点云估计的内参都可以正常工作。一个例子是校正2D图像的失真来校正镜头失真,从而保证直线的检测。

版本历史

SDK

变更

2.10.0

Monochrome Capture(单色捕获) 需要在使用它们之前修改内参。