127 lines
5.4 KiB
C#
127 lines
5.4 KiB
C#
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");
|
|
}
|
|
}
|
|
}
|