353 lines
14 KiB
C#
353 lines
14 KiB
C#
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|