Files
test_demo/MX-PD-盘古 - new/PanGu.DieBonderApp/MainShell/Common/Display/ViewModel/CameraViewModel.cs
Shi.Ji e31d3560bb 添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
2026-05-18 11:43:09 +08:00

318 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MainShell.EventArgsFolder;
using MainShell.Log;
using MainShell.Vision;
using MwFramework.Device.Model;
using MwFramework.Controls.ControlCanvas.DrawingControl;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using AppCameraType = MainShell.Common.CameraType;
using System.Windows.Media;
namespace MainShell.Common.Display.ViewModel
{
public class CameraViewModel : Screen, IHandle<DisplayImageEventArgs>, IHandle<DisplayFindTemplateResultEventArgs>
{
private readonly IEventAggregator _eventAggregator;
public AppCameraType CameraType { get; }
private WriteableBitmap currentImage;
private WriteableBitmap _displayImage;
public WriteableBitmap DisplayImage
{
get { return _displayImage; }
set
{
SetAndNotify(ref _displayImage, value);
}
}
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
public ObservableCollection<object> Shapes
{
get { return _shapes; }
set
{
if (_shapes != value) { _shapes = value; OnPropertyChanged(nameof(Shapes)); }
}
}
/// <summary>
/// 根据 CameraData 更新并返回 WriteableBitmap。
/// 优化要点:
/// - 重用已有 WriteableBitmap当尺寸与像素格式相同时以减少分配。
/// - 校验参数与缓冲区长度。
/// - 捕获并记录异常,失败时返回 null不抛出
/// </summary>
public WriteableBitmap UpdateBitmap(CameraData cameraData)
{
if (cameraData == null)
{
"图片数据为空".LogSysError();
return null;
}
try
{
int width = (int)cameraData.Width;
int height = (int)cameraData.Height;
int bitCnt = cameraData.Bit;
byte[] buffer = cameraData.BufferData;
if (width <= 0 || height <= 0)
{
$"CameraViewModel.UpdateBitmap: invalid size {width}x{height}".LogSysError();
return null;
}
// 选择像素格式
System.Windows.Media.PixelFormat pixelFormat = bitCnt == 1
? System.Windows.Media.PixelFormats.Gray8
: (bitCnt == 3 ? System.Windows.Media.PixelFormats.Bgr24 : System.Windows.Media.PixelFormats.Bgra32);
// 计算每行字节数stride
int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8;
int stride = width * bytesPerPixel;
long requiredLength = (long)stride * height;
if (buffer == null)
{
"CameraViewModel.UpdateBitmap: buffer is null".LogSysError();
return null;
}
if (buffer.Length < requiredLength)
{
($"CameraViewModel.UpdateBitmap: buffer length {buffer.Length} is less than required {requiredLength}").LogSysError();
return null;
}
// 如果现有 bitmap 可复用(尺寸和格式一致),就重用以减少 GC/分配
if (currentImage == null ||
currentImage.PixelWidth != width ||
currentImage.PixelHeight != height ||
!currentImage.Format.Equals(pixelFormat))
{
// 新建 WriteableBitmap
currentImage = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
}
// 将字节拷贝到 BackBufferUI 线程中调用)
currentImage.Lock();
try
{
// BackBuffer 为 IntPtr使用 Marshal.Copy 从 byte[] 拷贝
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, currentImage.BackBuffer, (int)requiredLength);
currentImage.AddDirtyRect(new Int32Rect(0, 0, width, height));
}
finally
{
currentImage.Unlock();
}
return currentImage;
}
catch (Exception ex)
{
($"CameraViewModel.UpdateBitmap exception: {ex}").LogSysError();
return null;
}
}
public CameraViewModel(IEventAggregator eventAggregator, AppCameraType cameraType)
{
_eventAggregator = eventAggregator;
CameraType = cameraType;
_eventAggregator?.Subscribe(this);
}
public void Handle(DisplayImageEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
WriteableBitmap bmp = UpdateBitmap(message.CameraData);
// 只有在成功生成 bitmap 时才更新 DisplayImage避免把 null 赋值导致 UI 清空
if (bmp != null)
{
DisplayImage = bmp;
}
});
}
public void Handle(DisplayFindTemplateResultEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
ApplyTemplateResultShapes(message);
});
}
private void ApplyTemplateResultShapes(DisplayFindTemplateResultEventArgs message)
{
if (message.ClearShapesBeforeDisplay)
{
Shapes.Clear();
}
if (message.Result == null)
{
return;
}
AppendCenterShapes(message.Result);
AppendContourShape(message.Result.Contour);
AppendCircleShapes(message.Result.Circles);
AppendLineShapes(message.Result.Lines);
AppendRectangleShapes(message.Result.Rectangles);
}
private void AppendCenterShapes(FindTemplateResult result)
{
if (result == null || !AreFinite(result.CenterX, result.CenterY, result.Score))
{
return;
}
ShapeDrawing pointShape = new ShapeDrawing();
pointShape.X = result.CenterX;
pointShape.Y = result.CenterY;
Shapes.Add(pointShape);
TextShapeDrawing textShape = new TextShapeDrawing();
textShape.X = result.CenterX;
textShape.Y = result.CenterY;
textShape.Text = $"Score:{result.Score:F3}";
Shapes.Add(textShape);
}
private void AppendContourShape(FindTemplateContourResult contour)
{
if (contour == null || !AreFinite(contour.StartX, contour.StartY, contour.EndX, contour.EndY))
{
return;
}
RectShapeDrawing contourShape = new RectShapeDrawing();
contourShape.X = contour.StartX;
contourShape.Y = contour.StartY;
contourShape.X1 = contour.EndX;
contourShape.Y1 = contour.EndY;
Shapes.Add(contourShape);
}
private void AppendCircleShapes(IList<FindTemplateCircleResult> circles)
{
if (circles == null)
{
return;
}
foreach (FindTemplateCircleResult circle in circles)
{
if (circle == null || !AreFinite(circle.CenterX, circle.CenterY, circle.Radius))
{
continue;
}
CircleShapeDrawing circleShape = new CircleShapeDrawing();
circleShape.X = circle.CenterX;
circleShape.Y = circle.CenterY;
circleShape.CenterPoint = new Point(circle.CenterX, circle.CenterY);
circleShape.RadiusX = circle.Radius;
circleShape.RadiusY = circle.Radius;
circleShape.LineWidth = 1;
circleShape.FillColor = Brushes.Blue;
Shapes.Add(circleShape);
}
}
private void AppendLineShapes(IList<FindTemplateLineResult> lines)
{
if (lines == null)
{
return;
}
foreach (FindTemplateLineResult line in lines)
{
if (line == null || !AreFinite(line.StartX, line.StartY, line.EndX, line.EndY))
{
continue;
}
LineShapeDrawing lineShape = new LineShapeDrawing();
lineShape.X = line.StartX;
lineShape.Y = line.StartY;
lineShape.X1 = line.EndX;
lineShape.Y1 = line.EndY;
Shapes.Add(lineShape);
}
}
private void AppendRectangleShapes(IList<FindTemplateRectangleResult> rectangles)
{
if (rectangles == null)
{
return;
}
foreach (FindTemplateRectangleResult rectangle in rectangles)
{
if (rectangle == null || !AreFinite(rectangle.StartX, rectangle.StartY, rectangle.Width, rectangle.Height, rectangle.Angle))
{
continue;
}
double halfWidth = rectangle.Width / 2d;
double halfHeight = rectangle.Height / 2d;
double cosAngle = Math.Cos(rectangle.Angle);
double sinAngle = Math.Sin(rectangle.Angle);
double centerX = rectangle.StartX + (halfWidth * cosAngle) - (halfHeight * sinAngle);
double centerY = rectangle.StartY + (halfWidth * sinAngle) + (halfHeight * cosAngle);
RectShape2Drawing rectangleShape = new RectShape2Drawing();
rectangleShape.X = centerX;
rectangleShape.Y = centerY;
rectangleShape.CenterX = centerX;
rectangleShape.CenterY = centerY;
rectangleShape.HalfWidth = halfWidth;
rectangleShape.HalfHeigth = halfHeight;
rectangleShape.Angle = rectangle.Angle;
Shapes.Add(rectangleShape);
}
}
private static bool AreFinite(params double[] values)
{
if (values == null)
{
return false;
}
foreach (double value in values)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return false;
}
}
return true;
}
}
}