.NET Programmer's Guide#
Linux
This topic provides Windows-specific information about how to access Basler blaze cameras using .NET.
This topic provides information that is specific to Basler blaze cameras and doesn't aim to give a comprehensive overview of the pylon .NET API.
If you are new to pylon, Basler recommends making yourself familiar with the pylon .NET API first by reading the pylon .NET Programmer's Guide.
.NET Programming Samples#
To open the folder containing the programming samples for blaze cameras, press the Win key, go to the Basler folder, and choose blaze Samples. A File Explorer window opens. The .NET programming samples are located in the DotNet folder.
How to Build the Samples#
信息
Before building the samples, copy the folder containing the samples to a location of your choice where you have read and write access.
A Visual Studio solution and project files are provided for the samples. You can open the solution and build the samples with Visual Studio 2017 or newer.
How to Build .NET Applications#
To operate Basler blaze cameras using C# or Visual Basic .NET, you have to add the Basler.Pylon
and Basler.Pylon.Blaze
assemblies to your project's list of assembly references.
To add the assemblies:
- Right-click your project in the Visual Studio Solution Explorer and select the Add > Project Reference.
A Reference Manager window opens. - Click the Browse button.
- Navigate to the %Program Files%\Basler\pylon 8\Development\Assemblies\Basler.Pylon\x64 folder.
- Select the Basler.Pylon.dll, and click the Add button.
- Click the Browse button.
- Navigate to the %Program Files%\Basler\pylon 8\Development\Assemblies\Basler.Pylon.Blaze\AnyCpu folder.
- Select the Basler.Pylon.Blaze.dll, and click the Add button.
Processor Architecture of the Basler.Pylon Assemblies
It doesn't matter whether you choose the assembly targeting 32-bit or the one targeting 64-bit platforms. Since the assemblies have been installed in the Global Assembly Cache (GAC), the .NET assembly loader will automatically choose the one matching the architecture your application is built for.
To avoid warnings about potential architecture mismatches when building your application, Basler recommends editing the Visual Studio project file manually to suppress such warnings. To do so:
- In the Solution Explorer, right-click your project and choose Unload project.
- Right-click the project again and choose Edit project.
The project's XML file is opened in an editor window. -
In the XML file, navigate to the location where the
Basler.Pylon
assembly is referenced, i.e., search for a fragment similar to the following: -
Above the
ItemGroup
element, add the following XML fragment:<PropertyGroup> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup>
After adding the elements, your project file should contain a segment like this:
<PropertyGroup> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup> <ItemGroup> .... <Reference Include="Basler.Pylon"> <HintPath>..\..\..\..\..\..\Program Files\Basler\pylon\Development\Assemblies\Basler.Pylon\x64\Basler.Pylon.dll</HintPath> </Reference> <Reference Include="Basler.Pylon.Blaze"> <HintPath>..\..\..\..\..\..\Program Files\Basler\pylon\Development\Assemblies\Basler.Pylon.Blaze\AnyCpu\Basler.Pylon.Blaze.dll</HintPath> </Reference> .... </ItemGroup>
-
When finished editing, reload your project by right-clicking the project and choosing Reload project.
When asked, confirm to close the project file and to save pending changes.
Opening and Accessing blaze Cameras#
The pylon .NET API provides a single Camera
class for all camera types. Overloaded constructors are available to select a specific camera device.
It takes three steps to create and open a blaze camera object:
-
Create a camera object for the camera you want to access.
-
Register a blaze-specific parameter configuration that applies a default configuration. Refer to the Predefined Camera Parameter Configurations section for more details about the blaze configuration event handler classes.
-
Call the
Open()
method to establish a connection to the camera device.
The following example illustrates the three steps:
using Basler.Pylon;
using Basler.Pylon.Blaze;
// ...
var filter = new Dictionary<string, string> {
{ CameraInfoKey.DeviceType, DeviceType.BaslerGenTlBlazeDeviceClass }
};
using (var camera = new Camera(filter, CameraSelectionStrategy.FirstFound))
{
// Set up the camera for continuously grabbing point clouds.
camera.CameraOpened += BlazeConfigurations.AcquirePointCloudsContinuously;
// Open the connection to the camera device.
camera.Open();
// Use the camera.
// Close the connection.
camera.Close();
}
Imports Basler.Pylon
Imports Basler.Pylon.Blaze
' ...
Dim filter = New Dictionary(Of String, String) From {
{CameraInfoKey.DeviceType, DeviceType.BaslerGenTlBlazeDeviceClass}
}
Using camera = New Camera(filter, CameraSelectionStrategy.FirstFound)
' Set up the camera for continuously grabbing point clouds.
AddHandler camera.CameraOpened,
AddressOf BlazeConfigurations.AcquirePointCloudsContinuously
' Open the connection to the camera device.
camera.Open()
' Use the camera.
' Close the connection.
camera.Close()
End Using
Creating a Camera Object for the First Available blaze Camera#
The Camera
class provides a constructor that allows you to pass a list of key-value pairs specifying properties of the camera device the camera object was created for.
To open the first available blaze camera, it's sufficient to specify that the device type is equal to the blaze device class.
Creating a Camera Object for a Specific blaze Camera#
If you want to open a specific blaze camera, e.g., by specifying its serial number, in addition to passing the pylon device class identifier for blaze cameras to CreateFirstDevice()
, you can specify further properties.
The following code snippets illustrate how to open a blaze camera by serial number.
To open a blaze camera by user-defined name, use the following lines:
User-Defined Names
You can assign a user-defined name to a camera using the DeviceUserID
parameter. You can do this in the blaze Viewer, the pylon IP Configurator, or the pylon API:
Creating a Device by Its IP Address
Currently, you can't create a device by specifying its IP address.
If you want to create and open a camera with a specific IP address, you first have to enumerate all cameras, iterate the returned list, and open each camera before you can query the IP address and other network-related information.
If the desired blaze camera is not found, check whether you're using the right values for the properties and check your camera's and your network adapter's IP configuration settings. Refer to the Network Configuration topic for more information about setting up your network.
Predefined Camera Parameter Configurations#
For blaze cameras, there is the BlazeConfigurations
class. The methods of this class can be added to the CameraOpened
event. This must be done before opening the camera by calling the Open()
method. When the camera is opened, the configuration method is automatically called and the camera configuration is changed accordingly.
示例:
The BlazeConfigurations
provides the following configuration methods:
AcquirePointCloudsContinuously
Acquire data continuously. Depth data is represented as point clouds.AcquireDepthMapsContinuously
Acquire data continuously. Depth data is represented as depth maps.
Refer to the Predefined Camera Parameter Configurations section of the pylon .NET Programmer's Guide to learn more about pylon's configuration event handlers.
Interpreting Depth Data#
The Processing Measurement Results topic provides information about the format of the depth data stored in the Range data component.
Connecting to a Camera#
After creating a camera object and registering a configuration event handler, the connection to the camera is established by calling the CBlazeInstantCamera::Open()
method.
The connection is kept alive until the CBlazeInstantCamera::Close()
method is called. The destructor of the Camera
class calls Close()
automatically if it hasn't been called explicitly. To have explicit control over the lifetime of a camera object, Basler recommends using the camera object inside a using statement as shown in the code snippets above.
The camera closes the connection if it doesn't periodically receive heartbeat requests from the camera. Missing heartbeat requests occur if the application crashes or is interrupted by a debugger. Refer to the Debugging Applications and Controlling the GigE Vision Heartbeat section for more details.
Accessing Parameters#
Refer to the Parameter Access section of the pylon .NET Programmer's Guide to get familiar with how to access camera parameters using the pylon .NET API.
The pylon .NET API provides parameter lists as a convenient method for accessing camera parameters, assisted by Visual Studio's IntelliSense.
For blaze cameras, there is the PLBlaze
parameter list.
The following code snippets demonstrate how to set and get parameters using the PLBlaze
parameter list.
// Set the operating mode of the camera. The choice you make here
// affects the working range of the camera, i.e., the Minimum Working
// Range and Maximum Working Range parameters.
var oldOperatingMode = camera.Parameters[PLBlaze.OperatingMode].GetValue();
camera.Parameters[PLBlaze.OperatingMode].SetValue(PLBlaze.OperatingMode.LongRange);
// Exposure time of the camera. If the operating mode is changed, the
// exposure time is set to the recommended default value.
camera.Parameters[PLBlaze.ExposureTime].SetValue(750); // us
// Enable and configure image filtering.
// The spatial noise filter uses the values of neighboring pixels to
// filter out noise in an image.
camera.Parameters[PLBlaze.SpatialFilter].SetValue(true);
// The temporal noise filter uses the values of the same pixel at
// different points in time to filter out noise in an image.
camera.Parameters[PLBlaze.TemporalFilter].SetValue(true);
camera.Parameters[PLBlaze.TemporalFilterStrength].SetValue(220);
// The outlier removal removes pixels that differ significantly from
// their local environment.
camera.Parameters[PLBlaze.OutlierRemoval].SetValue(true);
// Some properties have restrictions.
// We use API functions that automatically perform value corrections.
// Alternatively, you can use GetInc() / GetMin() / GetMax() to make sure you
// set a valid value.
camera.Parameters[PLBlaze.ConfidenceThreshold].SetValue(
321,
IntegerValueCorrection.Nearest
);
// Not all functions are available in older cameras.
// Therefore, we must use "Try" functions that only perform the action
// when parameters are writable. Otherwise, we would get an exception.
camera.Parameters[PLBlaze.MultiCameraChannel].TrySetValue(1);
Console.WriteLine("Operating Mode : {0}",
camera.Parameters[PLBlaze.OperatingMode].GetValue());
Console.WriteLine("Exposure Time : {0}",
camera.Parameters[PLBlaze.ExposureTime].GetValue());
Console.WriteLine("Spatial Filter : {0}",
camera.Parameters[PLBlaze.SpatialFilter].GetValue());
Console.WriteLine("Temporal Filter : {0}",
camera.Parameters[PLBlaze.TemporalFilter].GetValue());
Console.WriteLine("Temporal Filter Strength : {0}",
camera.Parameters[PLBlaze.TemporalFilterStrength].GetValue());
Console.WriteLine("Outlier Removal : {0}",
camera.Parameters[PLBlaze.OutlierRemoval].GetValue());
Console.WriteLine("Confidence Threshold : {0}",
camera.Parameters[PLBlaze.ConfidenceThreshold].GetValue());
if (camera.Parameters[PLBlaze.MultiCameraChannel].IsReadable)
{
Console.WriteLine("Multi-Camera Channel : {0}",
camera.Parameters[PLBlaze.MultiCameraChannel].GetValue());
}
// Restore the old operating mode.
camera.Parameters[PLBlaze.OperatingMode].SetValue(oldOperatingMode);
' Set the operating mode of the camera. The choice you make here
' affects the working range of the camera, i.e., the Minimum Working
' Range and Maximum Working Range parameters.
Dim oldOperatingMode = camera.Parameters(PLBlaze.OperatingMode).GetValue()
camera.Parameters(PLBlaze.OperatingMode).SetValue(PLBlaze.OperatingMode.LongRange)
' Exposure time of the camera. If the operating mode is changed, the
' exposure time is set to the recommended default value
camera.Parameters(PLBlaze.ExposureTime).SetValue(750) ' us
' Enable and configure image filtering.
' The spatial noise filter uses the values of neighboring pixels to
' filter out noise in an image.
camera.Parameters(PLBlaze.SpatialFilter).SetValue(True)
' The temporal noise filter uses the values of the same pixel at
' different points in time to filter out noise in an image.
camera.Parameters(PLBlaze.TemporalFilter).SetValue(True)
camera.Parameters(PLBlaze.TemporalFilterStrength).SetValue(220)
' The outlier removal removes pixels that differ significantly from
' their local environment
camera.Parameters(PLBlaze.OutlierRemoval).SetValue(True)
' Some properties have restrictions.
' We use API functions that automatically perform value corrections.
' Alternatively, you can use GetInc() / GetMin() / GetMax() to make sure you set
' a valid value.
camera.Parameters(PLBlaze.ConfidenceThreshold).SetValue(
321,
IntegerValueCorrection.Nearest
)
' Not all functions are available in older cameras.
' Therefore, we must use "Try" functions that only perform the action
' when parameters are writable. Otherwise, we would get an exception.
camera.Parameters(PLBlaze.MultiCameraChannel).TrySetValue(1)
Console.WriteLine("Operating Mode : {0}",
camera.Parameters(PLBlaze.OperatingMode).GetValue())
Console.WriteLine("Exposure Time : {0}",
camera.Parameters(PLBlaze.ExposureTime).GetValue())
Console.WriteLine("Spatial Filter : {0}",
camera.Parameters(PLBlaze.SpatialFilter).GetValue())
Console.WriteLine("Temporal Filter : {0}",
camera.Parameters(PLBlaze.TemporalFilter).GetValue())
Console.WriteLine("Temporal Filter Strength : {0}",
camera.Parameters(PLBlaze.TemporalFilterStrength).GetValue())
Console.WriteLine("Outlier Removal : {0}",
camera.Parameters(PLBlaze.OutlierRemoval).GetValue())
Console.WriteLine("Confidence Threshold : {0}",
camera.Parameters(PLBlaze.ConfidenceThreshold).GetValue())
If camera.Parameters(PLBlaze.MultiCameraChannel).IsReadable Then
Console.WriteLine("Multi-Camera Channel : {0}",
camera.Parameters(PLBlaze.MultiCameraChannel).GetValue())
End If
' Restore the old operating mode.
camera.Parameters(PLBlaze.OperatingMode).SetValue(oldOperatingMode)
Acquiring Data#
Refer to the Grabbing Images section of the pylon .NET Programmer's Guide to get familiar with how to acquire data using the pylon .NET API.
pylon represents grabbed images as data structures called GrabResults. GrabResults acquired by a 2D camera contain a single gray-value or color image.
GrabResults grabbed by blaze cameras contain multiple components. By default, each GrabResult stores an intensity image, a confidence map, and depth information.
Depending on the pixel format set for the Range component, depth data can be represented as 2D depth maps or 3D point clouds.
信息
It isn't possible to let the camera send depth information as point clouds and depth maps at the same time.
The Processing Measurement Results topic explains how to calculate a point cloud from a depth map.
Grab Loop#
pylon supports different approaches for setting up a grab loop and provides different strategies for handling memory buffers.
Refer to the Grabbing Images section of the pylon .NET Programmer's Guide for more details.
The following code samples illustrate a typical grab loop:
// Open the connection to the camera device.
camera.Open();
// The MaxNumBuffer parameter can be used to control the amount of buffers
// allocated for grabbing. The default value of this parameter is 10.
camera.Parameters[PLCameraInstance.MaxNumBuffer].SetValue(3);
// Start grabbing.
camera.StreamGrabber.Start();
// Grab a number of images.
for (int i = 0; i < 10; ++i)
{
// Wait for an image and then retrieve it. A timeout of 1000 ms is used.
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
);
using (grabResult) // Make sure that the grab buffers are reused for
// grabbing as soon we run out of scope.
{
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
// Access the data.
// ....
}
else
{
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode, grabResult.ErrorDescription);
}
}
}
// Stop grabbing.
camera.StreamGrabber.Stop();
' Open the connection to the camera device.
camera.Open()
' The MaxNumBuffer parameter can be used to control the amount of buffers
' allocated for grabbing. The default value of this parameter is 10.
camera.Parameters(PLCameraInstance.MaxNumBuffer).SetValue(3)
' Start grabbing.
camera.StreamGrabber.Start()
' Grab a number of images.
For i As Integer = 0 To 10 - 1
' Wait for an image and then retrieve it. A timeout of 1000 ms is used.
Dim grabResult As IGrabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
)
Using grabResult
' Image grabbed successfully?
If grabResult.GrabSucceeded Then
' Access the data.
' ....
Else
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode,
grabResult.ErrorDescription)
End If
End Using
Next
' Stop grabbing.
camera.StreamGrabber.Stop()
Accessing Components#
To access the individual components of a GrabResult, you can use the IDataContainer
and IDataComponent
classes. A container can hold one or more components. You can use the container to query for the number of components and to retrieve a specific component. Each component in the container holds the actual data, e.g, the depth values, as well as its metadata.
Use the IGrabResult::GetDataContainer()
method to get access to a GrabResult's IDataContainer
.
Refer to the Multi-Component Grab Results section of the pylon .NET Programmer's Guide for more information about how pylon provides access to GrabResults containing multiple components.
示例:
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
);
using (grabResult)
{
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
// Access the components.
// The container and the components should always be used in combination
// with using statements. This ensures that the underlying grab buffers
// are reused for grabbing new data as soon as the variables go out
// of scope. Omitting the using statements and relying on the garbage
// collector instead will cause buffer underruns resulting in missed data.
using (var container = grabResult.Container)
using (var rangeComponent = container[0])
using (var intensityComponent = container[1])
using (var confidenceComponent = container[2])
{
var pointCloudData = rangeComponent.PixelData as float[];
var intensityData = intensityComponent.PixelData as UInt16[];
var confidenceData = confidenceComponent.PixelData as UInt16[];
// Access the center pixel.
var u = (uint)(rangeComponent.Width / 2);
var v = (uint)(rangeComponent.Height / 2);
var idx = v * rangeComponent.Width + u;
var intensity = intensityData[idx];
var confidence = confidenceData[idx];
// There is a triple (x,y,z) of floats per pixel.
var x = pointCloudData[3 * idx];
var y = pointCloudData[3 * idx + 1];
var z = pointCloudData[3 * idx + 2];
if (confidence != 0) // Alternatively: if (z != 0)
{
// Valid depth data
Console.WriteLine("x={0}, y={1}, z={2}, intensity={3}, confidence = {4}",
x, y, z, intensity, confidence);
}
else
{
Console.WriteLine("No depth data available for pixel ({0},{1}), intensity={2}",
u, v, intensity);
}
}
else
{
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode, grabResult.ErrorDescription);
}
}
Dim grabResult As IGrabResult =
camera.StreamGrabber.RetrieveResult(5000, TimeoutHandling.ThrowException)
Using grabResult
' Image grabbed successfully?
If grabResult.GrabSucceeded Then
' Access the components.
' The container and the components should always be used in combination with
' using statements. This ensures that the underlying grab buffers are reused
' for grabbing new data as soon as the variables go out of scope.
' Omitting the using statements and relying on the garbage collector instead
' will cause buffer underruns resulting in missed data.
Using container = grabResult.Container,
rangeComponent = container(0),
intensityComponent = container(1),
confidenceComponent = container(2)
Dim pointCloudData = TryCast(rangeComponent.PixelData, Single())
Dim intensityData = TryCast(intensityComponent.PixelData, UInt16())
Dim confidenceData = TryCast(confidenceComponent.PixelData, UInt16())
' Access the center pixel.
Dim u = CUInt((rangeComponent.Width / 2))
Dim v = CUInt((rangeComponent.Height / 2))
Dim idx = v * rangeComponent.Width + u
Dim intensity = intensityData(idx)
Dim confidence = confidenceData(idx)
Dim x = pointCloudData(3 * idx)
Dim y = pointCloudData(3 * idx + 1)
Dim z = pointCloudData(3 * idx + 2)
If confidence <> 0 Then ' Alternatively: if (z != 0)
' Valid depth data
Console.WriteLine("x={0}, y={1}, z={2}, intensity={3}, confidence = {4}",
x, y, z, intensity, confidence)
Else
Console.WriteLine("No depth data available for pixel ({0},{1}), intensity={2}",
u, v, intensity)
End If
End Using
Else
Console.WriteLine("Error: {0} {1}", grabResult.ErrorCode,
grabResult.ErrorDescription)
End If
End Using
Interpreting Depth Data#
The Processing Measurement Results topic provides information about the format of the depth data stored in the Range data component.
Debugging Applications and Controlling the GigE Vision Heartbeat#
Reconnection Problems When Using pylon 7.0 or Below
If an application is terminated or crashes before it properly closed the connection to the camera, the camera keeps the connection open until the heartbeat timeout expires. Until the heartbeat timeout has expired, the camera refuses any further connection attempt. When restarting your application or starting another application requesting access to the camera, you will receive an error stating that the device is currently in use.
This usually happens if you stop your application using the debugger or if your application terminates unexpectedly. To open the camera again, you must either wait until the timeout has elapsed or temporarily disconnect the camera from the network.
To work around this, you can override the automatic adjustment of the heartbeat timeout when the application is running under the control of a debugger by setting the environment variable named GEV_HEARTBEAT_TIMEOUT
to the desired timeout in milliseconds. Alternatively, you can set the heartbeat timeout in your application using the pylon API as shown in the DeviceRemovalCSharp
and DeviceRemovalVB
sample programs.