合并图像处理库,删除图像lib库
This commit is contained in:
@@ -12,8 +12,10 @@ using XplorePlane.Services.Cnc;
|
||||
using XplorePlane.Services.Matrix;
|
||||
using XplorePlane.Services.Measurement;
|
||||
using XplorePlane.Services.Recipe;
|
||||
using XplorePlane.Services.Camera;
|
||||
using XplorePlane.ViewModels.Cnc;
|
||||
using XplorePlane.Views.Cnc;
|
||||
using XP.Camera;
|
||||
using Prism.Ioc;
|
||||
using Prism.DryIoc;
|
||||
using Prism.Modularity;
|
||||
@@ -146,6 +148,22 @@ namespace XplorePlane
|
||||
Log.Error(ex, "射线源资源释放失败");
|
||||
}
|
||||
|
||||
// 释放相机服务资源
|
||||
try
|
||||
{
|
||||
var bootstrapper = AppBootstrapper.Instance;
|
||||
if (bootstrapper != null)
|
||||
{
|
||||
var cameraService = bootstrapper.Container.Resolve<ICameraService>();
|
||||
cameraService?.Dispose();
|
||||
Log.Information("相机服务资源已释放");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "相机服务资源释放失败");
|
||||
}
|
||||
|
||||
Log.CloseAndFlush();
|
||||
base.OnExit(e);
|
||||
}
|
||||
@@ -211,6 +229,7 @@ namespace XplorePlane
|
||||
containerRegistry.RegisterForNavigation<MainWindow>();
|
||||
containerRegistry.RegisterForNavigation<MainWindowB>();
|
||||
containerRegistry.Register<MainViewModel>();
|
||||
containerRegistry.RegisterSingleton<NavigationPropertyPanelViewModel>();
|
||||
|
||||
// 注册图像处理服务与视图
|
||||
containerRegistry.RegisterSingleton<IImageProcessingService, ImageProcessingService>();
|
||||
@@ -266,6 +285,12 @@ namespace XplorePlane
|
||||
containerRegistry.RegisterForNavigation<CncPageView>();
|
||||
containerRegistry.RegisterForNavigation<MatrixPageView>();
|
||||
|
||||
// ── 相机服务(单例)──
|
||||
containerRegistry.RegisterSingleton<ICameraFactory, CameraFactory>();
|
||||
containerRegistry.RegisterSingleton<ICameraController>(() =>
|
||||
new CameraFactory().CreateController("Basler"));
|
||||
containerRegistry.RegisterSingleton<ICameraService, CameraService>();
|
||||
|
||||
Log.Information("依赖注入容器配置完成");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using XP.Camera;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XplorePlane.Models;
|
||||
using XplorePlane.Services.AppState;
|
||||
|
||||
namespace XplorePlane.Services.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// 相机管理服务实现,封装 ICameraController 并将状态同步到 IAppStateService。
|
||||
/// </summary>
|
||||
public class CameraService : ICameraService
|
||||
{
|
||||
private readonly ICameraController _controller;
|
||||
private readonly IAppStateService _appState;
|
||||
private readonly ILoggerService _logger;
|
||||
|
||||
private volatile bool _liveViewRunning;
|
||||
private bool _disposed;
|
||||
private readonly Stopwatch _fpsStopwatch = new();
|
||||
private int _frameCount;
|
||||
|
||||
public bool IsConnected => _controller.IsConnected;
|
||||
public bool IsGrabbing => _controller.IsGrabbing;
|
||||
public bool IsLiveView => _liveViewRunning;
|
||||
|
||||
public event EventHandler<BitmapSource> FrameArrived;
|
||||
public event EventHandler ConnectionLost;
|
||||
|
||||
public CameraService(
|
||||
ICameraController controller,
|
||||
IAppStateService appState,
|
||||
ILoggerService logger)
|
||||
{
|
||||
_controller = controller ?? throw new ArgumentNullException(nameof(controller));
|
||||
_appState = appState ?? throw new ArgumentNullException(nameof(appState));
|
||||
_logger = logger?.ForModule<CameraService>() ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_controller.ImageGrabbed += OnImageGrabbed;
|
||||
_controller.GrabError += OnGrabError;
|
||||
_controller.ConnectionLost += OnConnectionLost;
|
||||
}
|
||||
|
||||
public CameraInfo Connect()
|
||||
{
|
||||
var info = _controller.Open();
|
||||
_logger.Info("相机已连接: {ModelName} (SN: {SerialNumber})", info.ModelName, info.SerialNumber);
|
||||
|
||||
var resolution = $"{_controller.GetWidth()}x{_controller.GetHeight()}";
|
||||
_appState.UpdateCameraState(new CameraState(
|
||||
IsConnected: true,
|
||||
IsStreaming: false,
|
||||
CurrentFrame: null,
|
||||
Width: _controller.GetWidth(),
|
||||
Height: _controller.GetHeight(),
|
||||
FrameRate: 0));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
_liveViewRunning = false;
|
||||
_controller.Close();
|
||||
_appState.UpdateCameraState(CameraState.Default);
|
||||
_logger.Info("相机已断开");
|
||||
}
|
||||
|
||||
public void StartGrabbing()
|
||||
{
|
||||
_controller.StartGrabbing();
|
||||
_fpsStopwatch.Restart();
|
||||
_frameCount = 0;
|
||||
UpdateStreamingState(true);
|
||||
}
|
||||
|
||||
public void TriggerOnce()
|
||||
{
|
||||
_controller.ExecuteSoftwareTrigger();
|
||||
}
|
||||
|
||||
public void StopGrabbing()
|
||||
{
|
||||
_liveViewRunning = false;
|
||||
_controller.StopGrabbing();
|
||||
_fpsStopwatch.Stop();
|
||||
UpdateStreamingState(false);
|
||||
}
|
||||
|
||||
public void StartLiveView()
|
||||
{
|
||||
if (!_controller.IsGrabbing)
|
||||
StartGrabbing();
|
||||
|
||||
_liveViewRunning = true;
|
||||
_controller.ExecuteSoftwareTrigger();
|
||||
_logger.Info("实时预览已启动");
|
||||
}
|
||||
|
||||
public void StopLiveView()
|
||||
{
|
||||
_liveViewRunning = false;
|
||||
_logger.Info("实时预览已停止");
|
||||
}
|
||||
|
||||
// ── 参数读写(直接委托给 controller)──
|
||||
|
||||
public double GetExposureTime() => _controller.GetExposureTime();
|
||||
public void SetExposureTime(double microseconds) => _controller.SetExposureTime(microseconds);
|
||||
public double GetGain() => _controller.GetGain();
|
||||
public void SetGain(double value) => _controller.SetGain(value);
|
||||
public int GetWidth() => _controller.GetWidth();
|
||||
public void SetWidth(int value) => _controller.SetWidth(value);
|
||||
public int GetHeight() => _controller.GetHeight();
|
||||
public void SetHeight(int value) => _controller.SetHeight(value);
|
||||
public string GetPixelFormat() => _controller.GetPixelFormat();
|
||||
public void SetPixelFormat(string format) => _controller.SetPixelFormat(format);
|
||||
|
||||
// ── 事件处理 ──
|
||||
|
||||
private void OnImageGrabbed(object sender, ImageGrabbedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = PixelConverter.ToBitmapSource(e.PixelData, e.Width, e.Height, e.PixelFormat);
|
||||
|
||||
// 计算帧率
|
||||
_frameCount++;
|
||||
double fps = 0;
|
||||
if (_fpsStopwatch.ElapsedMilliseconds > 0)
|
||||
fps = _frameCount / (_fpsStopwatch.ElapsedMilliseconds / 1000.0);
|
||||
|
||||
// 更新全局状态
|
||||
_appState.UpdateCameraState(new CameraState(
|
||||
IsConnected: true,
|
||||
IsStreaming: true,
|
||||
CurrentFrame: bitmap,
|
||||
Width: e.Width,
|
||||
Height: e.Height,
|
||||
FrameRate: fps));
|
||||
|
||||
// 通知 UI
|
||||
var app = Application.Current;
|
||||
app?.Dispatcher.BeginInvoke(() => FrameArrived?.Invoke(this, bitmap));
|
||||
|
||||
// 链式触发下一帧
|
||||
if (_liveViewRunning)
|
||||
_controller.ExecuteSoftwareTrigger();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "处理相机图像帧时出错");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGrabError(object sender, GrabErrorEventArgs e)
|
||||
{
|
||||
_logger.Warn($"相机采集错误: ErrorCode={e.ErrorCode}, {e.ErrorDescription}");
|
||||
}
|
||||
|
||||
private void OnConnectionLost(object sender, EventArgs e)
|
||||
{
|
||||
_liveViewRunning = false;
|
||||
_appState.UpdateCameraState(CameraState.Default);
|
||||
_logger.Warn("相机连接意外断开");
|
||||
|
||||
var app = Application.Current;
|
||||
app?.Dispatcher.BeginInvoke(() => ConnectionLost?.Invoke(this, EventArgs.Empty));
|
||||
}
|
||||
|
||||
private void UpdateStreamingState(bool isStreaming)
|
||||
{
|
||||
var current = _appState.CameraState;
|
||||
_appState.UpdateCameraState(current with { IsStreaming = isStreaming });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
_liveViewRunning = false;
|
||||
_controller.ImageGrabbed -= OnImageGrabbed;
|
||||
_controller.GrabError -= OnGrabError;
|
||||
_controller.ConnectionLost -= OnConnectionLost;
|
||||
_controller.Dispose();
|
||||
|
||||
_logger.Info("CameraService 已释放");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Windows.Media.Imaging;
|
||||
using XP.Camera;
|
||||
|
||||
namespace XplorePlane.Services.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// 相机管理服务接口,封装 ICameraController 并桥接到 AppStateService。
|
||||
/// </summary>
|
||||
public interface ICameraService : IDisposable
|
||||
{
|
||||
bool IsConnected { get; }
|
||||
bool IsGrabbing { get; }
|
||||
bool IsLiveView { get; }
|
||||
|
||||
/// <summary>连接相机,返回设备信息。</summary>
|
||||
CameraInfo Connect();
|
||||
|
||||
/// <summary>断开相机连接。</summary>
|
||||
void Disconnect();
|
||||
|
||||
/// <summary>启动单帧采集(软件触发模式)。</summary>
|
||||
void StartGrabbing();
|
||||
|
||||
/// <summary>触发一次采集。</summary>
|
||||
void TriggerOnce();
|
||||
|
||||
/// <summary>停止采集。</summary>
|
||||
void StopGrabbing();
|
||||
|
||||
/// <summary>启动实时预览(链式触发)。</summary>
|
||||
void StartLiveView();
|
||||
|
||||
/// <summary>停止实时预览。</summary>
|
||||
void StopLiveView();
|
||||
|
||||
// ── 参数读写 ──
|
||||
double GetExposureTime();
|
||||
void SetExposureTime(double microseconds);
|
||||
double GetGain();
|
||||
void SetGain(double value);
|
||||
int GetWidth();
|
||||
void SetWidth(int value);
|
||||
int GetHeight();
|
||||
void SetHeight(int value);
|
||||
string GetPixelFormat();
|
||||
void SetPixelFormat(string format);
|
||||
|
||||
/// <summary>最新一帧图像(已 Freeze,可跨线程)。</summary>
|
||||
event EventHandler<BitmapSource> FrameArrived;
|
||||
|
||||
/// <summary>相机连接断开事件。</summary>
|
||||
event EventHandler ConnectionLost;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,5 @@ namespace XplorePlane.Services
|
||||
IProgress<double> progress = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<ushort[]> ProcessRawFrameAsync(
|
||||
ushort[] pixelData,
|
||||
int width,
|
||||
int height,
|
||||
string processorName,
|
||||
IDictionary<string, object> parameters,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,27 +58,5 @@ namespace XplorePlane.Services
|
||||
return BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray8, null, pixels, stride);
|
||||
}
|
||||
|
||||
public static Image<Gray, ushort> ToEmguCV16(BitmapSource bitmapSource)
|
||||
{
|
||||
if (bitmapSource == null) throw new ArgumentNullException(nameof(bitmapSource));
|
||||
|
||||
var formatted = new FormatConvertedBitmap(bitmapSource, PixelFormats.Gray16, null, 0);
|
||||
int width = formatted.PixelWidth;
|
||||
int height = formatted.PixelHeight;
|
||||
int stride = width * 2; // 2 bytes per pixel for 16-bit
|
||||
byte[] rawBytes = new byte[height * stride];
|
||||
formatted.CopyPixels(rawBytes, stride, 0);
|
||||
|
||||
ushort[] pixels = new ushort[width * height];
|
||||
Buffer.BlockCopy(rawBytes, 0, pixels, 0, rawBytes.Length);
|
||||
|
||||
var image = new Image<Gray, ushort>(width, height);
|
||||
// Copy pixel data row by row
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
image.Data[y, x, 0] = pixels[y * width + x];
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,11 @@ namespace XplorePlane.Services
|
||||
{
|
||||
private readonly ILoggerService _logger;
|
||||
private readonly ConcurrentDictionary<string, ImageProcessorBase> _processorRegistry;
|
||||
private readonly ConcurrentDictionary<string, ImageProcessorBase16> _processorRegistry16;
|
||||
|
||||
public ImageProcessingService(ILoggerService logger)
|
||||
{
|
||||
_logger = logger?.ForModule<ImageProcessingService>() ?? throw new ArgumentNullException(nameof(logger));
|
||||
_processorRegistry = new ConcurrentDictionary<string, ImageProcessorBase>();
|
||||
_processorRegistry16 = new ConcurrentDictionary<string, ImageProcessorBase16>();
|
||||
RegisterBuiltInProcessors();
|
||||
}
|
||||
|
||||
@@ -39,20 +37,10 @@ namespace XplorePlane.Services
|
||||
_processorRegistry["ShockFilter"] = new ShockFilterProcessor();
|
||||
_processorRegistry["BandPassFilter"] = new BandPassFilterProcessor();
|
||||
|
||||
// 16-bit processors (separate registry due to different base class)
|
||||
_processorRegistry16["GaussianBlur16"] = new GaussianBlurProcessor16();
|
||||
_processorRegistry16["FlatFieldCorrection16"] = new FlatFieldCorrectionProcessor16();
|
||||
|
||||
_logger.Info("Registered {Count8} 8-bit and {Count16} 16-bit built-in image processors",
|
||||
_processorRegistry.Count, _processorRegistry16.Count);
|
||||
_logger.Info("Registered {Count} built-in image processors", _processorRegistry.Count);
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetAvailableProcessors()
|
||||
{
|
||||
var all = new List<string>(_processorRegistry.Keys);
|
||||
all.AddRange(_processorRegistry16.Keys);
|
||||
return all.AsReadOnly();
|
||||
}
|
||||
public IReadOnlyList<string> GetAvailableProcessors() => new List<string>(_processorRegistry.Keys).AsReadOnly();
|
||||
|
||||
public void RegisterProcessor(string name, ImageProcessorBase processor)
|
||||
{
|
||||
@@ -66,8 +54,6 @@ namespace XplorePlane.Services
|
||||
{
|
||||
if (_processorRegistry.TryGetValue(processorName, out var processor))
|
||||
return processor.GetParameters().AsReadOnly();
|
||||
if (_processorRegistry16.TryGetValue(processorName, out var processor16))
|
||||
return processor16.GetParameters().AsReadOnly();
|
||||
throw new ArgumentException($"Processor not registered: {processorName}", nameof(processorName));
|
||||
}
|
||||
|
||||
@@ -82,8 +68,6 @@ namespace XplorePlane.Services
|
||||
{
|
||||
if (_processorRegistry.TryGetValue(processorName, out var p))
|
||||
return string.IsNullOrWhiteSpace(p.Name) ? processorName : p.Name;
|
||||
if (_processorRegistry16.TryGetValue(processorName, out var p16))
|
||||
return string.IsNullOrWhiteSpace(p16.Name) ? processorName : p16.Name;
|
||||
return processorName;
|
||||
}
|
||||
|
||||
@@ -142,49 +126,6 @@ namespace XplorePlane.Services
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<ushort[]> ProcessRawFrameAsync(
|
||||
ushort[] pixelData,
|
||||
int width,
|
||||
int height,
|
||||
string processorName,
|
||||
IDictionary<string, object> parameters,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (pixelData == null)
|
||||
throw new ArgumentException("pixelData cannot be null", nameof(pixelData));
|
||||
if (pixelData.Length != width * height)
|
||||
throw new ArgumentException(
|
||||
$"pixelData length {pixelData.Length} does not match width*height {width * height}");
|
||||
|
||||
if (!_processorRegistry16.TryGetValue(processorName, out var processor))
|
||||
throw new ArgumentException($"Processor not registered: {processorName}", nameof(processorName));
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var image = new Image<Gray, ushort>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
image.Data[y, x, 0] = pixelData[y * width + x];
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (var kvp in parameters)
|
||||
processor.SetParameter(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
var processed = processor.Process(image);
|
||||
|
||||
var result = new ushort[width * height];
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
result[y * width + x] = processed.Data[y, x, 0];
|
||||
|
||||
return result;
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var processor in _processorRegistry.Values)
|
||||
@@ -194,12 +135,6 @@ namespace XplorePlane.Services
|
||||
}
|
||||
_processorRegistry.Clear();
|
||||
|
||||
foreach (var processor in _processorRegistry16.Values)
|
||||
{
|
||||
if (processor is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
_processorRegistry16.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
using Prism.Commands;
|
||||
using Prism.Mvvm;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using XP.Camera;
|
||||
using Serilog;
|
||||
|
||||
namespace XplorePlane.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 相机预览 ViewModel,移植自 ImageProcessing.MainWindowViewModel 的相机控制逻辑。
|
||||
/// </summary>
|
||||
public class NavigationPropertyPanelViewModel : BindableBase, IDisposable
|
||||
{
|
||||
private static readonly ILogger _logger = Log.ForContext<NavigationPropertyPanelViewModel>();
|
||||
private readonly ICameraController _camera;
|
||||
private volatile bool _liveViewRunning;
|
||||
private bool _disposed;
|
||||
|
||||
#region Properties
|
||||
|
||||
private BitmapSource? _cameraImageSource;
|
||||
public BitmapSource? CameraImageSource
|
||||
{
|
||||
get => _cameraImageSource;
|
||||
set => SetProperty(ref _cameraImageSource, value);
|
||||
}
|
||||
|
||||
private bool _isCameraConnected;
|
||||
public bool IsCameraConnected
|
||||
{
|
||||
get => _isCameraConnected;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isCameraConnected, value))
|
||||
{
|
||||
ConnectCameraCommand.RaiseCanExecuteChanged();
|
||||
DisconnectCameraCommand.RaiseCanExecuteChanged();
|
||||
StartGrabCommand.RaiseCanExecuteChanged();
|
||||
StopGrabCommand.RaiseCanExecuteChanged();
|
||||
ApplyExposureCommand.RaiseCanExecuteChanged();
|
||||
ApplyGainCommand.RaiseCanExecuteChanged();
|
||||
ApplyWidthCommand.RaiseCanExecuteChanged();
|
||||
ApplyHeightCommand.RaiseCanExecuteChanged();
|
||||
ApplyPixelFormatCommand.RaiseCanExecuteChanged();
|
||||
RefreshCameraParamsCommand.RaiseCanExecuteChanged();
|
||||
OpenCameraSettingsCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isCameraGrabbing;
|
||||
public bool IsCameraGrabbing
|
||||
{
|
||||
get => _isCameraGrabbing;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isCameraGrabbing, value))
|
||||
{
|
||||
StartGrabCommand.RaiseCanExecuteChanged();
|
||||
StopGrabCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _cameraStatusText = "未连接";
|
||||
public string CameraStatusText
|
||||
{
|
||||
get => _cameraStatusText;
|
||||
set => SetProperty(ref _cameraStatusText, value);
|
||||
}
|
||||
|
||||
private bool _isLiveViewEnabled;
|
||||
public bool IsLiveViewEnabled
|
||||
{
|
||||
get => _isLiveViewEnabled;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isLiveViewEnabled, value))
|
||||
{
|
||||
if (value)
|
||||
StartLiveView();
|
||||
else
|
||||
StopLiveView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _cameraPixelCoord = "";
|
||||
public string CameraPixelCoord
|
||||
{
|
||||
get => _cameraPixelCoord;
|
||||
set => SetProperty(ref _cameraPixelCoord, value);
|
||||
}
|
||||
|
||||
private double _exposureTime;
|
||||
public double ExposureTime
|
||||
{
|
||||
get => _exposureTime;
|
||||
set => SetProperty(ref _exposureTime, value);
|
||||
}
|
||||
|
||||
private double _gainValue;
|
||||
public double GainValue
|
||||
{
|
||||
get => _gainValue;
|
||||
set => SetProperty(ref _gainValue, value);
|
||||
}
|
||||
|
||||
private int _imageWidth;
|
||||
public int ImageWidth
|
||||
{
|
||||
get => _imageWidth;
|
||||
set => SetProperty(ref _imageWidth, value);
|
||||
}
|
||||
|
||||
private int _imageHeight;
|
||||
public int ImageHeight
|
||||
{
|
||||
get => _imageHeight;
|
||||
set => SetProperty(ref _imageHeight, value);
|
||||
}
|
||||
|
||||
private string _selectedPixelFormat = "Mono8";
|
||||
public string SelectedPixelFormat
|
||||
{
|
||||
get => _selectedPixelFormat;
|
||||
set => SetProperty(ref _selectedPixelFormat, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> PixelFormatOptions { get; } = new() { "Mono8", "BGR8", "BGRA8" };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
public DelegateCommand ConnectCameraCommand { get; }
|
||||
public DelegateCommand DisconnectCameraCommand { get; }
|
||||
public DelegateCommand StartGrabCommand { get; }
|
||||
public DelegateCommand StopGrabCommand { get; }
|
||||
public DelegateCommand ApplyExposureCommand { get; }
|
||||
public DelegateCommand ApplyGainCommand { get; }
|
||||
public DelegateCommand ApplyWidthCommand { get; }
|
||||
public DelegateCommand ApplyHeightCommand { get; }
|
||||
public DelegateCommand ApplyPixelFormatCommand { get; }
|
||||
public DelegateCommand RefreshCameraParamsCommand { get; }
|
||||
public DelegateCommand OpenCameraSettingsCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
public NavigationPropertyPanelViewModel(ICameraController camera)
|
||||
{
|
||||
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
|
||||
|
||||
ConnectCameraCommand = new DelegateCommand(ConnectCamera, () => !IsCameraConnected);
|
||||
DisconnectCameraCommand = new DelegateCommand(DisconnectCamera, () => IsCameraConnected);
|
||||
StartGrabCommand = new DelegateCommand(StartGrab, () => IsCameraConnected && !IsCameraGrabbing);
|
||||
StopGrabCommand = new DelegateCommand(StopGrab, () => IsCameraGrabbing);
|
||||
ApplyExposureCommand = new DelegateCommand(() => ApplyCameraParam(() => _camera.SetExposureTime(ExposureTime)), () => IsCameraConnected);
|
||||
ApplyGainCommand = new DelegateCommand(() => ApplyCameraParam(() => _camera.SetGain(GainValue)), () => IsCameraConnected);
|
||||
ApplyWidthCommand = new DelegateCommand(() => ApplyCameraParam(() => _camera.SetWidth(ImageWidth)), () => IsCameraConnected);
|
||||
ApplyHeightCommand = new DelegateCommand(() => ApplyCameraParam(() => _camera.SetHeight(ImageHeight)), () => IsCameraConnected);
|
||||
ApplyPixelFormatCommand = new DelegateCommand(() => ApplyCameraParam(() => _camera.SetPixelFormat(SelectedPixelFormat)), () => IsCameraConnected);
|
||||
RefreshCameraParamsCommand = new DelegateCommand(RefreshCameraParams, () => IsCameraConnected);
|
||||
OpenCameraSettingsCommand = new DelegateCommand(OpenCameraSettings, () => IsCameraConnected);
|
||||
}
|
||||
|
||||
#region Camera Methods
|
||||
|
||||
private void ConnectCamera()
|
||||
{
|
||||
try
|
||||
{
|
||||
_camera.ImageGrabbed += OnCameraImageGrabbed;
|
||||
_camera.GrabError += OnCameraGrabError;
|
||||
_camera.ConnectionLost += OnCameraConnectionLost;
|
||||
|
||||
var info = _camera.Open();
|
||||
IsCameraConnected = true;
|
||||
CameraStatusText = $"已连接: {info.ModelName} (SN: {info.SerialNumber})";
|
||||
_logger.Information("Camera connected: {ModelName}", info.ModelName);
|
||||
RefreshCameraParams();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to connect camera");
|
||||
CameraStatusText = $"连接失败: {ex.Message}";
|
||||
IsCameraConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisconnectCamera()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLiveViewEnabled = false;
|
||||
_camera.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to disconnect camera");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_camera.ImageGrabbed -= OnCameraImageGrabbed;
|
||||
_camera.GrabError -= OnCameraGrabError;
|
||||
_camera.ConnectionLost -= OnCameraConnectionLost;
|
||||
IsCameraConnected = false;
|
||||
IsCameraGrabbing = false;
|
||||
CameraStatusText = "未连接";
|
||||
CameraImageSource = null;
|
||||
_logger.Information("Camera disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
private void StartGrab()
|
||||
{
|
||||
try
|
||||
{
|
||||
_camera.StartGrabbing();
|
||||
IsCameraGrabbing = true;
|
||||
CameraStatusText = "采集中...";
|
||||
|
||||
// 如果已勾选实时,自动启动 Live View
|
||||
if (IsLiveViewEnabled)
|
||||
{
|
||||
StartLiveView();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to start grabbing");
|
||||
CameraStatusText = $"采集失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void StopGrab()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLiveViewEnabled = false;
|
||||
_camera.StopGrabbing();
|
||||
IsCameraGrabbing = false;
|
||||
CameraStatusText = "已停止采集";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to stop grabbing");
|
||||
}
|
||||
}
|
||||
|
||||
private void StartLiveView()
|
||||
{
|
||||
if (!IsCameraGrabbing) return;
|
||||
|
||||
_liveViewRunning = true;
|
||||
CameraStatusText = "实时采集中...";
|
||||
|
||||
try { _camera.ExecuteSoftwareTrigger(); }
|
||||
catch (Exception ex) { _logger.Error(ex, "Live view trigger failed"); }
|
||||
}
|
||||
|
||||
private void StopLiveView()
|
||||
{
|
||||
_liveViewRunning = false;
|
||||
if (IsCameraGrabbing)
|
||||
CameraStatusText = "采集中...";
|
||||
}
|
||||
|
||||
private void RefreshCameraParams()
|
||||
{
|
||||
try
|
||||
{
|
||||
ExposureTime = _camera.GetExposureTime();
|
||||
GainValue = _camera.GetGain();
|
||||
ImageWidth = _camera.GetWidth();
|
||||
ImageHeight = _camera.GetHeight();
|
||||
SelectedPixelFormat = _camera.GetPixelFormat();
|
||||
_logger.Information("Camera parameters refreshed");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to read camera parameters");
|
||||
CameraStatusText = $"读取参数失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyCameraParam(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
_logger.Information("Camera parameter applied");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to apply camera parameter");
|
||||
CameraStatusText = $"设置参数失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenCameraSettings()
|
||||
{
|
||||
RefreshCameraParams();
|
||||
var window = new Views.CameraSettingsWindow(this);
|
||||
window.Owner = Application.Current.MainWindow;
|
||||
window.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Camera Event Handlers
|
||||
|
||||
private void OnCameraImageGrabbed(object? sender, ImageGrabbedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = PixelConverter.ToBitmapSource(e.PixelData, e.Width, e.Height, e.PixelFormat);
|
||||
var app = Application.Current;
|
||||
if (app == null) return;
|
||||
|
||||
app.Dispatcher.Invoke(() =>
|
||||
{
|
||||
CameraImageSource = bitmap;
|
||||
});
|
||||
|
||||
if (_liveViewRunning)
|
||||
{
|
||||
_camera.ExecuteSoftwareTrigger();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to process camera image");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCameraGrabError(object? sender, GrabErrorEventArgs e)
|
||||
{
|
||||
_logger.Error("Camera grab error: [{ErrorCode}] {ErrorDescription}", e.ErrorCode, e.ErrorDescription);
|
||||
var app = Application.Current;
|
||||
if (app == null) return;
|
||||
|
||||
app.Dispatcher.Invoke(() =>
|
||||
{
|
||||
CameraStatusText = $"采集错误: {e.ErrorDescription}";
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCameraConnectionLost(object? sender, EventArgs e)
|
||||
{
|
||||
_logger.Warning("Camera connection lost");
|
||||
var app = Application.Current;
|
||||
if (app == null) return;
|
||||
|
||||
app.Dispatcher.Invoke(() =>
|
||||
{
|
||||
IsCameraConnected = false;
|
||||
IsCameraGrabbing = false;
|
||||
CameraStatusText = "连接已断开";
|
||||
CameraImageSource = null;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_liveViewRunning = false;
|
||||
|
||||
try { _camera.Dispose(); }
|
||||
catch (Exception ex) { _logger.Error(ex, "Error disposing camera"); }
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<Window x:Class="XplorePlane.Views.CameraSettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="相机参数设置"
|
||||
Width="320" Height="420"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False">
|
||||
<StackPanel Margin="15">
|
||||
|
||||
<TextBlock Text="曝光时间 (µs)" FontSize="11" Foreground="#666" Margin="0,0,0,2" />
|
||||
<DockPanel Margin="0,0,0,10">
|
||||
<Button DockPanel.Dock="Right" Content="设置" Width="45" Height="26" FontSize="11"
|
||||
Margin="6,0,0,0" Command="{Binding ApplyExposureCommand}" />
|
||||
<TextBox Text="{Binding ExposureTime, UpdateSourceTrigger=PropertyChanged}"
|
||||
Height="26" FontSize="12" VerticalContentAlignment="Center" Padding="4,0" />
|
||||
</DockPanel>
|
||||
|
||||
<TextBlock Text="增益" FontSize="11" Foreground="#666" Margin="0,0,0,2" />
|
||||
<DockPanel Margin="0,0,0,10">
|
||||
<Button DockPanel.Dock="Right" Content="设置" Width="45" Height="26" FontSize="11"
|
||||
Margin="6,0,0,0" Command="{Binding ApplyGainCommand}" />
|
||||
<TextBox Text="{Binding GainValue, UpdateSourceTrigger=PropertyChanged}"
|
||||
Height="26" FontSize="12" VerticalContentAlignment="Center" Padding="4,0" />
|
||||
</DockPanel>
|
||||
|
||||
<TextBlock Text="图像宽度 (px)" FontSize="11" Foreground="#666" Margin="0,0,0,2" />
|
||||
<DockPanel Margin="0,0,0,10">
|
||||
<Button DockPanel.Dock="Right" Content="设置" Width="45" Height="26" FontSize="11"
|
||||
Margin="6,0,0,0" Command="{Binding ApplyWidthCommand}" />
|
||||
<TextBox Text="{Binding ImageWidth, UpdateSourceTrigger=PropertyChanged}"
|
||||
Height="26" FontSize="12" VerticalContentAlignment="Center" Padding="4,0" />
|
||||
</DockPanel>
|
||||
|
||||
<TextBlock Text="图像高度 (px)" FontSize="11" Foreground="#666" Margin="0,0,0,2" />
|
||||
<DockPanel Margin="0,0,0,10">
|
||||
<Button DockPanel.Dock="Right" Content="设置" Width="45" Height="26" FontSize="11"
|
||||
Margin="6,0,0,0" Command="{Binding ApplyHeightCommand}" />
|
||||
<TextBox Text="{Binding ImageHeight, UpdateSourceTrigger=PropertyChanged}"
|
||||
Height="26" FontSize="12" VerticalContentAlignment="Center" Padding="4,0" />
|
||||
</DockPanel>
|
||||
|
||||
<TextBlock Text="像素格式" FontSize="11" Foreground="#666" Margin="0,0,0,2" />
|
||||
<DockPanel Margin="0,0,0,10">
|
||||
<Button DockPanel.Dock="Right" Content="设置" Width="45" Height="26" FontSize="11"
|
||||
Margin="6,0,0,0" Command="{Binding ApplyPixelFormatCommand}" />
|
||||
<ComboBox SelectedItem="{Binding SelectedPixelFormat}"
|
||||
ItemsSource="{Binding PixelFormatOptions}"
|
||||
Height="26" FontSize="12" VerticalContentAlignment="Center" />
|
||||
</DockPanel>
|
||||
|
||||
<Rectangle Height="1" Fill="#E0E0E0" Margin="0,2,0,10" />
|
||||
|
||||
<Button Content="读取当前参数" Height="30" FontSize="12"
|
||||
Command="{Binding RefreshCameraParamsCommand}" />
|
||||
</StackPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace XplorePlane.Views
|
||||
{
|
||||
public partial class CameraSettingsWindow : Window
|
||||
{
|
||||
public CameraSettingsWindow(object viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,17 @@
|
||||
SmallImage="/Assets/Icons/xyz.png"
|
||||
Text="运动控制" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 第三列: 相机设置 -->
|
||||
<StackPanel>
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Description="打开相机参数设置对话框"
|
||||
telerik:ScreenTip.Title="相机设置"
|
||||
Click="CameraSettings_Click"
|
||||
Size="Medium"
|
||||
SmallImage="/Assets/Icons/detector2.png"
|
||||
Text="相机设置" />
|
||||
</StackPanel>
|
||||
</telerik:RadRibbonGroup>
|
||||
|
||||
<telerik:RadRibbonGroup telerik:ScreenTip.Title="图像算子" Header="图像算子">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Windows;
|
||||
using Prism.Ioc;
|
||||
using Telerik.Windows.Controls;
|
||||
using XplorePlane.ViewModels;
|
||||
|
||||
@@ -36,5 +37,21 @@ namespace XplorePlane.Views
|
||||
_toolboxWindow.Activate();
|
||||
}
|
||||
}
|
||||
|
||||
private void CameraSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var bootstrapper = AppBootstrapper.Instance;
|
||||
if (bootstrapper == null) return;
|
||||
|
||||
var vm = bootstrapper.Container.Resolve<NavigationPropertyPanelViewModel>();
|
||||
if (!vm.IsCameraConnected)
|
||||
{
|
||||
MessageBox.Show("请先连接相机", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var window = new CameraSettingsWindow(vm) { Owner = this };
|
||||
window.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="#FFFFFF">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Background="#F0F0F0"
|
||||
@@ -23,13 +27,80 @@
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="#333333"
|
||||
Text="导航" />
|
||||
Text="相机预览" />
|
||||
</Border>
|
||||
<ScrollViewer
|
||||
|
||||
<!-- 相机图像显示区域 -->
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Background="Black"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="4" />
|
||||
</ScrollViewer>
|
||||
Background="#000000"
|
||||
BorderBrush="#CCCCCC"
|
||||
BorderThickness="1">
|
||||
<Image
|
||||
x:Name="imgCamera"
|
||||
Source="{Binding CameraImageSource}"
|
||||
Stretch="Uniform"
|
||||
StretchDirection="Both"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
MouseMove="ImgCamera_MouseMove"
|
||||
MouseLeave="ImgCamera_MouseLeave" />
|
||||
</Border>
|
||||
|
||||
<!-- 状态信息 -->
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Background="#000000"
|
||||
Padding="8,4">
|
||||
<TextBlock FontSize="12">
|
||||
<Run Foreground="#FFFFFF" Text="{Binding CameraStatusText, Mode=OneWay}" />
|
||||
<Run Text=" " />
|
||||
<Run Foreground="#0078D4" Text="{Binding CameraPixelCoord, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</Border>
|
||||
|
||||
<!-- 控制按钮栏 -->
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Background="#F0F0F0"
|
||||
BorderBrush="#DDDDDD"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="4">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button
|
||||
Command="{Binding ConnectCameraCommand}"
|
||||
Padding="8,4"
|
||||
Margin="2"
|
||||
Background="#4CAF50"
|
||||
Foreground="#000000"
|
||||
Content="连接" />
|
||||
<Button
|
||||
Command="{Binding DisconnectCameraCommand}"
|
||||
Padding="8,4"
|
||||
Margin="2"
|
||||
Background="#F44336"
|
||||
Foreground="#000000"
|
||||
Content="断开" />
|
||||
<Button
|
||||
Command="{Binding StartGrabCommand}"
|
||||
Padding="8,4"
|
||||
Margin="2"
|
||||
Background="#2196F3"
|
||||
Foreground="#000000"
|
||||
Content="开始采集" />
|
||||
<Button
|
||||
Command="{Binding StopGrabCommand}"
|
||||
Padding="8,4"
|
||||
Margin="2"
|
||||
Background="#FF9800"
|
||||
Foreground="#000000"
|
||||
Content="停止采集" />
|
||||
<CheckBox
|
||||
IsChecked="{Binding IsLiveViewEnabled}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="6,0,0,0"
|
||||
Foreground="#333333"
|
||||
Content="实时" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +1,54 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using XplorePlane.ViewModels;
|
||||
using Prism.Ioc;
|
||||
|
||||
namespace XplorePlane.Views
|
||||
{
|
||||
public partial class NavigationPropertyPanelView : UserControl
|
||||
{
|
||||
private NavigationPropertyPanelViewModel? _viewModel;
|
||||
|
||||
public NavigationPropertyPanelView()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is NavigationPropertyPanelViewModel) return;
|
||||
|
||||
var bootstrapper = AppBootstrapper.Instance;
|
||||
if (bootstrapper != null)
|
||||
{
|
||||
_viewModel = bootstrapper.Container.Resolve<NavigationPropertyPanelViewModel>();
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ImgCamera_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (_viewModel?.CameraImageSource == null) return;
|
||||
|
||||
var image = (Image)sender;
|
||||
var pos = e.GetPosition(image);
|
||||
|
||||
int px = (int)(pos.X / image.ActualWidth * _viewModel.CameraImageSource.PixelWidth);
|
||||
int py = (int)(pos.Y / image.ActualHeight * _viewModel.CameraImageSource.PixelHeight);
|
||||
|
||||
if (px >= 0 && px < _viewModel.CameraImageSource.PixelWidth &&
|
||||
py >= 0 && py < _viewModel.CameraImageSource.PixelHeight)
|
||||
{
|
||||
_viewModel.CameraPixelCoord = $"X: {px}, Y: {py}";
|
||||
}
|
||||
}
|
||||
|
||||
private void ImgCamera_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (_viewModel != null)
|
||||
_viewModel.CameraPixelCoord = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,28 +53,12 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
|
||||
<!-- 图像处理库 DLL 引用 -->
|
||||
<Reference Include="ImageProcessing.Core">
|
||||
<HintPath>Libs\ImageProcessing\ImageProcessing.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessing.Processors">
|
||||
<HintPath>Libs\ImageProcessing\ImageProcessing.Processors.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessing.Controls">
|
||||
<HintPath>Libs\ImageProcessing\ImageProcessing.Controls.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImageROIControl">
|
||||
<HintPath>Libs\ImageProcessing\ImageROIControl.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Telerik UI for WPF 2024 Q1 - NetCore (v2024.1.408.310) -->
|
||||
<PropertyGroup>
|
||||
<TelerikDir>D:\Program Files (x86)\Progress\Telerik UI for WPF 2024 Q1\Binaries\NetCore</TelerikDir>
|
||||
<TelerikDir>C:\Program Files (x86)\Progress\Telerik UI for WPF 2024 Q1\Binaries\NetCore</TelerikDir>
|
||||
<BaseOutputPath>..\bin\</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Telerik.Windows.Controls">
|
||||
@@ -170,16 +154,6 @@
|
||||
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
|
||||
</None>
|
||||
|
||||
<!-- 图像处理外部运行时 DLL -->
|
||||
<None Include="Libs\ImageProcessing\ExternalLibraries\*.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<!-- 图像处理中文卫星程序集 -->
|
||||
<None Include="Libs\ImageProcessing\zh-CN\*.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>Libs\ImageProcessing\zh-CN\%(Filename)%(Extension)</Link>
|
||||
</None>
|
||||
<Resource Include="GapInspect.ico" />
|
||||
|
||||
<!-- 配置文件 -->
|
||||
@@ -187,4 +161,14 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ImageProcessing.Controls\ImageProcessing.Controls.csproj" />
|
||||
<ProjectReference Include="..\ImageProcessing.Core\ImageProcessing.Core.csproj" />
|
||||
<ProjectReference Include="..\ImageProcessing.Processors\ImageProcessing.Processors.csproj" />
|
||||
<ProjectReference Include="..\ImageROIControl\ImageROIControl.csproj" />
|
||||
<ProjectReference Include="..\XP.Camera\XP.Camera.csproj" />
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="xcopy $(SolutionDir)ExternalLibraries $(TargetDir) /d /y /s" />
|
||||
</Target>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user