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);
}