1.MVS中确保相机正常

前端代码
<Grid RowDefinitions="50,40,30,*">
<ComboBox ItemsSource="{Binding CameraList}"
SelectedItem="{Binding SelectedCamera}"
PlaceholderText="请选择一个相机" />
<StackPanel Grid.Row="1" Orientation="Horizontal">
<TextBox Text="{Binding CameraIP}" Foreground="White" ></TextBox>
<Button Click="OpenCamera" Width="80" HorizontalAlignment="Stretch" Content="OpenCamera" Classes.active="{Binding VisionPageIsActive}">
<Button Click="CloseCamera" Width="80" HorizontalAlignment="Stretch" Content="关闭相机" Foreground="White"></Button>
<Button Click="GetCameraFrame" Width="80" HorizontalAlignment="Stretch" Content="获取当前帧" Foreground="White" Background="Green"></Button>
<TextBox Text="{Binding ProgramLog}" Foreground="White" ></TextBox>
</StackPanel>
<Grid Grid.Row="3" Background="LightSkyBlue">
<Image x:Name="MyImage" Source="{Binding DisplayImage}" Stretch="UniformToFill" />
</Grid>
</Grid>
代码段
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media.Imaging;
using LinkAsiaVisionV2.ViewModels;
using MvCameraControl;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace LinkAsiaVisionV2.Views
{
public partial class MainView : Window
{
// ch:枚举的相机类型 | en:TLayerType for enumerate devices
readonly DeviceTLayerType enumTLayerType = DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvUsbDevice
| DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvGenTLCXPDevice | DeviceTLayerType.MvGenTLCameraLinkDevice | DeviceTLayerType.MvGenTLXoFDevice;
List<IDeviceInfo> deviceInfoList = new List<IDeviceInfo>();
IDevice device = null;
bool isGrabbing = true; // ch:是否正在取图 | en: Grabbing flag
Thread receiveThread = null; // ch:接收图像线程 | en: Receive image thread
IntPtr pictureBoxHandle = IntPtr.Zero; // ch:显示图像的控件句柄 | en: Control handle for image display
public MainView()
{
InitializeComponent();
}
// 重写 OnLoaded 方法
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e); // 调用基类方法,确保事件链正常传递
// 在这里编写你的初始化逻辑
Console.WriteLine("窗口已完全加载!");
// 例如:加载数据、启动动画、或初始化第三方组件
// ch: 初始化 SDK | en: Initialize SDK
SDKSystem.Initialize();
// ch: 枚举设备 | en: Enum Device List
RefreshDeviceList();
}
private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
this.BeginMoveDrag(e);
}
private void RefreshDeviceList()
{
if (DataContext is MainViewModel vm)
{
vm.CameraList.Clear();
// ch:创建设备列表 | en:Create Device List
int nRet = DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList);
if (nRet != MvError.MV_OK)
{
// ShowErrorMsg("Enumerate devices fail!", nRet);
return;
}
// ch:在窗体列表中显示设备名 | en:Display device name in the form list
for (int i = 0; i < deviceInfoList.Count; i++)
{
IDeviceInfo deviceInfo = deviceInfoList[i];
IGigEDeviceInfo gigEDevice = (IGigEDeviceInfo)deviceInfo;
uint nIp1 = ((gigEDevice.CurrentIp & 0xff000000) >> 24);
uint nIp2 = ((gigEDevice.CurrentIp & 0x00ff0000) >> 16);
uint nIp3 = ((gigEDevice.CurrentIp & 0x0000ff00) >> 8);
uint nIp4 = (gigEDevice.CurrentIp & 0x000000ff);
Console.WriteLine("DevIP: {0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
if (deviceInfo.UserDefinedName != "")
{
vm.CameraList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")");
}
else
{
vm.CameraList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + $" IP: [{nIp1}.{nIp2}.{nIp3}.{nIp4}] " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")");
}
}
}
}
private void OpenCamera(object? sender, RoutedEventArgs e)
{
int ret = 0;
if (DataContext is MainViewModel vm)
{
int index = vm.CameraList.FindIndex(s => s.Contains(vm.CameraIP));
// vm.CameraList.ind(vm.CameraIP);
IDeviceInfo deviceInfo = deviceInfoList[index];
/*创建相机实例*/
device = DeviceFactory.CreateDevice(deviceInfo);
/*打开相机*/
ret = device.Open();
vm.ProgramLog = $"相机打开 ret = {ret}";
//MessageBox.Show(errorMsg, "PROMPT");
/*如果是网口相机*/
if (device is IGigEDevice)
{
IGigEDevice gigEDevice = device as IGigEDevice;
/*配置网口相机的最佳包大小*/
int packetSize;
gigEDevice.GetOptimalPacketSize(out packetSize);
gigEDevice.Parameters.SetIntValue("GevSCPSPacketSize", packetSize);
}
else if (device is IUSBDevice)
{
/*设置USB同步读写超时时间*/
IUSBDevice usbDevice = device as IUSBDevice;
usbDevice.SetSyncTimeOut(1000);
}
// ch:设置采集连续模式 | en:Set Continues Acquisition Mode
device.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");
device.Parameters.SetEnumValueByString("TriggerMode", "Off");
//receiveThread = new Thread(ReceiveThreadProcess);
//receiveThread.Start();
// ch:开启抓图线程 | en: Start the grabbing thread
//Thread GrabThread = new Thread(FrameGrabThread);
//GrabThread.Start(device.StreamGrabber);
//GrabThread.Abort();
// ch:开启抓图 | en: start grab image
ret = device.StreamGrabber.StartGrabbing();
if (ret != MvError.MV_OK)
{
isGrabbing = false;
// receiveThread.Join();
Console.WriteLine("Start grabbing failed:{0:x8}", ret);
return;
}
vm.ProgramLog = $"开始采集 ret = {ret}";
}
}
private void CloseCamera(object? sender, RoutedEventArgs e)
{
int ret = 0;
if (DataContext is MainViewModel vm)
{
ret = device.StreamGrabber.StopGrabbing();
/*关闭相机*/
ret = device.Close();
if (ret != MvError.MV_OK)
{
Console.WriteLine("Close device fail: {0}", ret);
}
else
{
Console.WriteLine("Close device success!");
}
// ch:销毁设备 | en:Destroy device
device.Dispose();
device = null;
vm.ProgramLog = $"相机关闭 ret = {ret}";
}
}
private void GetCameraFrame(object? sender, RoutedEventArgs e)
{
if (DataContext is MainViewModel vm)
{
// while (isGrabbing)
{
IFrameOut frameOut;
int nRet = device.StreamGrabber.GetImageBuffer(1000, out frameOut);
if (MvError.MV_OK == nRet)
{
IImage inputImage = frameOut.Image;
OpenCvSharp.Mat mat = ByteToMatColor(inputImage.PixelData, ((int)inputImage.Width), ((int)inputImage.Height));
vm.CameraBitmap = ToAvaloniaBitmap(mat);
vm.DisplayImage = ToAvaloniaBitmap(mat);
vm.ProgramLog = $"获取当前帧 Width = {frameOut.Image.Width} {DateTime.Now}";
}
else
{
vm.ProgramLog = $"获取当前帧失败 {nRet} {DateTime.Now}";
}
device.StreamGrabber.FreeImageBuffer(frameOut);
}
}
}
public OpenCvSharp.Mat ByteToMatColor(byte[] data, int width, int height)
{
OpenCvSharp.Mat src = new OpenCvSharp.Mat(height, width, OpenCvSharp.MatType.CV_8UC3);
int length = height * width * 3; // or src.Height * src.Step;
Marshal.Copy(data, 0, src.Data, length);
return src;
}
public Bitmap ToAvaloniaBitmap( OpenCvSharp.Mat mat)
{
if (mat == null || mat.Empty())
throw new ArgumentNullException(nameof(mat));
OpenCvSharp.Mat matRgb = new OpenCvSharp.Mat();
// OpenCV 默认为 BGR 格式,必须转成 RGB
if (mat.Channels() == 3)
{
OpenCvSharp.Cv2.CvtColor(mat, matRgb, OpenCvSharp.ColorConversionCodes.BGR2RGB);
}
// 灰度图
else if (mat.Channels() == 1)
{
OpenCvSharp.Cv2.CvtColor(mat, matRgb, OpenCvSharp.ColorConversionCodes.GRAY2BGR);
OpenCvSharp.Cv2.CvtColor(matRgb, matRgb, OpenCvSharp.ColorConversionCodes.BGR2RGB);
}
else
{
matRgb = mat.Clone();
}
try
{
// 内存流编码为 JPG/PNG
using var ms = new System.IO.MemoryStream();
matRgb.WriteToStream(ms);
// 关键:Avalonia 从流创建 Bitmap
ms.Position = 0;
return new Bitmap(ms);
}
finally
{
matRgb.Dispose(); // 必须释放,防止内存泄漏
}
}
}
}
ViewModel代码
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using LinkAsiaVisionV2.Factories;
using LinkAsiaVisionV2.Message;
using LinkAsiaVisionV2.Views;
using Microsoft.Extensions.DependencyInjection;
using MvCameraControl;
using System;
using System.Collections.Generic;
using Tmds.DBus.Protocol;
namespace LinkAsiaVisionV2.ViewModels
{
public partial class MainViewModel : ViewModelBase
{
private PageFactory _pageFactory;
[ObservableProperty]
public partial List<string> CameraList { get; set; } = new List<string>();
[ObservableProperty]
public partial string SelectedCamera { get; set; }
[ObservableProperty]
public partial string ProgramLog { get; set; }
[ObservableProperty]
public partial Bitmap CameraBitmap { get; set; }
[ObservableProperty]
private Bitmap? _displayImage;
[ObservableProperty]
public partial string CameraIP { get; set; } = "192.168.0.11";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(VisionPageIsActive))]
private PageViewModel _currentPage;
public MainViewModel(PageFactory pageFactory)
{
WeakReferenceMessenger.Default.Register<OrderMessage>(this, (recipient, message) =>
{
// 当收到 MyMessage 时,这个 Lambda 表达式会被执行
//message.Key
var receivedValue = message.Value;
//IsVisibleVisionArea = true;
// 处理消息...
});
_pageFactory = pageFactory;
GotoHome();
}
[RelayCommand]
public void GotoHome()
{
CurrentPage = _pageFactory.GetPageViewModel(Data.ApplicationPageNames.Home);
}
}
}
需要注意的是bitmap必须是Avalonia的,不能是system.drawing的,因为这个问题耽误了一天才弄出来