添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
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);
|
||||
}
|
||||
|
||||
// 将字节拷贝到 BackBuffer(UI 线程中调用)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user