CNC高级模块的运行后的可视化
This commit is contained in:
@@ -216,6 +216,9 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
|||||||
OutputData["Thickness"] = thickness;
|
OutputData["Thickness"] = thickness;
|
||||||
OutputData["ResultText"] = $"Void: {overallVoidRate:F1}% | {classification} | BGA×{bgaResults.Count}";
|
OutputData["ResultText"] = $"Void: {overallVoidRate:F1}% | {classification} | BGA×{bgaResults.Count}";
|
||||||
|
|
||||||
|
// 渲染带标注的彩色结果图像
|
||||||
|
OutputData["RenderedResultImage"] = RenderAnnotatedResult(inputImage, bgaResults, voidLimit, thickness);
|
||||||
|
|
||||||
roiMask?.Dispose();
|
roiMask?.Dispose();
|
||||||
return inputImage.Clone();
|
return inputImage.Clone();
|
||||||
}
|
}
|
||||||
@@ -372,6 +375,67 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
|||||||
mask.Dispose();
|
mask.Dispose();
|
||||||
voidImg.Dispose();
|
voidImg.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染带标注的彩色结果图像(轮廓、编号、气泡填充、总览信息)
|
||||||
|
/// </summary>
|
||||||
|
private Image<Bgr, byte> RenderAnnotatedResult(Image<Gray, byte> grayImage, List<BgaBallInfo> bgaResults, double voidLimit, int thickness)
|
||||||
|
{
|
||||||
|
var colorImage = new Image<Bgr, byte>(grayImage.Width, grayImage.Height);
|
||||||
|
CvInvoke.CvtColor(grayImage, colorImage, ColorConversion.Gray2Bgr);
|
||||||
|
|
||||||
|
if (bgaResults.Count == 0)
|
||||||
|
return colorImage;
|
||||||
|
|
||||||
|
// 半透明气泡填充
|
||||||
|
var overlay = colorImage.Clone();
|
||||||
|
foreach (var bga in bgaResults)
|
||||||
|
{
|
||||||
|
var fillColor = new MCvScalar(0, 200, 255);
|
||||||
|
foreach (var v in bga.Voids)
|
||||||
|
{
|
||||||
|
if (v.ContourPoints.Length > 0)
|
||||||
|
{
|
||||||
|
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||||
|
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||||
|
CvInvoke.DrawContours(overlay, vvop, 0, fillColor, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CvInvoke.AddWeighted(overlay, 0.4, colorImage, 0.6, 0, colorImage);
|
||||||
|
overlay.Dispose();
|
||||||
|
|
||||||
|
// 绘制焊球轮廓 + 编号
|
||||||
|
int ngCount = 0;
|
||||||
|
foreach (var bga in bgaResults)
|
||||||
|
{
|
||||||
|
var bgaColor = bga.Classification == "PASS"
|
||||||
|
? new MCvScalar(0, 255, 0) : new MCvScalar(0, 0, 255);
|
||||||
|
if (bga.Classification != "PASS") ngCount++;
|
||||||
|
|
||||||
|
if (bga.ContourPoints.Length > 0)
|
||||||
|
{
|
||||||
|
using var vop = new VectorOfPoint(bga.ContourPoints);
|
||||||
|
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||||
|
CvInvoke.DrawContours(colorImage, vvop, 0, bgaColor, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bbox = CvInvoke.BoundingRectangle(new VectorOfPoint(bga.ContourPoints));
|
||||||
|
CvInvoke.PutText(colorImage, $"#{bga.Index}",
|
||||||
|
new Point(bbox.X + bbox.Width / 2 - 10, bbox.Bottom + 16),
|
||||||
|
FontFace.HersheySimplex, 0.45, new MCvScalar(255, 100, 0), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左上角总览
|
||||||
|
int okCount = bgaResults.Count - ngCount;
|
||||||
|
var overallColor = ngCount > 0 ? new MCvScalar(0, 0, 255) : new MCvScalar(0, 255, 0);
|
||||||
|
CvInvoke.PutText(colorImage,
|
||||||
|
$"Total: {bgaResults.Count} | OK: {okCount} | NG: {ngCount}",
|
||||||
|
new Point(10, 25),
|
||||||
|
FontFace.HersheySimplex, 0.55, overallColor, 2);
|
||||||
|
|
||||||
|
return colorImage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -207,12 +207,65 @@ public class VoidMeasurementProcessor : ImageProcessorBase
|
|||||||
OutputData["Voids"] = voids;
|
OutputData["Voids"] = voids;
|
||||||
OutputData["ResultText"] = $"Void: {voidRate:F1}% | {classification} | {voids.Count} voids | ROI: {roiArea}px";
|
OutputData["ResultText"] = $"Void: {voidRate:F1}% | {classification} | {voids.Count} voids | ROI: {roiArea}px";
|
||||||
|
|
||||||
|
// 渲染带标注的彩色结果图像
|
||||||
|
OutputData["RenderedResultImage"] = RenderAnnotatedResult(inputImage, voids, voidRate, voidLimit, classification);
|
||||||
|
|
||||||
blurred.Dispose();
|
blurred.Dispose();
|
||||||
voidImg.Dispose();
|
voidImg.Dispose();
|
||||||
roiMask.Dispose();
|
roiMask.Dispose();
|
||||||
|
|
||||||
return inputImage.Clone();
|
return inputImage.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染带标注的彩色结果图像(轮廓、编号、半透明填充、总览信息)
|
||||||
|
/// </summary>
|
||||||
|
private Image<Bgr, byte> RenderAnnotatedResult(Image<Gray, byte> grayImage, List<VoidRegionInfo> voids, double voidRate, double voidLimit, string classification)
|
||||||
|
{
|
||||||
|
var colorImage = new Image<Bgr, byte>(grayImage.Width, grayImage.Height);
|
||||||
|
CvInvoke.CvtColor(grayImage, colorImage, ColorConversion.Gray2Bgr);
|
||||||
|
|
||||||
|
if (voids.Count == 0)
|
||||||
|
return colorImage;
|
||||||
|
|
||||||
|
// 半透明气泡填充
|
||||||
|
var overlay = colorImage.Clone();
|
||||||
|
foreach (var v in voids)
|
||||||
|
{
|
||||||
|
if (v.ContourPoints.Length > 0)
|
||||||
|
{
|
||||||
|
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||||
|
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||||
|
CvInvoke.DrawContours(overlay, vvop, 0, new MCvScalar(0, 200, 255), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CvInvoke.AddWeighted(overlay, 0.4, colorImage, 0.6, 0, colorImage);
|
||||||
|
overlay.Dispose();
|
||||||
|
|
||||||
|
// 绘制轮廓 + 编号
|
||||||
|
foreach (var v in voids)
|
||||||
|
{
|
||||||
|
if (v.ContourPoints.Length > 0)
|
||||||
|
{
|
||||||
|
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||||
|
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||||
|
CvInvoke.DrawContours(colorImage, vvop, 0, new MCvScalar(0, 255, 255), 1);
|
||||||
|
}
|
||||||
|
CvInvoke.PutText(colorImage, $"#{v.Index}",
|
||||||
|
new Point((int)v.CenterX - 8, (int)v.CenterY + 5),
|
||||||
|
FontFace.HersheySimplex, 0.35, new MCvScalar(255, 100, 0), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左上角总览
|
||||||
|
var overallColor = classification == "PASS"
|
||||||
|
? new MCvScalar(0, 255, 0) : new MCvScalar(0, 0, 255);
|
||||||
|
CvInvoke.PutText(colorImage,
|
||||||
|
$"Void: {voidRate:F1}% | Limit: {voidLimit:F0}% | {voids.Count} voids | {classification}",
|
||||||
|
new Point(10, 25),
|
||||||
|
FontFace.HersheySimplex, 0.5, overallColor, 2);
|
||||||
|
|
||||||
|
return colorImage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -67,5 +67,17 @@ namespace XplorePlane.Services
|
|||||||
|
|
||||||
return BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray8, null, pixels, stride);
|
return BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray8, null, pixels, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BitmapSource ToBitmapSourceFromBgr(Image<Bgr, byte> emguImage)
|
||||||
|
{
|
||||||
|
if (emguImage == null) throw new ArgumentNullException(nameof(emguImage));
|
||||||
|
|
||||||
|
int width = emguImage.Width;
|
||||||
|
int height = emguImage.Height;
|
||||||
|
byte[] pixels = emguImage.Bytes;
|
||||||
|
int stride = pixels.Length / height;
|
||||||
|
|
||||||
|
return BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgr24, null, pixels, stride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,13 +258,30 @@ namespace XplorePlane.Services
|
|||||||
var processedEmgu = processor.Process(emguImage);
|
var processedEmgu = processor.Process(emguImage);
|
||||||
progress?.Report(0.9);
|
progress?.Report(0.9);
|
||||||
|
|
||||||
var result = ImageConverter.ToBitmapSource(processedEmgu);
|
BitmapSource result;
|
||||||
|
|
||||||
|
// 如果处理器输出了渲染后的彩色结果图像,优先使用它
|
||||||
|
if (processor.OutputData.TryGetValue("RenderedResultImage", out var renderedObj)
|
||||||
|
&& renderedObj is Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte> renderedImage)
|
||||||
|
{
|
||||||
|
result = ImageConverter.ToBitmapSourceFromBgr(renderedImage);
|
||||||
|
renderedImage.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = ImageConverter.ToBitmapSource(processedEmgu);
|
||||||
|
}
|
||||||
|
|
||||||
result.Freeze();
|
result.Freeze();
|
||||||
progress?.Report(1.0);
|
progress?.Report(1.0);
|
||||||
|
|
||||||
var snapshot = new Dictionary<string, object>(processor.OutputData.Count);
|
var snapshot = new Dictionary<string, object>(processor.OutputData.Count);
|
||||||
foreach (var kv in processor.OutputData)
|
foreach (var kv in processor.OutputData)
|
||||||
|
{
|
||||||
|
// 不将大型图像对象序列化到快照中
|
||||||
|
if (kv.Key == "RenderedResultImage") continue;
|
||||||
snapshot[kv.Key] = kv.Value;
|
snapshot[kv.Key] = kv.Value;
|
||||||
|
}
|
||||||
|
|
||||||
return (result, snapshot);
|
return (result, snapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user