将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Prism.Events;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Hardware.Detector.Abstractions;
|
||||
using XP.Hardware.Detector.Abstractions.Events;
|
||||
|
||||
namespace XP.Hardware.Detector.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 图像服务实现 | Image service implementation
|
||||
/// 提供最新帧获取和 16 位 TIFF 保存功能
|
||||
/// </summary>
|
||||
public class ImageService : IImageService
|
||||
{
|
||||
private readonly IDetectorService _detectorService;
|
||||
private readonly ILoggerService _logger;
|
||||
|
||||
/// <summary>
|
||||
/// 最新采集的原始图像数据 | Latest captured raw image data
|
||||
/// </summary>
|
||||
private volatile ImageCapturedEventArgs _latestFrame;
|
||||
|
||||
public ImageService(
|
||||
IDetectorService detectorService,
|
||||
IEventAggregator eventAggregator,
|
||||
ILoggerService logger)
|
||||
{
|
||||
_detectorService = detectorService ?? throw new ArgumentNullException(nameof(detectorService));
|
||||
_logger = logger?.ForModule<ImageService>();
|
||||
|
||||
// 订阅图像采集事件,缓存最新帧 | Subscribe to image captured event, cache latest frame
|
||||
if (eventAggregator == null) throw new ArgumentNullException(nameof(eventAggregator));
|
||||
eventAggregator.GetEvent<ImageCapturedEvent>()
|
||||
.Subscribe(OnImageCaptured, ThreadOption.BackgroundThread);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImageCapturedEventArgs LatestFrame => _latestFrame;
|
||||
|
||||
/// <summary>
|
||||
/// 图像采集事件回调,缓存最新帧 | Image captured callback, cache latest frame
|
||||
/// </summary>
|
||||
private void OnImageCaptured(ImageCapturedEventArgs args)
|
||||
{
|
||||
if (args?.ImageData != null && args.Width > 0 && args.Height > 0)
|
||||
_latestFrame = args;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<DetectorResult> SaveAsTiffAsync(ushort[] imageData, int width, int height, string filePath)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (imageData == null || imageData.Length == 0)
|
||||
return DetectorResult.Failure("图像数据为空 | Image data is empty");
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return DetectorResult.Failure($"图像尺寸无效:{width}x{height} | Invalid image size: {width}x{height}");
|
||||
|
||||
// 确保目录存在 | Ensure directory exists
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// 创建 16 位灰度 BitmapSource | Create 16-bit grayscale BitmapSource
|
||||
int stride = width * sizeof(ushort);
|
||||
var bitmap = BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray16, null, imageData, stride);
|
||||
bitmap.Freeze();
|
||||
|
||||
// 编码为 TIFF(无压缩)| Encode as TIFF (no compression)
|
||||
var encoder = new TiffBitmapEncoder { Compression = TiffCompressOption.None };
|
||||
encoder.Frames.Add(BitmapFrame.Create(bitmap));
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
encoder.Save(stream);
|
||||
}
|
||||
|
||||
_logger?.Info("图像已保存:{FilePath},分辨率:{Width}x{Height} | Image saved: {FilePath}, resolution: {Width}x{Height}", filePath, width, height);
|
||||
return DetectorResult.Success($"图像已保存 | Image saved: {filePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMsg = $"保存图像失败:{ex.Message} | Failed to save image: {ex.Message}";
|
||||
_logger?.Error(ex, errorMsg);
|
||||
return DetectorResult.Failure(errorMsg, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<DetectorResult> SaveAsTiffAsync(ushort[] imageData, int width, int height, string saveDirectory, string prefix, int frameNumber)
|
||||
{
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");
|
||||
var fileName = $"{prefix}_{timestamp}_F{frameNumber}.tif";
|
||||
var filePath = Path.Combine(saveDirectory, fileName);
|
||||
return SaveAsTiffAsync(imageData, width, height, filePath);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<DetectorResult> SaveLatestFrameAsync(string saveDirectory, string prefix)
|
||||
{
|
||||
var frame = _latestFrame;
|
||||
if (frame?.ImageData == null)
|
||||
return Task.FromResult(DetectorResult.Failure("无可用的图像数据 | No image data available"));
|
||||
|
||||
return SaveAsTiffAsync(frame.ImageData, (int)frame.Width, (int)frame.Height, saveDirectory, prefix, frame.FrameNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetDefaultSaveDirectory()
|
||||
{
|
||||
var config = _detectorService.GetCurrentConfig();
|
||||
if (config != null && !string.IsNullOrEmpty(config.SavePath))
|
||||
return config.SavePath;
|
||||
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user