Files

353 lines
14 KiB
C#
Raw Permalink Normal View History

using MainShell.Common;
using MainShell.Log;
using MainShell.Models;
using MainShell.Vision.Common;
using MaxwellFramework.Core.Attributes;
using SemiconductorVisionAlgorithm.SemiCalib;
using SemiconductorVisionAlgorithm.SemiParams;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Vision
{
/// <summary>
/// 抓圆服务实现
/// </summary>
[Singleton]
public class FindEdgeCircleService : IFindEdgeCircleService
{
private readonly IImageCaptureService _imageCaptureService;
public FindEdgeCircleService(IImageCaptureService imageCaptureService)
{
_imageCaptureService = imageCaptureService ?? throw new ArgumentNullException(nameof(imageCaptureService));
}
/// <summary>
/// 采集图像并执行抓圆
/// </summary>
public async Task<VisionProcessResult<FindEdgeCircleResult>> ProcessAsync(
FindEdgeCircleRequest request,
CancellationToken cancellationToken = default(CancellationToken))
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
FindEdgeCircleImageRequest imageRequest = CreateImageRequest(request);
return await ExecuteWithCaptureAsync(
request.CameraSource,
request.CaptureOptions,
imageRequest,
ProcessImageAsync,
cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 基于已有图像直接执行抓圆
/// </summary>
public Task<VisionProcessResult<FindEdgeCircleResult>> ProcessImageAsync(
MxImage image,
FindEdgeCircleImageRequest request,
CancellationToken cancellationToken = default(CancellationToken))
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
FindEdgeCircleImageRequest normalizedRequest = NormalizeRequest(request);
VisionProcessResult validationResult = ValidateRequest(normalizedRequest);
if (validationResult != null)
{
return Task.FromResult(CreateFailureResult(validationResult));
}
try
{
cancellationToken.ThrowIfCancellationRequested();
VisionProcessResult<FindEdgeCircleResult> result = ExecuteEdgeCircle(image, normalizedRequest);
if (!result.Succeeded)
{
result.Message.LogSysError();
}
return Task.FromResult(result);
}
catch (OperationCanceledException ex)
{
string message = $"FindEdgeCircleService: process image was cancelled.Timeout ={ normalizedRequest.TimeoutMilliseconds}ms.";
message.LogInfo();
return Task.FromResult(VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Cancelled,
VisionErrorCode.OperationCancelled,
null,
MessageKey.VisionOperationCancelled,
message,
ex));
}
catch (Exception ex)
{
string message = $"FindEdgeCircleService: process image failed. {ex.Message}";
message.LogSysError();
return Task.FromResult(VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Algorithm,
VisionErrorCode.CommonAlgorithmExecutionFailed,
VisionAlarmIds.CommonAlgorithmExecutionFailed,
MessageKey.VisionCommonAlgorithmExecutionFailed,
message,
ex));
}
}
/// <summary>
/// 执行边缘圆检测算法
/// </summary>
private static VisionProcessResult<FindEdgeCircleResult> ExecuteEdgeCircle(
MxImage image,
FindEdgeCircleImageRequest request)
{
Camera camera = image.Image;
if (camera == null)
{
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Algorithm,
VisionErrorCode.ImageNull,
VisionAlarmIds.ImageNull,
MessageKey.VisionImageIsNull,
"FindEdgeCircleService: input image does not contain a valid camera object.");
}
var parameters = request.Parameters;
double[] hv_Parameter;
bool flag;
if (parameters.RoiRect != null)
{
// 使用 ROI 区域进行抓圆
flag = CalibManager.Instance.calib_edge_circle_by_roi(
camera,
parameters.CameraId,
0,
parameters.RoiRect,
parameters.FileDirectory,
out hv_Parameter);
}
else
{
// 不使用 ROI 进行抓圆
flag = CalibManager.Instance.calib_edge_circle(
camera,
parameters.CameraId,
0,
parameters.FileDirectory,
out hv_Parameter);
}
if (!flag)
{
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Algorithm,
VisionErrorCode.CommonAlgorithmExecutionFailed,
VisionAlarmIds.CommonAlgorithmExecutionFailed,
MessageKey.VisionCommonAlgorithmExecutionFailed,
"FindEdgeCircleService: algorithm calib_edge_circle returned false.");
}
if (hv_Parameter == null || hv_Parameter.Length < 2)
{
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Algorithm,
VisionErrorCode.CommonAlgorithmExecutionFailed,
VisionAlarmIds.CommonAlgorithmExecutionFailed,
MessageKey.VisionCommonAlgorithmExecutionFailed,
"FindEdgeCircleService: algorithm returned invalid parameters.");
}
FindEdgeCircleResult result = new FindEdgeCircleResult();
result.CenterX = hv_Parameter[0];
result.CenterY = hv_Parameter[1];
return VisionProcessResult<FindEdgeCircleResult>.Success(result);
}
private static FindEdgeCircleImageRequest NormalizeRequest(FindEdgeCircleImageRequest request)
{
FindEdgeCircleImageRequest normalizedRequest = new FindEdgeCircleImageRequest();
normalizedRequest.TimeoutMilliseconds = request.TimeoutMilliseconds > 0 ? request.TimeoutMilliseconds :3000;
normalizedRequest.Parameters = CloneParameters(request.Parameters);
return normalizedRequest;
}
private static VisionProcessResult ValidateRequest(FindEdgeCircleImageRequest request)
{
if (request.Parameters == null)
{
return VisionProcessResult.Failure(
VisionFailureCategory.Validation,
VisionErrorCode.RequestInvalid,
VisionAlarmIds.RequestInvalid,
MessageKey.VisionRequestInvalid,
"FindEdgeCircleService: parameters cannot be null.");
}
if (request.TimeoutMilliseconds <= 0 || request.TimeoutMilliseconds > 60000)
{
return VisionProcessResult.Failure(
VisionFailureCategory.Validation,
VisionErrorCode.TimeoutInvalid,
VisionAlarmIds.RequestInvalid,
MessageKey.VisionTimeoutInvalid,
string.Format("FindEdgeCircleService: TimeoutMilliseconds {0} is invalid. Expected range is [1,60000].", request.TimeoutMilliseconds));
}
if (string.IsNullOrWhiteSpace(request.Parameters.FileDirectory))
{
return VisionProcessResult.Failure(
VisionFailureCategory.Validation,
VisionErrorCode.RequestInvalid,
VisionAlarmIds.RequestInvalid,
MessageKey.VisionRequestInvalid,
"FindEdgeCircleService: FileDirectory cannot be empty.");
}
if (string.IsNullOrWhiteSpace(request.Parameters.CameraId))
{
return VisionProcessResult.Failure(
VisionFailureCategory.Validation,
VisionErrorCode.RequestInvalid,
VisionAlarmIds.RequestInvalid,
MessageKey.VisionRequestInvalid,
"FindEdgeCircleService: CameraId cannot be empty.");
}
return null;
}
private static FindEdgeCircleParameters CloneParameters(FindEdgeCircleParameters parameters)
{
if (parameters == null)
{
return new FindEdgeCircleParameters();
}
FindEdgeCircleParameters clone = new FindEdgeCircleParameters();
clone.UseRoi = parameters.UseRoi;
clone.RoiName = parameters.RoiName;
clone.FileDirectory = parameters.FileDirectory;
clone.CameraId = parameters.CameraId;
clone.RoiRect = parameters.RoiRect;
return clone;
}
private static FindEdgeCircleImageRequest CreateImageRequest(FindEdgeCircleRequest request)
{
FindEdgeCircleImageRequest imageRequest = new FindEdgeCircleImageRequest();
imageRequest.TimeoutMilliseconds = request.TimeoutMilliseconds;
imageRequest.Parameters = CloneParameters(request.Parameters);
return imageRequest;
}
private async Task<VisionProcessResult<FindEdgeCircleResult>> ExecuteWithCaptureAsync(
CameraType cameraSource,
CameraCaptureOptions captureOptions,
FindEdgeCircleImageRequest imageRequest,
Func<MxImage, FindEdgeCircleImageRequest, CancellationToken,
Task<VisionProcessResult<FindEdgeCircleResult>>> imageProcessor,
CancellationToken cancellationToken)
{
ImageCaptureResult captureResult = null;
MxImage image = null;
try
{
captureResult = await _imageCaptureService.CaptureAsync(
cameraSource,
captureOptions,
cancellationToken).ConfigureAwait(false);
if (captureResult == null)
{
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Capture,
VisionErrorCode.NoFrame,
VisionAlarmIds.NoFrame,
MessageKey.VisionNoFrameReturned,
string.Format(
"FindEdgeCircleService: image capture returned null result for camera '{0}'.",
cameraSource));
}
if (!captureResult.Succeeded)
{
return CreateFailureResult(VisionResultMapper.CreateCaptureFailure(captureResult));
}
image = captureResult.Image;
if (image == null)
{
string message = string.Format(
"FindEdgeCircleService: image capture succeeded but returned null image for camera '{0}'.",
cameraSource);
message.LogSysError();
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Capture,
VisionErrorCode.ImageNull,
VisionAlarmIds.ImageNull,
MessageKey.VisionImageIsNull,
message);
}
return await imageProcessor(image, imageRequest, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
string message = $"FindEdgeCircleService: capture orchestration was cancelled for camera '{cameraSource}'.";
message.LogInfo();
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Cancelled,
VisionErrorCode.OperationCancelled,
null,
MessageKey.VisionOperationCancelled,
message,
ex);
}
catch (Exception ex)
{
string message = $"FindEdgeCircleService: capture orchestration failed for camera '{cameraSource}'. { ex.Message} ";
message.LogSysError();
return VisionProcessResult<FindEdgeCircleResult>.Failure(
VisionFailureCategory.Algorithm,
VisionErrorCode.CommonAlgorithmExecutionFailed,
VisionAlarmIds.CommonAlgorithmExecutionFailed,
MessageKey.VisionCommonAlgorithmExecutionFailed,
message,
ex);
}
finally
{
if (image != null)
{
image.Dispose();
}
}
}
private static VisionProcessResult<FindEdgeCircleResult> CreateFailureResult(VisionProcessResult result)
{
return VisionProcessResult<FindEdgeCircleResult>.Failure(
result.FailureCategory,
result.ErrorCode,
result.AlarmId,
result.UserMessageKey,
result.Message,
result.Exception);
}
}
}