添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
using JM1Vision;
|
||||
using MainShell.Models;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>ƥ<EFBFBD><C6A5>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public class FindTemplateImageRequest
|
||||
{
|
||||
public FindTemplateImageRequest()
|
||||
{
|
||||
TimeoutMilliseconds = 3000;
|
||||
Parameters = new FindTemplateParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20>㷨<EFBFBD><E3B7A8>ʱʱ<CAB1>䣨<EFBFBD><E4A3A8><EFBFBD>룩<EFBFBD><EBA3A9>
|
||||
/// </summary>
|
||||
public int TimeoutMilliseconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public FindTemplateParameters Parameters { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板匹配参数。
|
||||
/// </summary>
|
||||
public sealed class FindTemplateParameters : TemplateVisionAlgorithmParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启用 ROI。
|
||||
/// </summary>
|
||||
public bool UseRoi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ROI 名称或标识。
|
||||
/// </summary>
|
||||
public string RoiName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using MainShell.Common;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public class FindTemplateRequest
|
||||
{
|
||||
private CameraCaptureOptions _captureOptions;
|
||||
|
||||
public FindTemplateRequest()
|
||||
{
|
||||
_captureOptions = new CameraCaptureOptions();
|
||||
TimeoutMilliseconds = 3000;
|
||||
Parameters = new FindTemplateParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4>
|
||||
/// </summary>
|
||||
public CameraType CameraSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><>ͼѡ<CDBC>
|
||||
/// </summary>
|
||||
public CameraCaptureOptions CaptureOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _captureOptions;
|
||||
}
|
||||
set
|
||||
{
|
||||
_captureOptions = value ?? new CameraCaptureOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20>㷨<EFBFBD><E3B7A8>ʱʱ<CAB1>䣨<EFBFBD><E4A3A8><EFBFBD>룩<EFBFBD><EBA3A9>
|
||||
/// </summary>
|
||||
public int TimeoutMilliseconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public FindTemplateParameters Parameters { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public class FindTemplateResult
|
||||
{
|
||||
public FindTemplateResult()
|
||||
{
|
||||
Circles = new List<FindTemplateCircleResult>();
|
||||
Lines = new List<FindTemplateLineResult>();
|
||||
Rectangles = new List<FindTemplateRectangleResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ƥ<><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD> X <20><><EFBFBD>ꡣ
|
||||
/// </summary>
|
||||
public double CenterX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ƥ<><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Y <20><><EFBFBD>ꡣ
|
||||
/// </summary>
|
||||
public double CenterY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ƥ<><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public double Score { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD>չƥ<D5B9><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public VisionMatchResult Match { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ƥ<><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public FindTemplateContourResult Contour { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Բ<><D4B2>ʶ<EFBFBD><CAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public IList<FindTemplateCircleResult> Circles { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ֱ<><D6B1>ʶ<EFBFBD><CAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public IList<FindTemplateLineResult> Lines { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>ʶ<EFBFBD><CAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public IList<FindTemplateRectangleResult> Rectangles { get; private set; }
|
||||
}
|
||||
|
||||
public class FindTemplateContourResult
|
||||
{
|
||||
public double StartX { get; set; }
|
||||
|
||||
public double StartY { get; set; }
|
||||
|
||||
public double EndX { get; set; }
|
||||
|
||||
public double EndY { get; set; }
|
||||
}
|
||||
|
||||
public class FindTemplateCircleResult
|
||||
{
|
||||
public double CenterX { get; set; }
|
||||
|
||||
public double CenterY { get; set; }
|
||||
|
||||
public double Radius { get; set; }
|
||||
}
|
||||
|
||||
public class FindTemplateLineResult
|
||||
{
|
||||
public double StartX { get; set; }
|
||||
|
||||
public double StartY { get; set; }
|
||||
|
||||
public double EndX { get; set; }
|
||||
|
||||
public double EndY { get; set; }
|
||||
}
|
||||
|
||||
public class FindTemplateRectangleResult
|
||||
{
|
||||
public double StartX { get; set; }
|
||||
|
||||
public double StartY { get; set; }
|
||||
|
||||
public double Width { get; set; }
|
||||
|
||||
public double Height { get; set; }
|
||||
|
||||
public double Angle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,705 @@
|
||||
using MainShell.Common;
|
||||
using MainShell.Log;
|
||||
using MainShell.Models;
|
||||
using MaxwellFramework.Core.Attributes;
|
||||
using SemiconductorVisionAlgorithm.SemiParams;
|
||||
using SemiconductorVisionAlgorithm.SemiWaferRecip;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// ?????????????
|
||||
/// </summary>
|
||||
[Singleton]
|
||||
public class FindTemplateService : IFindTemplateService
|
||||
{
|
||||
private readonly IImageCaptureService _imageCaptureService;
|
||||
|
||||
public FindTemplateService(IImageCaptureService imageCaptureService)
|
||||
{
|
||||
_imageCaptureService = imageCaptureService ?? throw new ArgumentNullException(nameof(imageCaptureService));
|
||||
}
|
||||
|
||||
public async Task<VisionProcessResult<FindTemplateResult>> ProcessAsync(
|
||||
FindTemplateRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
FindTemplateImageRequest imageRequest = CreateImageRequest(request);
|
||||
return await ExecuteWithCaptureAsync(
|
||||
request.CameraSource,
|
||||
request.CaptureOptions,
|
||||
imageRequest,
|
||||
ProcessImageAsync,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task<VisionProcessResult<FindTemplateResult>> ProcessImageAsync(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(image));
|
||||
}
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
FindTemplateImageRequest normalizedRequest = NormalizeRequest(request);
|
||||
VisionProcessResult validationResult = ValidateRequest(normalizedRequest);
|
||||
if (validationResult != null)
|
||||
{
|
||||
return Task.FromResult(CreateFailureResult<FindTemplateResult>(validationResult));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
VisionProcessResult<FindTemplateResult> result = ExecuteSingleMatch(image, normalizedRequest);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
result.Message.LogSysError();
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
string message = $"FindTemplateService: process image was cancelled. Timeout={normalizedRequest.TimeoutMilliseconds} ms.";
|
||||
message.LogInfo();
|
||||
return Task.FromResult(VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Cancelled,
|
||||
VisionErrorCode.OperationCancelled,
|
||||
null,
|
||||
MessageKey.VisionOperationCancelled,
|
||||
message,
|
||||
ex));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string message = $"FindTemplateService: process image failed. {ex.Message}";
|
||||
message.LogSysError();
|
||||
return Task.FromResult(VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
message,
|
||||
ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<VisionProcessResult<FindTemplatesResult>> ProcessMultipleAsync(
|
||||
FindTemplateRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
FindTemplateImageRequest imageRequest = CreateImageRequest(request);
|
||||
return await ExecuteWithCaptureAsync(
|
||||
request.CameraSource,
|
||||
request.CaptureOptions,
|
||||
imageRequest,
|
||||
ProcessMultipleImageAsync,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task<VisionProcessResult<FindTemplatesResult>> ProcessMultipleImageAsync(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(image));
|
||||
}
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
FindTemplateImageRequest normalizedRequest = NormalizeRequest(request);
|
||||
VisionProcessResult validationResult = ValidateRequest(normalizedRequest);
|
||||
if (validationResult != null)
|
||||
{
|
||||
return Task.FromResult(CreateFailureResult<FindTemplatesResult>(validationResult));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
VisionProcessResult<FindTemplatesResult> result = ExecuteMultipleMatch(image, normalizedRequest);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
result.Message.LogSysError();
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
string message = $"FindTemplateService: multiple template process image was cancelled. Timeout={normalizedRequest.TimeoutMilliseconds} ms.";
|
||||
message.LogInfo();
|
||||
return Task.FromResult(VisionProcessResult<FindTemplatesResult>.Failure(
|
||||
VisionFailureCategory.Cancelled,
|
||||
VisionErrorCode.OperationCancelled,
|
||||
null,
|
||||
MessageKey.VisionOperationCancelled,
|
||||
message,
|
||||
ex));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string message = $"FindTemplateService: multiple template process image failed. {ex.Message}";
|
||||
message.LogSysError();
|
||||
return Task.FromResult(VisionProcessResult<FindTemplatesResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
message,
|
||||
ex));
|
||||
}
|
||||
}
|
||||
|
||||
private static FindTemplateImageRequest NormalizeRequest(FindTemplateImageRequest request)
|
||||
{
|
||||
FindTemplateImageRequest normalizedRequest = new FindTemplateImageRequest();
|
||||
normalizedRequest.TimeoutMilliseconds = request.TimeoutMilliseconds > 0 ? request.TimeoutMilliseconds : 3000;
|
||||
normalizedRequest.Parameters = CloneParameters(request.Parameters);
|
||||
return normalizedRequest;
|
||||
}
|
||||
|
||||
private static VisionProcessResult ValidateRequest(FindTemplateImageRequest request)
|
||||
{
|
||||
if (request.Parameters == null)
|
||||
{
|
||||
return VisionProcessResult.Failure(
|
||||
VisionFailureCategory.Validation,
|
||||
VisionErrorCode.RequestInvalid,
|
||||
VisionAlarmIds.RequestInvalid,
|
||||
MessageKey.VisionRequestInvalid,
|
||||
"FindTemplateService: parameters cannot be null.");
|
||||
}
|
||||
|
||||
if (request.TimeoutMilliseconds <= 0 || request.TimeoutMilliseconds > 600000)
|
||||
{
|
||||
return VisionProcessResult.Failure(
|
||||
VisionFailureCategory.Validation,
|
||||
VisionErrorCode.TimeoutInvalid,
|
||||
VisionAlarmIds.RequestInvalid,
|
||||
MessageKey.VisionTimeoutInvalid,
|
||||
string.Format("FindTemplateService: TimeoutMilliseconds {0} is invalid. Expected range is [1, 60000].", request.TimeoutMilliseconds));
|
||||
}
|
||||
|
||||
if (request.Parameters.MinScore <= 0d || request.Parameters.MinScore > 1d)
|
||||
{
|
||||
return VisionProcessResult.Failure(
|
||||
VisionFailureCategory.Validation,
|
||||
VisionErrorCode.MinScoreInvalid,
|
||||
VisionAlarmIds.TemplateMinScoreInvalid,
|
||||
MessageKey.VisionTemplateMinScoreInvalid,
|
||||
string.Format("FindTemplateService: MinScore {0} is invalid. Expected range is (0, 1].", request.Parameters.MinScore));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Parameters.TemplatePath))
|
||||
{
|
||||
return VisionProcessResult.Failure(
|
||||
VisionFailureCategory.Validation,
|
||||
VisionErrorCode.TemplatePathEmpty,
|
||||
VisionAlarmIds.TemplatePathEmpty,
|
||||
MessageKey.VisionTemplatePathEmpty,
|
||||
"FindTemplateService: TemplatePath cannot be empty.");
|
||||
}
|
||||
|
||||
if (request.Parameters.UseRoi && string.IsNullOrWhiteSpace(request.Parameters.RoiName))
|
||||
{
|
||||
return VisionProcessResult.Failure(
|
||||
VisionFailureCategory.Validation,
|
||||
VisionErrorCode.RoiNameEmpty,
|
||||
VisionAlarmIds.TemplateRoiInvalid,
|
||||
MessageKey.VisionTemplateRoiInvalid,
|
||||
"FindTemplateService: RoiName cannot be empty when UseRoi is true.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FindTemplateParameters CloneParameters(FindTemplateParameters parameters)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
return new FindTemplateParameters();
|
||||
}
|
||||
|
||||
FindTemplateParameters clone = new FindTemplateParameters();
|
||||
clone.UseRoi = parameters.UseRoi;
|
||||
clone.RoiName = parameters.RoiName;
|
||||
clone.MinScore = parameters.MinScore;
|
||||
clone.TemplatePath = parameters.TemplatePath;
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static FindTemplatesResult CreateMultipleResult(IList<FindTemplateResult> matches)
|
||||
{
|
||||
FindTemplatesResult result = new FindTemplatesResult();
|
||||
if (matches == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (FindTemplateResult match in matches)
|
||||
{
|
||||
result.Items.Add(match);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FindTemplateImageRequest CreateImageRequest(FindTemplateRequest request)
|
||||
{
|
||||
FindTemplateImageRequest imageRequest = new FindTemplateImageRequest();
|
||||
imageRequest.TimeoutMilliseconds = request.TimeoutMilliseconds;
|
||||
imageRequest.Parameters = CloneParameters(request.Parameters);
|
||||
return imageRequest;
|
||||
}
|
||||
|
||||
private static VisionProcessResult<FindTemplateResult> ExecuteSingleMatch(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request)
|
||||
{
|
||||
Camera camera = image.Image;
|
||||
if (camera == null)
|
||||
{
|
||||
return VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.ImageNull,
|
||||
VisionAlarmIds.ImageNull,
|
||||
MessageKey.VisionImageIsNull,
|
||||
"FindTemplateService: input image does not contain a valid camera object.");
|
||||
}
|
||||
|
||||
Template template = null;
|
||||
double score = 0d;
|
||||
Rectangle1 contour = null;
|
||||
List<Circle> circles = null;
|
||||
List<LineSeg> lines = null;
|
||||
List<Rectangle2> rectangles = null;
|
||||
|
||||
int matchCode = WaferRecips.Instance.match_image(
|
||||
camera,
|
||||
request.Parameters.TemplatePath,
|
||||
out template,
|
||||
out score,
|
||||
out contour,
|
||||
out circles,
|
||||
out lines,
|
||||
out rectangles);
|
||||
|
||||
if (matchCode != 0)
|
||||
{
|
||||
return VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
$"FindTemplateService: single template match failed. Code={matchCode}, TemplatePath={request.Parameters.TemplatePath}.");
|
||||
}
|
||||
|
||||
if (template == null || double.IsNaN(score) || double.IsInfinity(score))
|
||||
{
|
||||
return VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateResultInvalid,
|
||||
VisionAlarmIds.TemplateResultInvalid,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
$"FindTemplateService: single template match returned an invalid result. TemplatePath={request.Parameters.TemplatePath}, Score={score}.");
|
||||
}
|
||||
|
||||
if (score < request.Parameters.MinScore)
|
||||
{
|
||||
return VisionProcessResult<FindTemplateResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
$"FindTemplateService: single template match score {score} is lower than required {request.Parameters.MinScore}. TemplatePath={request.Parameters.TemplatePath}.");
|
||||
}
|
||||
|
||||
FindTemplateResult result = CreateSingleResult(template, score, contour, circles, lines, rectangles);
|
||||
return VisionProcessResult<FindTemplateResult>.Success(result);
|
||||
}
|
||||
|
||||
private static VisionProcessResult<FindTemplatesResult> ExecuteMultipleMatch(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request)
|
||||
{
|
||||
Camera camera = image.Image;
|
||||
if (camera == null)
|
||||
{
|
||||
return VisionProcessResult<FindTemplatesResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.ImageNull,
|
||||
VisionAlarmIds.ImageNull,
|
||||
MessageKey.VisionImageIsNull,
|
||||
"FindTemplateService: input image does not contain a valid camera object.");
|
||||
}
|
||||
|
||||
double[] rows = null;
|
||||
double[] cols = null;
|
||||
double[] angles = null;
|
||||
double[] scores = null;
|
||||
List<Rectangle1> contours = null;
|
||||
|
||||
bool succeeded = WaferRecips.Instance.match_multi_0bject(
|
||||
camera,
|
||||
request.Parameters.TemplatePath,
|
||||
out rows,
|
||||
out cols,
|
||||
out angles,
|
||||
out scores,
|
||||
out contours);
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
return VisionProcessResult<FindTemplatesResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
$"FindTemplateService: multiple template match failed. TemplatePath={request.Parameters.TemplatePath}.");
|
||||
}
|
||||
|
||||
if (!HasConsistentMultipleResult(rows, cols, angles, scores, contours))
|
||||
{
|
||||
return VisionProcessResult<FindTemplatesResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateResultInvalid,
|
||||
VisionAlarmIds.TemplateResultInvalid,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
$"FindTemplateService: multiple template match returned inconsistent result data. TemplatePath={request.Parameters.TemplatePath}.");
|
||||
}
|
||||
|
||||
FindTemplatesResult result = CreateMultipleResult(rows, cols, angles, scores, contours);
|
||||
return VisionProcessResult<FindTemplatesResult>.Success(result);
|
||||
}
|
||||
|
||||
private static FindTemplateResult CreateSingleResult(
|
||||
Template template,
|
||||
double score,
|
||||
Rectangle1 contour,
|
||||
IList<Circle> circles,
|
||||
IList<LineSeg> lines,
|
||||
IList<Rectangle2> rectangles)
|
||||
{
|
||||
FindTemplateResult result = new FindTemplateResult();
|
||||
result.CenterX = template.X;
|
||||
result.CenterY = template.Y;
|
||||
result.Score = score;
|
||||
result.Match = CreateMatchResult(template.Phi, score, contour, circles, lines, rectangles);
|
||||
result.Contour = CreateContourResult(contour);
|
||||
AppendCircleResults(result.Circles, circles);
|
||||
AppendLineResults(result.Lines, lines);
|
||||
AppendRectangleResults(result.Rectangles, rectangles);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FindTemplatesResult CreateMultipleResult(
|
||||
double[] rows,
|
||||
double[] cols,
|
||||
double[] angles,
|
||||
double[] scores,
|
||||
IList<Rectangle1> contours)
|
||||
{
|
||||
FindTemplatesResult result = new FindTemplatesResult();
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
FindTemplateResult item = new FindTemplateResult();
|
||||
item.CenterX = cols[i];
|
||||
item.CenterY = rows[i];
|
||||
item.Score = scores[i];
|
||||
item.Match = CreateMatchResult(angles[i], scores[i], contours[i], null, null, null);
|
||||
item.Contour = CreateContourResult(contours[i]);
|
||||
result.Items.Add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static VisionMatchResult CreateMatchResult(
|
||||
double angle,
|
||||
double score,
|
||||
Rectangle1 contour,
|
||||
IList<Circle> circles,
|
||||
IList<LineSeg> lines,
|
||||
IList<Rectangle2> rectangles)
|
||||
{
|
||||
VisionMatchResult match = new VisionMatchResult();
|
||||
match.Angle = angle;
|
||||
match.Score = score;
|
||||
match.Tag = CreateMatchTag(contour, circles, lines, rectangles);
|
||||
return match;
|
||||
}
|
||||
|
||||
private static FindTemplateContourResult CreateContourResult(Rectangle1 contour)
|
||||
{
|
||||
if (contour == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FindTemplateContourResult result = new FindTemplateContourResult();
|
||||
result.StartX = contour.Start_X;
|
||||
result.StartY = contour.Start_Y;
|
||||
result.EndX = contour.End_X;
|
||||
result.EndY = contour.End_Y;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AppendCircleResults(IList<FindTemplateCircleResult> destination, IList<Circle> circles)
|
||||
{
|
||||
if (destination == null || circles == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Circle circle in circles)
|
||||
{
|
||||
if (circle == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FindTemplateCircleResult item = new FindTemplateCircleResult();
|
||||
item.CenterX = circle.X;
|
||||
item.CenterY = circle.Y;
|
||||
item.Radius = circle.R;
|
||||
destination.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendLineResults(IList<FindTemplateLineResult> destination, IList<LineSeg> lines)
|
||||
{
|
||||
if (destination == null || lines == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (LineSeg line in lines)
|
||||
{
|
||||
if (line == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FindTemplateLineResult item = new FindTemplateLineResult();
|
||||
item.StartX = line.Start_X;
|
||||
item.StartY = line.Start_Y;
|
||||
item.EndX = line.End_X;
|
||||
item.EndY = line.End_Y;
|
||||
destination.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendRectangleResults(IList<FindTemplateRectangleResult> destination, IList<Rectangle2> rectangles)
|
||||
{
|
||||
if (destination == null || rectangles == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Rectangle2 rectangle in rectangles)
|
||||
{
|
||||
if (rectangle == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FindTemplateRectangleResult item = new FindTemplateRectangleResult();
|
||||
item.StartX = rectangle.XStart;
|
||||
item.StartY = rectangle.YStart;
|
||||
item.Width = rectangle.Width;
|
||||
item.Height = rectangle.Height;
|
||||
item.Angle = rectangle.Phi;
|
||||
destination.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static string CreateMatchTag(
|
||||
Rectangle1 contour,
|
||||
IList<Circle> circles,
|
||||
IList<LineSeg> lines,
|
||||
IList<Rectangle2> rectangles)
|
||||
{
|
||||
if (contour == null && circles == null && lines == null && rectangles == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int circleCount = circles != null ? circles.Count : 0;
|
||||
int lineCount = lines != null ? lines.Count : 0;
|
||||
int rectangleCount = rectangles != null ? rectangles.Count : 0;
|
||||
|
||||
if (contour == null)
|
||||
{
|
||||
return $"Circles={circleCount};Lines={lineCount};Rects={rectangleCount}";
|
||||
}
|
||||
|
||||
return $"Contour=({contour.Start_X},{contour.Start_Y})-({contour.End_X},{contour.End_Y});Circles={circleCount};Lines={lineCount};Rects={rectangleCount}";
|
||||
}
|
||||
|
||||
private static bool HasConsistentMultipleResult(
|
||||
double[] rows,
|
||||
double[] cols,
|
||||
double[] angles,
|
||||
double[] scores,
|
||||
IList<Rectangle1> contours)
|
||||
{
|
||||
if (rows == null || cols == null || angles == null || scores == null || contours == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rows.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rows.Length != cols.Length || rows.Length != angles.Length || rows.Length != scores.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rows.Length != contours.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
if (double.IsNaN(rows[i]) || double.IsInfinity(rows[i]) ||
|
||||
double.IsNaN(cols[i]) || double.IsInfinity(cols[i]) ||
|
||||
double.IsNaN(angles[i]) || double.IsInfinity(angles[i]) ||
|
||||
double.IsNaN(scores[i]) || double.IsInfinity(scores[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<VisionProcessResult<TResult>> ExecuteWithCaptureAsync<TResult>(
|
||||
MainShell.Common.CameraType cameraSource,
|
||||
CameraCaptureOptions captureOptions,
|
||||
FindTemplateImageRequest imageRequest,
|
||||
Func<MxImage, FindTemplateImageRequest, CancellationToken, Task<VisionProcessResult<TResult>>> imageProcessor,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ImageCaptureResult captureResult = null;
|
||||
MxImage image = null;
|
||||
|
||||
try
|
||||
{
|
||||
captureResult = await _imageCaptureService.CaptureAsync(
|
||||
cameraSource,
|
||||
captureOptions,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (captureResult == null)
|
||||
{
|
||||
return VisionProcessResult<TResult>.Failure(
|
||||
VisionFailureCategory.Capture,
|
||||
VisionErrorCode.NoFrame,
|
||||
VisionAlarmIds.NoFrame,
|
||||
MessageKey.VisionNoFrameReturned,
|
||||
string.Format(
|
||||
"FindTemplateService: image capture returned null result for camera '{0}'.",
|
||||
cameraSource));
|
||||
}
|
||||
|
||||
if (!captureResult.Succeeded)
|
||||
{
|
||||
return CreateFailureResult<TResult>(VisionResultMapper.CreateCaptureFailure(captureResult));
|
||||
}
|
||||
|
||||
image = captureResult.Image;
|
||||
if (image == null)
|
||||
{
|
||||
string message = string.Format(
|
||||
"FindTemplateService: image capture succeeded but returned null image for camera '{0}'.",
|
||||
cameraSource);
|
||||
message.LogSysError();
|
||||
return VisionProcessResult<TResult>.Failure(
|
||||
VisionFailureCategory.Capture,
|
||||
VisionErrorCode.ImageNull,
|
||||
VisionAlarmIds.ImageNull,
|
||||
MessageKey.VisionImageIsNull,
|
||||
message);
|
||||
}
|
||||
|
||||
return await imageProcessor(image, imageRequest, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
string message = $"FindTemplateService: capture orchestration was cancelled for camera '{cameraSource}'.";
|
||||
message.LogInfo();
|
||||
return VisionProcessResult<TResult>.Failure(
|
||||
VisionFailureCategory.Cancelled,
|
||||
VisionErrorCode.OperationCancelled,
|
||||
null,
|
||||
MessageKey.VisionOperationCancelled,
|
||||
message,
|
||||
ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string message = $"FindTemplateService: capture orchestration failed for camera '{cameraSource}'. {ex.Message}";
|
||||
message.LogSysError();
|
||||
return VisionProcessResult<TResult>.Failure(
|
||||
VisionFailureCategory.Algorithm,
|
||||
VisionErrorCode.TemplateMatchFailed,
|
||||
VisionAlarmIds.TemplateMatchFailed,
|
||||
MessageKey.VisionTemplateMatchFailed,
|
||||
message,
|
||||
ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (image != null)
|
||||
{
|
||||
image.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static VisionProcessResult<TData> CreateFailureResult<TData>(VisionProcessResult result)
|
||||
{
|
||||
return VisionProcessResult<TData>.Failure(
|
||||
result.FailureCategory,
|
||||
result.ErrorCode,
|
||||
result.AlarmId,
|
||||
result.UserMessageKey,
|
||||
result.Message,
|
||||
result.Exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD>
|
||||
/// </summary>
|
||||
public class FindTemplatesResult
|
||||
{
|
||||
public FindTemplatesResult()
|
||||
{
|
||||
Items = new List<FindTemplateResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ƥ<><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD>
|
||||
/// </summary>
|
||||
public IList<FindTemplateResult> Items { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using MainShell.Models;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MainShell.Vision
|
||||
{
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӿڡ<D3BF>
|
||||
/// </summary>
|
||||
public interface IFindTemplateService
|
||||
{
|
||||
/// <summary>
|
||||
/// <20>ɼ<EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD>ִ<EFBFBD>е<EFBFBD><D0B5><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD>䡣
|
||||
/// </summary>
|
||||
Task<VisionProcessResult<FindTemplateResult>> ProcessAsync(
|
||||
FindTemplateRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC>ֱ<EFBFBD><D6B1>ִ<EFBFBD>е<EFBFBD><D0B5><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD>䡣
|
||||
/// </summary>
|
||||
Task<VisionProcessResult<FindTemplateResult>> ProcessImageAsync(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// <20>ɼ<EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD>ִ<EFBFBD>ж<EFBFBD><D0B6><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD>䡣
|
||||
/// </summary>
|
||||
Task<VisionProcessResult<FindTemplatesResult>> ProcessMultipleAsync(
|
||||
FindTemplateRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC>ֱ<EFBFBD><D6B1>ִ<EFBFBD>ж<EFBFBD><D0B6><EFBFBD>ģ<EFBFBD><C4A3>ƥ<EFBFBD>䡣
|
||||
/// </summary>
|
||||
Task<VisionProcessResult<FindTemplatesResult>> ProcessMultipleImageAsync(
|
||||
MxImage image,
|
||||
FindTemplateImageRequest request,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user