Capture Tutorial
Introduction
To capture a point cloud or 2D image with a Zivid camera: initialize the Zivid application, connect to the camera, configure capture settings, then capture and save the result.
For the corresponding API, see Camera Basic.
Prerequisites
Install Zivid Software.
For Python: install zivid-python
Initialize
Calling any of the APIs in the Zivid SDK requires initializing the Zivid application and keeping it alive while the program runs.
Note
Application must be kept alive while operating the Zivid Camera.
This is essentially the Zivid driver.
Connect
Now we can connect to the camera.
Specific Camera
Sometimes multiple cameras are connected to the same computer, but it can be necessary to work with a specific camera in the code. This can be done by providing the serial number of the wanted camera.
Note
The serial number of your camera is shown in the Zivid Studio.
It is also possible to connect to a camera directly by its IP address or hostname. This bypasses mDNS discovery, so connecting is no longer limited to auto-discovery or serial number.
When to connect by IP address or hostname
Connecting directly is useful when:
The camera is on a different subnet than the PC, where it would otherwise be unreachable.
mDNS is unavailable, for example when it is blocked by network policy, switches, or firewalls, or when running in a Docker container, whose default bridge network does not allow the multicast traffic mDNS relies on.
You want to connect programmatically, without relying on discovery or on a
Cameras.ymlfile to list the cameras.
This commonly applies to production cells with fixed camera IPs, multi-camera stations on managed networks, and locked-down or segmented industrial networks.
The camera’s default hostname follows the format zivid-<serial-number>.local.
It is not possible to get or set the hostname through the SDK.
To do so, use the ZividNetworkCameraConfigurator CLI tool, as described in DHCP Network Configuration.
You can also list all cameras connected to the computer, and view their serial numbers using Application::cameras:
auto cameras = zivid.cameras();
std::cout << "Found " << cameras.size() << " cameras" << std::endl;
for(auto &camera : cameras)
{
std::cout << camera.info() << std::endl;
std::cout << camera.state() << std::endl;
}
You can then connect to all available cameras.
std::vector<Zivid::Camera> connectedCameras;
for(auto &camera : zivid.cameras())
{
if(camera.state().status() == Zivid::CameraState::Status::available)
{
std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl;
camera.connect();
connectedCameras.push_back(camera);
}
else
{
std::cout << "Camera " << camera.info().serialNumber() << " is not available. "
<< "Camera status: " << camera.state().status() << std::endl;
}
}
var connectedCameras = new List<Zivid.NET.Camera>();
foreach (var camera in zivid.Cameras)
{
if (camera.State.Status == Zivid.NET.CameraState.StatusOption.Available)
{
Console.WriteLine("Connecting to camera: " + camera.Info.SerialNumber);
camera.Connect();
connectedCameras.Add(camera);
}
else
{
Console.WriteLine("Camera " + camera.Info.SerialNumber + " is not available. "
+ "Camera status: " + camera.State.Status);
}
}
connected_cameras = []
for camera in app.cameras():
if camera.state.status == zivid.CameraState.Status.available:
print(f"Connecting to camera: {camera.info.serial_number}")
camera.connect()
connected_cameras.append(camera)
else:
print(f"Camera {camera.info.serial_number} is not available. Camera status: {camera.state.status}")
Configure
As with all cameras there are settings that can be configured.
Presets
The recommendation is to use Presets available in Zivid Studio and as .yml files (see the Load and Save sections). Presets work well for most cases right away, making them a great starting point. If needed, you can fine-tune the settings for better results. You can edit the YAML files in any text editor or code the settings manually.
Load
You can export camera settings to .yml files from Zivid Studio. These can be loaded and applied in the API.
const auto settingsFile = "Settings.yml";
std::cout << "Loading settings from file: " << settingsFile << std::endl;
const auto settingsFromFile = Zivid::Settings(settingsFile);
Save
You can also save settings to .yml file.
Manual configuration
Another option is to configure settings manually. For more information about what each settings does, please see Camera Settings. Then, the next step is Capturing High Quality Point Clouds
Single 2D and 3D Acquisition - Default settings
We can create settings for a single acquisition capture.
const auto settings =
Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
Zivid::Settings::Color{ Zivid::Settings2D{
Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };
Multi Acquisition HDR
We can also create settings with multiple acquisitions for an HDR capture.
using std::chrono::microseconds;
Zivid::Settings settings;
for(const auto exposure : { microseconds{ 1000 }, microseconds{ 10000 } })
{
std::cout << "Adding acquisition with exposure time of " << exposure.count() << " microseconds "
<< std::endl;
const auto acquisitionSettings = Zivid::Settings::Acquisition{
Zivid::Settings::Acquisition::ExposureTime{ exposure },
};
settings.acquisitions().emplaceBack(acquisitionSettings);
}
var settings = new Zivid.NET.Settings();
foreach (var exposure in new Duration[] { Duration.FromMicroseconds(1000), Duration.FromMicroseconds(10000) })
{
Console.WriteLine("Adding acquisition with exposure time of " + exposure.Microseconds + " microseconds");
var acquisitionSettings = new Zivid.NET.Settings.Acquisition { ExposureTime = exposure };
settings.Acquisitions.Add(acquisitionSettings);
}
settings.Color = new Zivid.NET.Settings2D { Acquisitions = { new Zivid.NET.Settings2D.Acquisition { } } };
settings = zivid.Settings()
for exposure in [1000, 10000]:
print(f"Adding acquisition with exposure time of {exposure} microseconds")
settings.acquisitions.append(
zivid.Settings.Acquisition(exposure_time=datetime.timedelta(microseconds=exposure))
)
Fully Configured Settings
2D Settings, such as color balance and gamma, configured manually:
std::cout << "Configuring settings for capture:" << std::endl;
Zivid::Settings2D settings2D{
Zivid::Settings2D::Sampling::Color::rgb,
Zivid::Settings2D::Sampling::Pixel::all,
Zivid::Settings2D::Sampling::Interval::Enabled::no,
Zivid::Settings2D::Sampling::Interval::Duration{ microseconds{ 10000 } },
Zivid::Settings2D::Processing::Color::Balance::Blue{ 1.0 },
Zivid::Settings2D::Processing::Color::Balance::Green{ 1.0 },
Zivid::Settings2D::Processing::Color::Balance::Red{ 1.0 },
Zivid::Settings2D::Processing::Color::Gamma{ 1.0 },
Zivid::Settings2D::Processing::Color::Experimental::Mode::automatic,
};
Console.WriteLine("Configuring settings for capture:");
var settings2D = new Zivid.NET.Settings2D()
{
Sampling =
{
Color = Zivid.NET.Settings2D.SamplingGroup.ColorOption.Rgb,
Pixel = Zivid.NET.Settings2D.SamplingGroup.PixelOption.All,
Interval =
{
Enabled = false,
Duration = Duration.FromMicroseconds(10000),
},
},
Processing =
{
Color =
{
Balance =
{
Blue = 1.0,
Green = 1.0,
Red = 1.0,
},
Gamma = 1.0,
Experimental = { Mode = Zivid.NET.Settings2D.ProcessingGroup.ColorGroup.ExperimentalGroup.ModeOption.Automatic },
},
},
};
print("Configuring settings for capture:")
settings_2d = zivid.Settings2D()
settings_2d.sampling.color = zivid.Settings2D.Sampling.Color.rgb
settings_2d.sampling.pixel = zivid.Settings2D.Sampling.Pixel.all
settings_2d.sampling.interval.enabled = False
settings_2d.sampling.interval.duration = timedelta(microseconds=10000)
settings_2d.processing.color.balance.red = 1.0
settings_2d.processing.color.balance.blue = 1.0
settings_2d.processing.color.balance.green = 1.0
settings_2d.processing.color.gamma = 1.0
settings_2d.processing.color.experimental.mode = zivid.Settings2D.Processing.Color.Experimental.Mode.automatic
Manually configured 3D settings such as engine, region of interest, filter settings and more:
Zivid::Settings settings{
Zivid::Settings::Color{ settings2D },
Zivid::Settings::Engine::stripe,
Zivid::Settings::RegionOfInterest::Box::Enabled::yes,
Zivid::Settings::RegionOfInterest::Box::PointO{ 1000, 1000, 1000 },
Zivid::Settings::RegionOfInterest::Box::PointA{ 1000, -1000, 1000 },
Zivid::Settings::RegionOfInterest::Box::PointB{ -1000, 1000, 1000 },
Zivid::Settings::RegionOfInterest::Box::Extents{ -1000, 1000 },
Zivid::Settings::RegionOfInterest::Depth::Enabled::yes,
Zivid::Settings::RegionOfInterest::Depth::Range{ 200, 2000 },
Zivid::Settings::Processing::Filters::Cluster::Removal::Enabled::yes,
Zivid::Settings::Processing::Filters::Cluster::Removal::MaxNeighborDistance{ 10 },
Zivid::Settings::Processing::Filters::Cluster::Removal::MinArea{ 100 },
Zivid::Settings::Processing::Filters::Hole::Repair::Enabled::yes,
Zivid::Settings::Processing::Filters::Hole::Repair::HoleSize{ 0.2 },
Zivid::Settings::Processing::Filters::Hole::Repair::Strictness{ 1 },
Zivid::Settings::Processing::Filters::Noise::Removal::Enabled::yes,
Zivid::Settings::Processing::Filters::Noise::Removal::Threshold{ 7.0 },
Zivid::Settings::Processing::Filters::Noise::Suppression::Enabled::yes,
Zivid::Settings::Processing::Filters::Noise::Repair::Enabled::yes,
Zivid::Settings::Processing::Filters::Outlier::Removal::Enabled::yes,
Zivid::Settings::Processing::Filters::Outlier::Removal::Threshold{ 5.0 },
Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes,
Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global,
Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes,
Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 },
Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Correction::Enabled::yes,
Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Correction::Strength{ 0.4 },
Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Enabled::no,
Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Threshold{ 0.5 },
Zivid::Settings::Processing::Resampling::Mode::upsample2x2,
Zivid::Settings::Diagnostics::Enabled::no,
};
setSamplingPixel(settings, camera);
std::cout << settings << std::endl;
var settings = new Zivid.NET.Settings()
{
Engine = Zivid.NET.Settings.EngineOption.Stripe,
RegionOfInterest =
{
Box = {
Enabled = true,
PointO = new Zivid.NET.PointXYZ{ x = 1000, y = 1000, z = 1000 },
PointA = new Zivid.NET.PointXYZ{ x = 1000, y = -1000, z = 1000 },
PointB = new Zivid.NET.PointXYZ{ x = -1000, y = 1000, z = 1000 },
Extents = new Zivid.NET.Range<double>(-1000, 1000),
},
Depth =
{
Enabled = true,
Range = new Zivid.NET.Range<double>(200, 2000),
},
},
Processing =
{
Filters =
{
Cluster =
{
Removal = { Enabled = true, MaxNeighborDistance = 10, MinArea = 100}
},
Hole =
{
Repair = { Enabled = true, HoleSize = 0.2, Strictness = 1 },
},
Noise =
{
Removal = { Enabled = true, Threshold = 7.0 },
Suppression = { Enabled = true },
Repair = { Enabled = true },
},
Outlier =
{
Removal = { Enabled = true, Threshold = 5.0 },
},
Reflection =
{
Removal = { Enabled = true, Mode = ReflectionFilterModeOption.Global },
},
Smoothing =
{
Gaussian = { Enabled = true, Sigma = 1.5 },
},
Experimental =
{
ContrastDistortion =
{
Correction = { Enabled = true, Strength = 0.4 },
Removal = { Enabled = true, Threshold = 0.5 },
},
},
},
Resampling = { Mode = Zivid.NET.Settings.ProcessingGroup.ResamplingGroup.ModeOption.Upsample2x2 },
},
Diagnostics = { Enabled = false },
};
settings.Color = settings2D;
SetSamplingPixel(ref settings, camera);
Console.WriteLine(settings);
settings = zivid.Settings()
settings.engine = zivid.Settings.Engine.stripe
settings.region_of_interest.box.enabled = True
settings.region_of_interest.box.point_o = [1000, 1000, 1000]
settings.region_of_interest.box.point_a = [1000, -1000, 1000]
settings.region_of_interest.box.point_b = [-1000, 1000, 1000]
settings.region_of_interest.box.extents = [-1000, 1000]
settings.region_of_interest.depth.enabled = True
settings.region_of_interest.depth.range = [200, 2000]
settings.processing.filters.cluster.removal.enabled = True
settings.processing.filters.cluster.removal.max_neighbor_distance = 10
settings.processing.filters.cluster.removal.min_area = 100
settings.processing.filters.hole.repair.enabled = True
settings.processing.filters.hole.repair.hole_size = 0.2
settings.processing.filters.hole.repair.strictness = 1
settings.processing.filters.noise.removal.enabled = True
settings.processing.filters.noise.removal.threshold = 7.0
settings.processing.filters.noise.suppression.enabled = True
settings.processing.filters.noise.repair.enabled = True
settings.processing.filters.outlier.removal.enabled = True
settings.processing.filters.outlier.removal.threshold = 5.0
settings.processing.filters.reflection.removal.enabled = True
settings.processing.filters.reflection.removal.mode = (
zivid.Settings.Processing.Filters.Reflection.Removal.Mode.global_
)
settings.processing.filters.smoothing.gaussian.enabled = True
settings.processing.filters.smoothing.gaussian.sigma = 1.5
settings.processing.filters.experimental.contrast_distortion.correction.enabled = True
settings.processing.filters.experimental.contrast_distortion.correction.strength = 0.4
settings.processing.filters.experimental.contrast_distortion.removal.enabled = False
settings.processing.filters.experimental.contrast_distortion.removal.threshold = 0.5
settings.processing.resampling.mode = zivid.Settings.Processing.Resampling.Mode.upsample2x2
settings.diagnostics.enabled = False
settings.color = settings_2d
_set_sampling_pixel(settings, camera)
print(settings)
Different values per acquisition are also possible:
std::cout << "Configuring acquisition settings different for all HDR acquisitions" << std::endl;
const auto baseAcquisition = Zivid::Settings::Acquisition{};
std::cout << baseAcquisition << std::endl;
auto exposureValues = getExposureValues(camera);
const std::vector<double> aperture = std::get<0>(exposureValues);
const std::vector<double> gain = std::get<1>(exposureValues);
const std::vector<std::chrono::microseconds> exposureTime = std::get<2>(exposureValues);
const std::vector<double> brightness = std::get<3>(exposureValues);
for(size_t i = 0; i < aperture.size(); ++i)
{
std::cout << "Acquisition " << i + 1 << ":" << std::endl;
std::cout << " Exposure Time: " << exposureTime.at(i).count() << std::endl;
std::cout << " Aperture: " << aperture.at(i) << std::endl;
std::cout << " Gain: " << gain.at(i) << std::endl;
std::cout << " Brightness: " << brightness.at(i) << std::endl;
const auto acquisitionSettings = baseAcquisition.copyWith(
Zivid::Settings::Acquisition::Aperture{ aperture.at(i) },
Zivid::Settings::Acquisition::Gain{ gain.at(i) },
Zivid::Settings::Acquisition::ExposureTime{ exposureTime.at(i) },
Zivid::Settings::Acquisition::Brightness{ brightness.at(i) });
settings.acquisitions().emplaceBack(acquisitionSettings);
}
const auto aquisitionSettings2D = makeSettings2D(camera).acquisitions();
settings.color().value().set(aquisitionSettings2D);
Console.WriteLine("Configuring acquisition settings different for all HDR acquisitions:");
var baseAcquisition = new Zivid.NET.Settings.Acquisition { };
Console.WriteLine(baseAcquisition);
var baseAcquisition2D = new Zivid.NET.Settings2D.Acquisition { };
Tuple<double[], Duration[], double[], double[]> exposureValues = GetExposureValues(camera);
double[] aperture = exposureValues.Item1;
Duration[] exposureTime = exposureValues.Item2;
double[] gain = exposureValues.Item3;
double[] brightness = exposureValues.Item4;
for (int i = 0; i < aperture.Length; i++)
{
Console.WriteLine("Acquisition {0}:", i + 1);
Console.WriteLine(" Exposure Time: {0}", exposureTime[i].Microseconds);
Console.WriteLine(" Aperture: {0}", aperture[i]);
Console.WriteLine(" Gain: {0}", gain[i]);
Console.WriteLine(" Brightness: {0}", brightness[i]);
var acquisitionSettings = baseAcquisition.CopyWith(s =>
{
s.Aperture = aperture[i];
s.ExposureTime = exposureTime[i];
s.Gain = gain[i];
s.Brightness = brightness[i];
});
settings.Acquisitions.Add(acquisitionSettings);
}
var aquisitionSettings2D = MakeSettings2D(camera);
settings.Color.Acquisitions = aquisitionSettings2D.Acquisitions;
print("Configuring acquisition settings different for all HDR acquisitions")
exposure_values = _get_exposure_values(camera)
for aperture, gain, exposure_time, brightness in exposure_values:
settings.acquisitions.append(
zivid.Settings.Acquisition(
aperture=aperture,
exposure_time=exposure_time,
brightness=brightness,
gain=gain,
)
)
acquisition_settings_2d = make_settings_2d(camera).Acquisition()
settings.color.acquisitions.append(acquisition_settings_2d)
Capture 2D3D
Now we can capture a 2D and 3D image (point cloud with color).
Whether there is a single acquisition or multiple acquisitions (HDR) is given by the number of acquisitions in settings.
The Frame contains the point cloud, the color image, the capture, and the camera information (all of which are stored on the compute device memory).
The Frame contains the point cloud, the color image, the capture, and the camera information (all of which are stored on the compute device memory).
The zivid.Frame contains the point cloud, the color image, the capture, and the camera information (all of which are stored on the compute device memory).
Capture 3D
If we only want to capture 3D, the points cloud without color, we can do so via the capture3D API.
Capture 2D
If we only want to capture a 2D image, which is faster than 3D, we can do so via the capture2D API.
Save
We can now save our results.
Tip
You can open and view Frame.zdf file in Zivid Studio.
Export
In the next code example, the point cloud is exported to the .ply format. For other exporting options, see Point Cloud for a list of supported formats.
Load
Once saved, the frame can be loaded from a ZDF file.
const auto dataFile = std::string(ZIVID_SAMPLE_DATA_DIR) + "/Zivid3D.zdf";
std::cout << "Reading ZDF frame from file: " << dataFile << std::endl;
const auto frame = Zivid::Frame(dataFile);
Save 2D
From a capture2D() you get a Frame2D.
There are two color spaces available for 2D images: linear RGB and sRGB.
The imageRGBA() will return an image in the linear RGB color space.
If you append _SRGB to the function name then the returned image will be in the sRGB color space.
First, get the 2D image from the capture in either color space.
Get image (Linear RGB)
Get the 2D image in the linear RGB color space:
Get image (sRGB)
Get the 2D image in the sRGB color space:
Then, we can save the 2D image in linear RGB or sRGB color space.
Save image (Linear RGB)
Save the 2D image in the linear RGB color space:
const auto imageFile = "ImageRGBA_linear.png";
std::cout << "Saving 2D color image (Linear RGB) to file: " << imageFile << std::endl;
imageRGBA.save(imageFile);
Save image (sRGB)
Save the 2D image in the sRGB color space:
const auto imageFile = "ImageRGBA_sRGB.png";
std::cout << "Saving 2D color image (sRGB color space) to file: " << imageFile << std::endl;
imageSRGB.save(imageFile);
We can get 2D color image directly from the point cloud. This image will have the same resolution as the point cloud and it will be in the sRGB color space.
const auto pointCloud = frame.pointCloud();
const auto image2DInPointCloudResolution = pointCloud.copyImageRGBA_SRGB();
var pointCloud = frame.PointCloud;
var image2DInPointCloudResolution = pointCloud.CopyImageRGBA_SRGB();
point_cloud = frame.point_cloud()
image_2d_in_point_cloud_resolution = point_cloud.copy_image("bgra_srgb")
We can get the 2D color image from Frame2D, which is part of the Frame object, obtained from capture2D3D().
This image will have the resolution given by the 2D settings inside the 2D3D settings.
const auto image2D = frame.frame2D().value().imageBGRA_SRGB();
var image2D = frame.Frame2D.ImageBGRA_SRGB();
image_2d = frame.frame_2d().image_bgra_srgb()
File Camera
With a file camera, you can experiment with the SDK without access to a physical camera. The file cameras can be found in Sample Data where there are multiple file cameras to choose from.
The acquisition settings must be initialized as shown in the example that follows, but you are free to alter the processing settings.
std::cout << "Configuring settings" << std::endl;
Zivid::Settings settings{
Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes,
Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 },
Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes,
Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global,
};
settings.set(
Zivid::Settings::RegionOfInterest::Box{
Zivid::Settings::RegionOfInterest::Box::Enabled::yes,
Zivid::Settings::RegionOfInterest::Box::PointO{ { -266, 190, 771 } },
Zivid::Settings::RegionOfInterest::Box::PointA{ { 203, 207, 771 } },
Zivid::Settings::RegionOfInterest::Box::PointB{ { -255, -131, 771 } },
Zivid::Settings::RegionOfInterest::Box::Extents{ 0, 298 } });
Zivid::Settings2D settings2D{ Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } };
settings.color() = Zivid::Settings::Color{ settings2D };
Console.WriteLine("Configuring settings");
var settings2D = new Zivid.NET.Settings2D
{
Acquisitions = { new Zivid.NET.Settings2D.Acquisition { } }
};
var settings = new Zivid.NET.Settings
{
Acquisitions = { new Zivid.NET.Settings.Acquisition { } },
Processing =
{
Filters =
{
Smoothing =
{
Gaussian = { Enabled = true, Sigma = 1.5 }
},
Reflection =
{
Removal = { Enabled = true, Mode = ReflectionFilterModeOption.Global}
}
}
}
};
var roiBox = new Zivid.NET.Settings.RegionOfInterestGroup.BoxGroup
{
Enabled = true,
PointO = new Zivid.NET.PointXYZ { x = -266, y = 190, z = 771 },
PointA = new Zivid.NET.PointXYZ { x = 203, y = 207, z = 771 },
PointB = new Zivid.NET.PointXYZ { x = -255, y = -131, z = 771 }
};
roiBox.Extents = new Zivid.NET.Range<double>(0, 298);
settings.RegionOfInterest.Box = roiBox;
settings.Color = settings2D;
print("Configuring settings")
settings = zivid.Settings()
settings.acquisitions.append(zivid.Settings.Acquisition())
settings.processing.filters.smoothing.gaussian.enabled = True
settings.processing.filters.smoothing.gaussian.sigma = 1.5
settings.processing.filters.reflection.removal.enabled = True
settings.processing.filters.reflection.removal.mode = (
zivid.Settings.Processing.Filters.Reflection.Removal.Mode.global_
)
settings.region_of_interest.box = zivid.Settings.RegionOfInterest.Box(
enabled=True,
point_o=(-266, 190, 771),
point_a=(203, 207, 771),
point_b=(-255, -131, 771),
extents=(0, 298),
)
settings_2d = zivid.Settings2D()
settings_2d.acquisitions.append(zivid.Settings2D.Acquisition())
settings.color = settings_2d
You can read more about the file camera option in File Camera.
Multithreading
Operations on camera objects are thread-safe, but other operations like listing cameras and connecting to cameras must be executed in sequence. Find out more in Multithreading.
Conclusion
This tutorial shows how to use the Zivid SDK to connect to, configure, capture, and save from the Zivid camera.
Version History
SDK |
Changes |
|---|---|
2.18.0 |
Added connecting to a camera by IP address or hostname. |