添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
using MaxwellFramework.Core.Common;
|
||||
using MwFramework.Controls.ControlCanvas.Model;
|
||||
using MwFramework.ManagerService;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MainShell.Common.Display.Models
|
||||
{
|
||||
public class CameraAxisDevices : ViewModelBase
|
||||
{
|
||||
List<HardwareDevice> _hardwareDeviceList = new List<HardwareDevice>();
|
||||
HardwareDevice _selectedHardwareDevice = new HardwareDevice();
|
||||
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
|
||||
private int _selectedIndex = 0;
|
||||
private bool _cameraSwitchEnable = true;
|
||||
|
||||
public List<HardwareDevice> HardwareDeviceList
|
||||
{
|
||||
get { return _hardwareDeviceList; }
|
||||
set
|
||||
{
|
||||
if (_hardwareDeviceList == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hardwareDeviceList = value;
|
||||
|
||||
if (_hardwareDeviceList != null && _hardwareDeviceList.Count > 0)
|
||||
{
|
||||
_selectedIndex = 0;
|
||||
SelectedHardwareDevice = _hardwareDeviceList[_selectedIndex];
|
||||
RaisePropertyChanged(nameof(SelectedIndex));
|
||||
}
|
||||
|
||||
RaisePropertyChanged(nameof(HardwareDeviceList));
|
||||
}
|
||||
}
|
||||
public HardwareDevice SelectedHardwareDevice
|
||||
{
|
||||
get { return _selectedHardwareDevice; }
|
||||
set
|
||||
{
|
||||
if (_selectedHardwareDevice != null && _selectedHardwareDevice.Camera != null && value != null && value.Camera != null)
|
||||
{
|
||||
|
||||
}
|
||||
_selectedHardwareDevice = value; RaisePropertyChanged(nameof(SelectedHardwareDevice));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<object> Shapes
|
||||
{
|
||||
get { return _shapes; }
|
||||
set
|
||||
{
|
||||
if (_shapes != value) { _shapes = value; RaisePropertyChanged(nameof(Shapes)); }
|
||||
}
|
||||
}
|
||||
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
|
||||
public ObservableCollection<PrimShape> Regions
|
||||
{
|
||||
get { return _regions; }
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _regions, value);
|
||||
}
|
||||
}
|
||||
public int SelectedIndex
|
||||
{
|
||||
get { return _selectedIndex; }
|
||||
set
|
||||
{
|
||||
_selectedIndex = value;
|
||||
if (HardwareDeviceList != null && HardwareDeviceList.Count > 0 && _selectedIndex >= 0)
|
||||
{
|
||||
if (_selectedIndex < HardwareDeviceList.Count)
|
||||
{
|
||||
SelectedHardwareDevice = HardwareDeviceList[_selectedIndex];
|
||||
}
|
||||
|
||||
}
|
||||
RaisePropertyChanged(nameof(SelectedIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CameraSwitchEnable
|
||||
{
|
||||
get { return _cameraSwitchEnable; }
|
||||
set { _cameraSwitchEnable = value; RaisePropertyChanged(nameof(CameraSwitchEnable)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<UserControl x:Class="MainShell.Common.Display.View.CameraAxisView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:MainShell.Common.Display.View"
|
||||
xmlns:control="http://www.maxwell.com/controls"
|
||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<CheckBox Panel.ZIndex="2" IsChecked="{Binding IsOpen, UpdateSourceTrigger=PropertyChanged}" Cursor="Hand" Margin="2,2" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Top">
|
||||
<TextBlock Text="移动"/>
|
||||
</CheckBox>
|
||||
<control:CameraAxisControlA Name="cameraAxisControl"
|
||||
IsShowDoubleLine="False"
|
||||
IsAxisControlLDBVisible2="Visible"
|
||||
Cameras="{Binding CameraAxisDevices.HardwareDeviceList}"
|
||||
TabSelectedIndex ="{Binding CameraAxisDevices.SelectedIndex}"
|
||||
Items="{Binding CameraAxisDevices.Shapes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
||||
Regions="{Binding CameraAxisDevices.Regions}" ShapeThickness="1" IsShowSolidLine="True" DrawInConcurrency="False"
|
||||
>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="RecordPix">
|
||||
<i:InvokeCommandAction Command="{Binding OnLeftMouseDownCommand}" CommandParameter="{Binding ElementName=cameraAxisControl}" />
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
</control:CameraAxisControlA>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MainShell.Common.Display.View
|
||||
{
|
||||
/// <summary>
|
||||
/// CameraAxisView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CameraAxisView : UserControl
|
||||
{
|
||||
public CameraAxisView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<UserControl x:Class="MainShell.Common.Display.View.CameraView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:MainShell.Common.Display.View"
|
||||
xmlns:mw="http://www.maxwell.com/controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Border BorderThickness="1" BorderBrush ="#B0B9C4">
|
||||
<mw:DesignPanel x:Name="cameraDesignPanel"
|
||||
Source="{Binding DisplayImage}"
|
||||
Items="{Binding Shapes}"
|
||||
IsVirtualPix="Visible"
|
||||
IsShowSolidLine="True"
|
||||
IsCrossingLineVisible="False"
|
||||
IsNonRectCrossingLineVisible="False"
|
||||
IsImagePix="Collapsed" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MainShell.Common.Display.View
|
||||
{
|
||||
/// <summary>
|
||||
/// CameraView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CameraView : UserControl
|
||||
{
|
||||
public CameraView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
using MainShell.Common;
|
||||
using MainShell.Common.Display.Models;
|
||||
using MainShell.Log;
|
||||
using MainShell.Motion;
|
||||
using Maxwell.SemiFramework.DefaultConfig.Vision;
|
||||
using MwFramework.Controls.Components;
|
||||
using MwFramework.Controls.ControlCanvas.Model;
|
||||
using MwFramework.Controls.UIControl;
|
||||
using MwFramework.Device;
|
||||
using MwFramework.ManagerService;
|
||||
using Stylet;
|
||||
using StyletIoC;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MainShell.Common.Display.ViewModel
|
||||
{
|
||||
public class CameraAxisViewModel : Screen
|
||||
{
|
||||
private const double DefaultFovX = 14.1 * 10;
|
||||
private const double DefaultFovY = 10.3 * 10;
|
||||
private const string SafeAxisMotionNotInjectedMessage = "SafeAxisMotion is not injected.";
|
||||
|
||||
private readonly object _moveLock = new object();
|
||||
|
||||
private CameraAxisDevices _cameraAxisDevices = new CameraAxisDevices();
|
||||
private double _xPixelPos;
|
||||
private double _yPixelPos;
|
||||
private double _cameraXIndex;
|
||||
private double _cameraYIndex;
|
||||
private GetConvertValue _convertFunXDelegate;
|
||||
private GetConvertValue _convertFunYDelegate;
|
||||
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
|
||||
private bool _isCameraEnable = true;
|
||||
private Visibility _controlVisibility = Visibility.Visible;
|
||||
private bool _isOpen;
|
||||
|
||||
[Inject]
|
||||
public SafeAxisMotion SafeAxisMotion { get; set; }
|
||||
|
||||
public CameraAxisViewModel()
|
||||
{
|
||||
ConvertFunXDelegate = new GetConvertValue(ConvertFunX);
|
||||
ConvertFunYDelegate = new GetConvertValue(ConvertFunY);
|
||||
OnLeftMouseDownCommand = new DelegateCommand<object>(OnLeftMouseDown);
|
||||
}
|
||||
|
||||
public CameraAxisDevices CameraAxisDevices
|
||||
{
|
||||
get { return _cameraAxisDevices; }
|
||||
set { SetAndNotify(ref _cameraAxisDevices, value); }
|
||||
}
|
||||
|
||||
public double XPixelPos
|
||||
{
|
||||
get { return _xPixelPos; }
|
||||
set { SetAndNotify(ref _xPixelPos, value); }
|
||||
}
|
||||
|
||||
public double YPixelPos
|
||||
{
|
||||
get { return _yPixelPos; }
|
||||
set { SetAndNotify(ref _yPixelPos, value); }
|
||||
}
|
||||
|
||||
public double CameraXIndex
|
||||
{
|
||||
get { return _cameraXIndex; }
|
||||
set { SetAndNotify(ref _cameraXIndex, value); }
|
||||
}
|
||||
|
||||
public double CameraYIndex
|
||||
{
|
||||
get { return _cameraYIndex; }
|
||||
set { SetAndNotify(ref _cameraYIndex, value); }
|
||||
}
|
||||
|
||||
public GetConvertValue ConvertFunXDelegate
|
||||
{
|
||||
get { return _convertFunXDelegate; }
|
||||
set { SetAndNotify(ref _convertFunXDelegate, value); }
|
||||
}
|
||||
|
||||
public GetConvertValue ConvertFunYDelegate
|
||||
{
|
||||
get { return _convertFunYDelegate; }
|
||||
set { SetAndNotify(ref _convertFunYDelegate, value); }
|
||||
}
|
||||
|
||||
public ObservableCollection<PrimShape> Regions
|
||||
{
|
||||
get { return _regions; }
|
||||
set { SetAndNotify(ref _regions, value); }
|
||||
}
|
||||
|
||||
public bool IsCameraEnable
|
||||
{
|
||||
get { return _isCameraEnable; }
|
||||
set { SetAndNotify(ref _isCameraEnable, value); }
|
||||
}
|
||||
|
||||
public Visibility ControlVisibility
|
||||
{
|
||||
get { return _controlVisibility; }
|
||||
set { SetAndNotify(ref _controlVisibility, value); }
|
||||
}
|
||||
|
||||
public ICommand OnLeftMouseDownCommand { get; private set; }
|
||||
|
||||
public bool IsOpen
|
||||
{
|
||||
get { return _isOpen; }
|
||||
set { SetAndNotify(ref _isOpen, value); }
|
||||
}
|
||||
|
||||
public void OnLeftMouseDown(object sender)
|
||||
{
|
||||
try
|
||||
{
|
||||
HardwareDevice selectedDevice;
|
||||
if (!TryGetSelectedDevice(out selectedDevice))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CameraAxisControlA controlA = sender as CameraAxisControlA;
|
||||
if (controlA == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point pixelPos = new Point(controlA.YPixPos, controlA.XPixPos);
|
||||
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.LogSysError(ex);
|
||||
ShowOperationFailed(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void MovePosAction()
|
||||
{
|
||||
try
|
||||
{
|
||||
HardwareDevice selectedDevice;
|
||||
if (!TryGetSelectedDevice(out selectedDevice))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point pixelPos = new Point(YPixelPos, XPixelPos);
|
||||
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.LogSysError(ex);
|
||||
ShowOperationFailed(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public double ConvertFunX(double interval)
|
||||
{
|
||||
try
|
||||
{
|
||||
HardwareDevice selectedDevice;
|
||||
if (!TryGetSelectedDevice(out selectedDevice))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
string name = selectedDevice.Camera.Id;
|
||||
double dx;
|
||||
double dy;
|
||||
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
|
||||
double pixelX = interval / Math.Abs(dx);
|
||||
return pixelX;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.LogSysError(ex);
|
||||
ShowOperationFailed(ex.Message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public double ConvertFunY(double interval)
|
||||
{
|
||||
try
|
||||
{
|
||||
HardwareDevice selectedDevice;
|
||||
if (!TryGetSelectedDevice(out selectedDevice))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
string name = selectedDevice.Camera.Id;
|
||||
double dx;
|
||||
double dy;
|
||||
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
|
||||
double pixelY = interval / Math.Abs(dy);
|
||||
return pixelY;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.LogSysError(ex);
|
||||
ShowOperationFailed(ex.Message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetSelectedDevice(out HardwareDevice selectedDevice)
|
||||
{
|
||||
selectedDevice = CameraAxisDevices != null ? CameraAxisDevices.SelectedHardwareDevice : null;
|
||||
if (!IsOpen || selectedDevice == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDevice.Camera == null || selectedDevice.Camera.Camera == null)
|
||||
{
|
||||
ShowDeviceNotInitialized();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!selectedDevice.Camera.Camera.IsGrabbing)
|
||||
{
|
||||
LocalizedMessageBox.Show(MessageKey.VisionCameraNotGrabbing, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDevice.AxisX == null || selectedDevice.AxisY == null)
|
||||
{
|
||||
ShowDeviceNotInitialized();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void MoveToPixelPoint(HardwareDevice selectedDevice, Point pixelPos, bool requireConfirmation)
|
||||
{
|
||||
lock (_moveLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
IAxis axisX = selectedDevice.AxisX;
|
||||
IAxis axisY = selectedDevice.AxisY;
|
||||
Point rulerPos = new Point(axisX.State.ActualPos, axisY.State.ActualPos);
|
||||
Point newRulerPoint = Algorithm.Instance.MoveToPixelPoint(selectedDevice.Camera.Id, rulerPos, pixelPos);
|
||||
|
||||
if (requireConfirmation)
|
||||
{
|
||||
double dx = newRulerPoint.X - rulerPos.X;
|
||||
double dy = newRulerPoint.Y - rulerPos.Y;
|
||||
if (!ValidateMoveRange(dx, dy))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ConfirmMove(newRulerPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (SafeAxisMotion == null)
|
||||
{
|
||||
InvalidOperationException exception = new InvalidOperationException(SafeAxisMotionNotInjectedMessage);
|
||||
LogManager.LogSysError(exception);
|
||||
ShowOperationFailed(SafeAxisMotionNotInjectedMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
SafeAxisMotion.SafeMove(
|
||||
MotionMoveRequest.ForAxis(axisX, newRulerPoint.X),
|
||||
MotionMoveRequest.ForAxis(axisY, newRulerPoint.Y));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.LogSysError(ex);
|
||||
ShowOperationFailed(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValidateMoveRange(double dx, double dy)
|
||||
{
|
||||
if (Math.Abs(dx) > DefaultFovX || Math.Abs(dy) > DefaultFovY)
|
||||
{
|
||||
LocalizedMessageBox.Show(MessageKey.ParamOutOfRange, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ConfirmMove(Point newRulerPoint)
|
||||
{
|
||||
string message = string.Format("新点的坐标({0},{1}),是否移动?", newRulerPoint.X, newRulerPoint.Y);
|
||||
return MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleConfirm), MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel) == MessageBoxResult.OK;
|
||||
}
|
||||
|
||||
private static void ShowDeviceNotInitialized()
|
||||
{
|
||||
LocalizedMessageBox.Show(MessageKey.DeviceNotInitialized, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
|
||||
private static void ShowOperationFailed(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
LocalizedMessageBox.Show(MessageKey.CommonOperationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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