feat: camera tuning + capturing command + raw image correction

This commit is contained in:
2026-02-17 14:42:50 +03:00
parent 9c4f12d45e
commit 5b8db82894
7 changed files with 579 additions and 70 deletions
+73 -39
View File
@@ -58,7 +58,11 @@ public class CameraService : IDisposable
ImageCaptureFrameBufferCount = 2,
ImageCaptureWidth = 4056,
ImageCaptureHeight = 3080,
CameraHardSettings = null
CameraHardSettings = null,
DecodeGainR = 1,
DecodeGainG = 1,
DecodeGainB = 1,
DecodeBlackLevel = 256
};
public string CameraId { get; init; } = "";
@@ -70,6 +74,10 @@ public class CameraService : IDisposable
public int ImageCaptureWidth { get; init; }
public int ImageCaptureHeight { get; init; }
public IConfigurationSection? CameraHardSettings { get; init; }
public double DecodeGainR { get; set; }
public double DecodeGainG { get; set; }
public double DecodeGainB { get; set; }
public int DecodeBlackLevel { get; set; }
}
private bool _disposedValue;
@@ -94,6 +102,7 @@ public class CameraService : IDisposable
public bool IsImageCapturing { get; private set; } = false;
public List<FrameBuffer> ViewFinderBufferQueue { get; private set; } = new List<FrameBuffer>();
public CameraServiceConfiguration Configuration => _configuration;
public CameraService(ILogger<CameraService> logger, CameraServiceConfiguration? configuration = null, bool autoInitialize = false)
{
@@ -298,7 +307,8 @@ public class CameraService : IDisposable
cancellationToken.ThrowIfCancellationRequested();
int result = _camera.Start();
// int result = _camera.Start();
int result = _camera.Start(GetSettingsControlList(_cameraHardSettings));
if (result < 0)
{
_logger.LogCritical("Error while camera start {Result}", result);
@@ -504,6 +514,7 @@ public class CameraService : IDisposable
_state |= State.ImageCaptureFrameBuffersMapped;
}
private string GetSettings(int indent = 0)
{
if (_camera is null)
@@ -562,7 +573,60 @@ public class CameraService : IDisposable
return sb.ToString();
}
public async Task<OpenCvSharp.Mat?> CaptureImage(CancellationToken cancellationToken, int framesCount = 1)
private void ApplyRequestSettings(Request request, Dictionary<string, object> settings)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
foreach (var (id, info) in _camera.Controls)
{
if (!settings.TryGetValue(id.Name, out var parameter))
continue;
ControlValue controlValue;
try
{
controlValue = ControlValueHelper.Convert(parameter, id, info);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while converting value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, parameter.GetType(), parameter);
continue;
}
request.Controls.Set(id.Id, controlValue);
}
}
private ControlList GetSettingsControlList(Dictionary<string, object> settings)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
var controlList = new ControlList();
foreach (var (id, info) in _camera.Controls)
{
if (!settings.TryGetValue(id.Name, out var parameter))
continue;
ControlValue controlValue;
try
{
controlValue = ControlValueHelper.Convert(parameter, id, info);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while converting value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, parameter.GetType(), parameter);
continue;
}
controlList.Set(id.Id, controlValue);
}
return controlList;
}
public async Task<OpenCvSharp.Mat?> CaptureImage(CancellationToken cancellationToken, int framesCount = 1, Dictionary<string, object>? settings = null)
{
ArgumentOutOfRangeException.ThrowIfLessThan(framesCount, 1, nameof(framesCount));
@@ -581,6 +645,8 @@ public class CameraService : IDisposable
.CreateLinkedTokenSource(_disposeCts.Token, cancellationToken)
.Token;
if (IsImageCapturing)
return null;
IsImageCapturing = true;
_logger.LogDebug("Capturing {} frame(s)", framesCount);
@@ -609,11 +675,13 @@ public class CameraService : IDisposable
foreach (var buffer in buffers)
{
var request = _camera.CreateRequest(1);
ApplySettings(request, _cameraHardSettings);
var result = request.AddBuffer(_imageCaptureStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
if (settings is not null)
ApplyRequestSettings(request, settings);
requests.Add(request);
}
_logger.LogDebug("{} request(s) created", requests.Count());
@@ -726,7 +794,7 @@ public class CameraService : IDisposable
try
{
image = decoder.Decode(checked((int)size.Width), checked((int)size.Height), checked((int)stride), planesData);
image = decoder.Decode(checked((int)size.Width), checked((int)size.Height), checked((int)stride), planesData, _configuration.DecodeGainR, _configuration.DecodeGainG, _configuration.DecodeGainB, _configuration.DecodeBlackLevel);
}
catch (Exception ex)
{
@@ -756,40 +824,6 @@ public class CameraService : IDisposable
IsImageCapturing = false;
return image;
}
private void ApplySettings(Request request, Dictionary<string, object> settings)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
foreach (var (id, info) in _camera.Controls)
{
if (!settings.ContainsKey(id.Name))
continue;
var value = settings[id.Name];
ControlValue? controlValue;
try
{
if (!id.IsArray)
controlValue = ControlValueHelper.ConvertToControlValue(value, id, info);
else
controlValue = ControlValueHelper.ConvertToControlArray(value, id, info);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while converting value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, value.GetType(), value);
continue;
}
if (controlValue is null)
{
_logger.LogWarning("Cannot convert to control value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, value.GetType(), value);
continue;
}
request.Controls.Set(id.Id, controlValue);
}
}
public void StartViewFinder()
{