Cameras, Preview and Image Settings in C# (AR51.Net)
List the AR 51 cameras with their calibration values, stream a live JPEG preview, read and change their image settings, and tune them automatically. Cameras are spread over several camera servers; ar51.Cameras covers all of them.
List the cameras
foreach (CameraInfo camera in await ar51.Cameras.GetCamerasAsync())
Console.WriteLine($"{camera.Name}: {camera.Width}x{camera.Height} @ {camera.FrameRate} fps, " +
$"{(camera.IsConnected ? "connected" : "offline")}");
Each CameraInfo has the camera's id (its serial number), name, driver type, connection state, the camera server it's plugged into, image size and frame rate, and its calibration — or null when the camera isn't calibrated. A camera server that doesn't answer in time is left out.
Calibration values
Calibration follows the OpenCV convention: focal lengths (Fx, Fy) and principal point (Cx, Cy) in pixels, lens distortion in the order k1, k2, p1, p2, k3, and WorldToCamera, which maps an AR 51 world point into the camera's space.
| Space | Units | Axes |
|---|---|---|
| AR 51 world | meters | left-handed: X right, Y up, Z forward |
| Camera space | meters | X right, Y down, Z forward (OpenCV) |
To project a world point to a pixel (before distortion):
Vector3 p = Vector3.Transform(worldPoint, calibration.WorldToCamera);
float u = calibration.Fx * p.X / p.Z + calibration.Cx;
float v = calibration.Fy * p.Y / p.Z + calibration.Cy;
The camera's own position in the world is the translation of the inverse matrix:
Matrix4x4.Invert(calibration.WorldToCamera, out Matrix4x4 cameraToWorld);
Console.WriteLine($"camera at {cameraToWorld.Translation} m");
Live preview
StreamPreviewAsync streams a camera's video as JPEG images. Ending the await foreach loop stops the preview.
var options = new PreviewOptions { FitWidth = 640, FitHeight = 480, FrameRate = 5 };
await foreach (CameraFrame frame in ar51.Cameras.StreamPreviewAsync(camera.Id, options))
{
// frame.Jpeg holds one JPEG image; show it or save it.
}
The image is shrunk to fit inside FitWidth × FitHeight, keeping the camera's aspect ratio — never enlarged. null values use the camera's own size and frame rate; JpegQuality defaults to 70.
Each preview costs network bandwidth. Open one stream per camera and share it between every view that shows that camera, and stream only the cameras on screen. For a wall of camera tiles, request small images at a low frame rate. The web dashboard sample does all three.
Image settings
Settings are saved on the server and apply to every app connected to the AR 51 system. Excluding a camera from tracking, or changing its exposure during a live session, changes tracking for everyone.
Read a camera's settings, change values with with, and send them back. Only the values that differ are sent, and the server saves them, so they survive a restart. The API doesn't report valid ranges, so keep each setting within the camera's range: a value outside it is clamped by the camera to the nearest limit, and current servers report the value you sent rather than the one the camera uses.
CameraSettings settings = await ar51.Cameras.GetSettingsAsync(camera.Id);
await ar51.Cameras.SetSettingsAsync(camera.Id, settings with { ExposureMicroseconds = 4000, Gain = 2 });
| Setting | What it does |
|---|---|
ExposureMicroseconds | Exposure time, in microseconds |
Gain, Gamma, Contrast, Saturation | Overall gain, gamma correction, contrast, color saturation |
GainRed, GainGreen, GainBlue | Color balance |
Rotate180 | Turn the image upside down, for a camera mounted upside down |
ExcludedFromTracking | Leave the camera out of tracking, for example while it's faulty |
Auto-tune
Auto-tune adjusts each camera's color balance (GainRed, GainGreen, GainBlue) using the camera's own one-shot white balance — for every camera you include (all of them by default), for every app using the system. It takes about two seconds. If you may want the old values back, save them first with GetSettingsAsync; read the new values afterwards the same way. Follow it with events:
ar51.Cameras.AutoTuneProgressChanged += (_, e) => Console.WriteLine($"auto-tune {e.Status.Progress:P0}");
ar51.Cameras.AutoTuneEnded += (_, e) => Console.WriteLine($"auto-tune {e.Outcome}");
await ar51.Cameras.StartAutoTuneAsync(); // all cameras; pass AutoTuneOptions to choose some
It ends as Completed, Failed (see each camera's error) or Cancelled.
Current servers don't report auto-tune progress, so AR51.Net estimates it: AutoTuneStarted at once, AutoTuneProgressChanged over about two seconds, then AutoTuneEnded with Completed. While it estimates, AutoTuneStatus.IsEstimated is true. Current servers also can't cancel an auto-tune: CancelAutoTuneAsync doesn't throw, and the auto-tune finishes as Completed. An auto-tune started by another app isn't seen. When a server reports real progress, AR51.Net uses it instead, with no change to your code.
Complete sample
List the cameras with their calibration, save one preview image of the first camera, and print its settings.
// List the cameras with their calibration, save one preview image of the first camera, and show its settings.
using AR51.Net;
using System.Numerics;
await using IAR51Client ar51 = AR51Client.Create();
await ar51.StartAsync();
IReadOnlyList<CameraInfo> cameras = await ar51.Cameras.GetCamerasAsync();
foreach (CameraInfo camera in cameras)
{
Console.WriteLine($"{camera.Name} ({camera.Type}) {camera.Width}x{camera.Height} @ {camera.FrameRate} fps, " +
$"{(camera.IsConnected ? "connected" : "offline")}, on {camera.ServerHost}");
if (camera.Calibration is { } calibration)
{
Matrix4x4.Invert(calibration.WorldToCamera, out Matrix4x4 cameraToWorld);
Console.WriteLine($" fx {calibration.Fx:F1} px, position {cameraToWorld.Translation} m");
}
}
if (cameras.Count == 0) return;
CameraInfo first = cameras[0];
// A small preview that fits a 640 x 480 box, keeping the camera's aspect ratio. Ending the loop stops the preview.
await foreach (CameraFrame frame in ar51.Cameras.StreamPreviewAsync(first.Id, new PreviewOptions { FitWidth = 640, FitHeight = 480, FrameRate = 5 }))
{
var file = $"{first.Id}.jpg";
await File.WriteAllBytesAsync(file, frame.Jpeg.ToArray());
Console.WriteLine($"Saved {file}: {frame.Width}x{frame.Height}, camera at {frame.CameraFps:F0} fps");
break;
}
CameraSettings settings = await ar51.Cameras.GetSettingsAsync(first.Id);
Console.WriteLine($"Settings of {first.Name}: {settings}");
// To change a setting: await ar51.Cameras.SetSettingsAsync(first.Id, settings with { Gain = 2 });
View this sample on Bitbucket — clone the samples repository to run it.
Reference
ICameraClient · CameraInfo · CameraCalibration · PreviewOptions · CameraSettings