using Microsoft.Extensions.Logging; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Contracts; using OrpaonVision.SiteApp.Controls; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Media.Imaging; using System.Windows.Media; using System.Windows; using WpfColor = System.Windows.Media.Color; using WpfPixelFormat = System.Windows.Media.PixelFormat; using WpfFontStyle = System.Windows.FontStyle; using DrawingPen = System.Drawing.Pen; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 图像处理服务接口。 /// public interface IImageProcessingService { /// /// 将相机帧转换为BitmapSource。 /// Result ConvertFrameToBitmapSource(CameraFrameDto frame); /// /// 将推理结果转换为检测框列表。 /// Result> ConvertInferenceToDetectionBoxes(InferenceResultDto inference); /// /// 创建占位图像。 /// BitmapSource CreatePlaceholderImage(int width, int height, string text); /// /// 调整图像大小。 /// Result ResizeImage(BitmapSource source, int width, int height); /// /// 旋转图像。 /// Result RotateImage(BitmapSource source, double angle); /// /// 裁剪图像。 /// Result CropImage(BitmapSource source, int x, int y, int width, int height); /// /// 应用图像滤镜。 /// Result ApplyFilter(BitmapSource source, ImageFilter filter); } /// /// 图像滤镜类型。 /// public enum ImageFilter { /// /// 无滤镜。 /// None, /// /// 灰度。 /// Grayscale, /// /// 二值化。 /// Binary, /// /// 边缘检测。 /// EdgeDetection, /// /// 模糊。 /// Blur, /// /// 锐化。 /// Sharpen } /// /// 图像处理服务实现。 /// public sealed class ImageProcessingService : IImageProcessingService { private readonly ILogger _logger; /// /// 构造函数。 /// public ImageProcessingService(ILogger logger) { _logger = logger; } /// public Result ConvertFrameToBitmapSource(CameraFrameDto frame) { try { if (frame == null) { return Result.Fail("FRAME_NULL", "相机帧不能为空。"); } if (frame.ImageData == null || frame.ImageData.Length == 0) { return Result.Fail("IMAGE_DATA_EMPTY", "图像数据为空。"); } _logger.LogDebug("正在转换相机帧为BitmapSource,尺寸: {Width}x{Height}", frame.Width, frame.Height); using var ms = new System.IO.MemoryStream(frame.ImageData); var bitmap = new Bitmap(ms); var bitmapSource = bitmap.ToBitmapSource(); _logger.LogDebug("相机帧转换完成"); return Result.Success(bitmapSource, message: "相机帧转换成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "转换相机帧失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CONVERT_FRAME_FAILED", "转换相机帧失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result> ConvertInferenceToDetectionBoxes(InferenceResultDto inference) { try { if (inference == null) { return Result>.Fail("INFERENCE_NULL", "推理结果不能为空。"); } var detectionBoxes = new List(); if (inference.Detections != null) { foreach (var detection in inference.Detections) { var box = new DetectionBox { ClassName = detection.ClassName ?? "Unknown", Confidence = detection.Confidence, X = detection.X, Y = detection.Y, Width = detection.Width, Height = detection.Height, Label = $"{detection.ClassName} ({detection.Confidence:P0})" }; detectionBoxes.Add(box); } } _logger.LogDebug("推理结果转换完成,检测框数量: {Count}", detectionBoxes.Count); return Result>.Success(detectionBoxes, message: "推理结果转换成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "转换推理结果失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CONVERT_INFERENCE_FAILED", "转换推理结果失败。", traceId); return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public BitmapSource CreatePlaceholderImage(int width, int height, string text) { try { using var bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); using var graphics = Graphics.FromImage(bitmap); // 清除背景 graphics.Clear(System.Drawing.Color.LightGray); // 绘制文本 var font = new Font("Arial", 16, System.Drawing.FontStyle.Bold); var brush = new SolidBrush(System.Drawing.Color.DarkGray); var textSize = graphics.MeasureString(text, font); var textX = (width - textSize.Width) / 2; var textY = (height - textSize.Height) / 2; graphics.DrawString(text, font, brush, textX, textY); // 绘制边框 using var pen = new DrawingPen(System.Drawing.Color.Gray, 2); graphics.DrawRectangle(pen, 0, 0, width - 1, height - 1); return bitmap.ToBitmapSource(); } catch (Exception ex) { _logger.LogError(ex, "创建占位图像失败"); return CreateErrorPlaceholder(width, height); } } /// public Result ResizeImage(BitmapSource source, int width, int height) { try { if (source == null) { return Result.Fail("SOURCE_NULL", "源图像不能为空。"); } _logger.LogDebug("正在调整图像大小: {Width}x{Height} -> {NewWidth}x{NewHeight}", source.PixelWidth, source.PixelHeight, width, height); var scaled = new TransformedBitmap(source, new ScaleTransform( (double)width / source.PixelWidth, (double)height / source.PixelHeight)); _logger.LogDebug("图像大小调整完成"); return Result.Success(scaled, message: "图像大小调整成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "调整图像大小失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "RESIZE_IMAGE_FAILED", "调整图像大小失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result RotateImage(BitmapSource source, double angle) { try { if (source == null) { return Result.Fail("SOURCE_NULL", "源图像不能为空。"); } _logger.LogDebug("正在旋转图像,角度: {Angle}", angle); var rotated = new TransformedBitmap(source, new RotateTransform(angle)); _logger.LogDebug("图像旋转完成"); return Result.Success(rotated, message: "图像旋转成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "旋转图像失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "ROTATE_IMAGE_FAILED", "旋转图像失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result CropImage(BitmapSource source, int x, int y, int width, int height) { try { if (source == null) { return Result.Fail("SOURCE_NULL", "源图像不能为空。"); } _logger.LogDebug("正在裁剪图像: ({X},{Y}) {Width}x{Height}", x, y, width, height); var cropped = new CroppedBitmap(source, new Int32Rect(x, y, width, height)); _logger.LogDebug("图像裁剪完成"); return Result.Success(cropped, message: "图像裁剪成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "裁剪图像失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CROP_IMAGE_FAILED", "裁剪图像失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result ApplyFilter(BitmapSource source, ImageFilter filter) { try { if (source == null) { return Result.Fail("SOURCE_NULL", "源图像不能为空。"); } _logger.LogDebug("正在应用图像滤镜: {Filter}", filter); // 这里可以实现各种滤镜效果 // 由于WPF的图像处理相对复杂,这里只是占位实现 BitmapSource result = filter switch { ImageFilter.Grayscale => ApplyGrayscaleFilter(source), ImageFilter.Binary => ApplyBinaryFilter(source), ImageFilter.EdgeDetection => ApplyEdgeDetectionFilter(source), ImageFilter.Blur => ApplyBlurFilter(source), ImageFilter.Sharpen => ApplySharpenFilter(source), _ => source }; _logger.LogDebug("图像滤镜应用完成"); return Result.Success(result, message: "图像滤镜应用成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "应用图像滤镜失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "APPLY_FILTER_FAILED", "应用图像滤镜失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } private BitmapSource ApplyGrayscaleFilter(BitmapSource source) { // 简化的灰度滤镜实现 // 这里应该实现真正的灰度转换 // 为了简化,直接返回原图 return source; } private BitmapSource ApplyBinaryFilter(BitmapSource source) { // 简化的二值化滤镜实现 return source; } private BitmapSource ApplyEdgeDetectionFilter(BitmapSource source) { // 简化的边缘检测滤镜实现 return source; } private BitmapSource ApplyBlurFilter(BitmapSource source) { // 简化的模糊滤镜实现 return source; } private BitmapSource ApplySharpenFilter(BitmapSource source) { // 简化的锐化滤镜实现 return source; } private BitmapSource CreateErrorPlaceholder(int width, int height) { using var bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); using var graphics = Graphics.FromImage(bitmap); graphics.Clear(System.Drawing.Color.Red); using var font = new Font("Arial", 12, System.Drawing.FontStyle.Bold); using var brush = new SolidBrush(System.Drawing.Color.White); graphics.DrawString("Error", font, brush, 10, 10); return bitmap.ToBitmapSource(); } } /// /// Bitmap扩展方法。 /// public static class BitmapExtensions { /// /// 将Bitmap转换为BitmapSource。 /// public static BitmapSource ToBitmapSource(this Bitmap bitmap) { var hBitmap = bitmap.GetHbitmap(); try { return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, new Int32Rect(0, 0, bitmap.Width, bitmap.Height), BitmapSizeOptions.FromEmptyOptions()); } finally { DeleteObject(hBitmap); } } [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); }